fix: clarify client amounts and log date filters
This commit is contained in:
@@ -8,11 +8,11 @@ export const adminOperationsApi = {
|
||||
getSendQuality: (date?: string) => request<SendQualityResponse>(withQuery('/admin/operations/send-quality', { date })),
|
||||
getSignatureQuality: (query: { date?: string; keyword?: string; page?: number; pageSize?: number } = {}) =>
|
||||
request<SignatureChannelQualityResponse>(withQuery('/admin/operations/signature-quality', query)),
|
||||
listSystemLogs: (query: { tenantId?: string; keyword?: string; level?: string; module?: string; range?: string; page?: number; pageSize?: number }) =>
|
||||
listSystemLogs: (query: { tenantId?: string; keyword?: string; level?: string; module?: string; range?: string; createdAtFrom?: string; createdAtTo?: string; page?: number; pageSize?: number }) =>
|
||||
request<OperationLogResponse>(withQuery('/admin/system-logs', query)),
|
||||
listProtocolInteractionLogs: (query: { protocol?: string; direction?: string; eventType?: string; status?: string; keyword?: string; range?: string; page?: number; pageSize?: number }) =>
|
||||
listProtocolInteractionLogs: (query: { protocol?: string; direction?: string; eventType?: string; status?: string; keyword?: string; range?: string; createdAtFrom?: string; createdAtTo?: string; page?: number; pageSize?: number }) =>
|
||||
request<ProtocolInteractionLogResponse>(withQuery('/admin/system-logs/protocol-interactions', query)),
|
||||
exportSystemLogs: (query: { tenantId?: string; keyword?: string; level?: string; module?: string; range?: string }) =>
|
||||
exportSystemLogs: (query: { tenantId?: string; keyword?: string; level?: string; module?: string; range?: string; createdAtFrom?: string; createdAtTo?: string }) =>
|
||||
request<SystemLogExportResult>('/admin/system-logs/exports', { method: 'POST', body: JSON.stringify(query) }),
|
||||
listReconciliationReports: (query: { dateFrom?: string; dateTo?: string; tenantId?: string; applicationId?: string; page?: number; pageSize?: number } = {}) =>
|
||||
request<PagedResponse<DailyReconciliationReport> & { summary: ReconciliationReportSummary }>(withQuery('/admin/reports/reconciliation', query)),
|
||||
|
||||
@@ -36,9 +36,9 @@ export const clientApi = {
|
||||
tenantId,
|
||||
body: JSON.stringify({ ...body, tenantId }),
|
||||
}),
|
||||
listSystemLogs: (query: { keyword?: string; level?: string; module?: string; range?: string; page?: number; pageSize?: number }, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
listSystemLogs: (query: { keyword?: string; level?: string; module?: string; range?: string; createdAtFrom?: string; createdAtTo?: string; page?: number; pageSize?: number }, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<OperationLogResponse>(withQuery('/client/operations/system-logs', query), { tenantId }),
|
||||
exportSystemLogs: (query: { keyword?: string; level?: string; module?: string; range?: string }) =>
|
||||
exportSystemLogs: (query: { keyword?: string; level?: string; module?: string; range?: string; createdAtFrom?: string; createdAtTo?: string }) =>
|
||||
request<SystemLogExportResult>('/client/operations/system-logs/exports', { method: 'POST', body: JSON.stringify(query) }),
|
||||
listOrders: (tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<RechargeOrder[]>('/client/billing/orders', { tenantId }),
|
||||
|
||||
@@ -331,7 +331,7 @@ export type SystemLogExportResult = {
|
||||
recordCount: number;
|
||||
truncated: boolean;
|
||||
content: string;
|
||||
filters: { keyword?: string; level?: string; module?: string; range?: string };
|
||||
filters: { keyword?: string; level?: string; module?: string; range?: string; createdAtFrom?: string; createdAtTo?: string };
|
||||
};
|
||||
|
||||
export type DailyReconciliationReport = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { CalendarDays, FileText, Search } from 'lucide-react';
|
||||
import { Button, Input, Modal, Pagination, Select, SystemLogExport, Table, Tabs, Tag, type TableColumn } from '@/components/ui';
|
||||
import { FileText, Search } from 'lucide-react';
|
||||
import { Button, DateRangeInput, Input, Modal, Pagination, Select, SystemLogExport, Table, Tabs, Tag, type DateRangeValue, type TableColumn } from '@/components/ui';
|
||||
import { adminApi, type OperationLogItem, type ProtocolInteractionLogItem } from '@/api/adminApi';
|
||||
import { formatDateTime } from '@/utils/dateTime';
|
||||
import { formatDateTime, recentBeijingDateRange } from '@/utils/dateTime';
|
||||
|
||||
type LogLevel = 'info' | 'success' | 'warning' | 'error';
|
||||
|
||||
@@ -25,8 +25,11 @@ export function AdminSystemLogsPage() {
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [level, setLevel] = useState('all');
|
||||
const [module, setModule] = useState('all');
|
||||
const [range, setRange] = useState('today');
|
||||
const [filters, setFilters] = useState({ keyword: '', level: 'all', module: 'all', range: 'today' });
|
||||
const [dateRange, setDateRange] = useState<DateRangeValue>(() => recentBeijingDateRange(7));
|
||||
const [filters, setFilters] = useState<{ keyword: string; level: string; module: string; createdAtFrom?: string; createdAtTo?: string }>(() => {
|
||||
const initialRange = recentBeijingDateRange(7);
|
||||
return { keyword: '', level: 'all', module: 'all', createdAtFrom: initialRange.start, createdAtTo: initialRange.end };
|
||||
});
|
||||
const [page, setPage] = useState(1);
|
||||
const pageSize = 5;
|
||||
const [logs, setLogs] = useState<OperationLogItem[]>([]);
|
||||
@@ -59,16 +62,17 @@ export function AdminSystemLogsPage() {
|
||||
|
||||
function query() {
|
||||
setPage(1);
|
||||
setFilters({ keyword: keyword.trim(), level, module, range });
|
||||
setFilters({ keyword: keyword.trim(), level, module, createdAtFrom: dateRange.start, createdAtTo: dateRange.end });
|
||||
}
|
||||
|
||||
function reset() {
|
||||
setKeyword('');
|
||||
setLevel('all');
|
||||
setModule('all');
|
||||
setRange('today');
|
||||
const nextDateRange = recentBeijingDateRange(7);
|
||||
setDateRange(nextDateRange);
|
||||
setPage(1);
|
||||
setFilters({ keyword: '', level: 'all', module: 'all', range: 'today' });
|
||||
setFilters({ keyword: '', level: 'all', module: 'all', createdAtFrom: nextDateRange.start, createdAtTo: nextDateRange.end });
|
||||
}
|
||||
|
||||
const columns = useMemo<Array<TableColumn<OperationLogItem>>>(() => [
|
||||
@@ -118,14 +122,13 @@ export function AdminSystemLogsPage() {
|
||||
value={level}
|
||||
/>
|
||||
<Select onChange={(event) => setModule(event.target.value)} options={moduleOptions} value={module} />
|
||||
<DateRangeInput label="日志时间" onChange={setDateRange} placeholder="选择日志时间区间" value={dateRange} />
|
||||
<div className="system-log-filters__actions">
|
||||
<Button icon={<Search size={16} />} onClick={query}>查询</Button>
|
||||
<Button onClick={reset} variant="ghost">重置</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LogRange range={range} onChange={setRange} />
|
||||
|
||||
<div className="surface system-table-card">
|
||||
<Table columns={columns} data={logs} emptyText={error || '暂无系统日志'} pagination={false} rowKey="id" />
|
||||
<Pagination
|
||||
@@ -222,7 +225,7 @@ function protocolStatusLabel(record: ProtocolInteractionLogItem) {
|
||||
}
|
||||
|
||||
function ProtocolInteractionPanel({ active }: { active: boolean }) {
|
||||
const [inputs, setInputs] = useState({ keyword: '', protocol: 'all', direction: 'all', eventType: 'all', status: 'all', range: 'today' });
|
||||
const [inputs, setInputs] = useState(() => ({ keyword: '', protocol: 'all', direction: 'all', eventType: 'all', status: 'all', dateRange: recentBeijingDateRange(7) as DateRangeValue }));
|
||||
const [filters, setFilters] = useState(inputs);
|
||||
const [items, setItems] = useState<ProtocolInteractionLogItem[]>([]);
|
||||
const [eventTypes, setEventTypes] = useState<string[]>([]);
|
||||
@@ -235,7 +238,8 @@ function ProtocolInteractionPanel({ active }: { active: boolean }) {
|
||||
useEffect(() => {
|
||||
if (!active) return;
|
||||
let cancelled = false;
|
||||
adminApi.listProtocolInteractionLogs({ ...filters, page, pageSize })
|
||||
const { dateRange, ...queryFilters } = filters;
|
||||
adminApi.listProtocolInteractionLogs({ ...queryFilters, createdAtFrom: dateRange.start, createdAtTo: dateRange.end, page, pageSize })
|
||||
.then((data) => {
|
||||
if (cancelled) return;
|
||||
setItems(data.items);
|
||||
@@ -271,7 +275,7 @@ function ProtocolInteractionPanel({ active }: { active: boolean }) {
|
||||
}
|
||||
|
||||
function reset() {
|
||||
const next = { keyword: '', protocol: 'all', direction: 'all', eventType: 'all', status: 'all', range: 'today' };
|
||||
const next = { keyword: '', protocol: 'all', direction: 'all', eventType: 'all', status: 'all', dateRange: recentBeijingDateRange(7) };
|
||||
setInputs(next);
|
||||
setFilters(next);
|
||||
setPage(1);
|
||||
@@ -286,9 +290,9 @@ function ProtocolInteractionPanel({ active }: { active: boolean }) {
|
||||
<Select onChange={(event) => setInputs((value) => ({ ...value, direction: event.target.value }))} options={[{ label: '全部方向', value: 'all' }, ...Object.entries(directionLabels).map(([value, label]) => ({ value, label }))]} value={inputs.direction} />
|
||||
<Select onChange={(event) => setInputs((value) => ({ ...value, eventType: event.target.value }))} options={[{ label: '全部事件', value: 'all' }, ...eventTypes.map((value) => ({ label: value, value }))]} value={inputs.eventType} />
|
||||
<Select onChange={(event) => setInputs((value) => ({ ...value, status: event.target.value }))} options={[{ label: '全部结果', value: 'all' }, { label: '已接收', value: 'received' }, { label: '已受理', value: 'accepted' }, { label: '成功', value: 'success' }, { label: '重试中', value: 'retrying' }, { label: '失败', value: 'failed' }]} value={inputs.status} />
|
||||
<DateRangeInput label="日志时间" onChange={(dateRange) => setInputs((value) => ({ ...value, dateRange }))} placeholder="选择日志时间区间" value={inputs.dateRange} />
|
||||
<div className="system-log-filters__actions"><Button icon={<Search size={16} />} onClick={query}>查询</Button><Button onClick={reset} variant="ghost">重置</Button></div>
|
||||
</div>
|
||||
<LogRange range={inputs.range} onChange={(range) => setInputs((value) => ({ ...value, range }))} />
|
||||
<div className="surface system-table-card protocol-log-table">
|
||||
<Table columns={columns} data={items} emptyText={error || '暂无通讯交互日志'} pagination={false} rowKey="id" />
|
||||
<Pagination nextDisabled={page >= totalPages} onNext={() => setPage((value) => Math.min(totalPages, value + 1))} onPrevious={() => setPage((value) => Math.max(1, value - 1))} page={Math.min(page, totalPages)} totalPages={totalPages} onPageChange={setPage} previousDisabled={page <= 1} total={total} />
|
||||
@@ -299,14 +303,3 @@ function ProtocolInteractionPanel({ active }: { active: boolean }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LogRange({ range, onChange }: { range: string; onChange: (value: string) => void }) {
|
||||
return (
|
||||
<div className="system-log-range">
|
||||
<span><CalendarDays size={18} /> 时间范围:</span>
|
||||
{[{ label: '今天', value: 'today' }, { label: '近7天', value: '7d' }, { label: '近30天', value: '30d' }, { label: '全部', value: 'all' }].map((item) => (
|
||||
<Button key={item.value} onClick={() => onChange(item.value)} size="sm" variant={range === item.value ? 'primary' : 'secondary'}>{item.label}</Button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { CalendarDays, FileText, Search } from 'lucide-react';
|
||||
import { FileText, Search } from 'lucide-react';
|
||||
import {
|
||||
Button,
|
||||
DateRangeInput,
|
||||
Input,
|
||||
Pagination,
|
||||
Select,
|
||||
SystemLogExport,
|
||||
Table,
|
||||
Tag,
|
||||
type DateRangeValue,
|
||||
type TableColumn,
|
||||
} from '@/components/ui';
|
||||
import { clientApi, type OperationLogItem } from '@/api/adminApi';
|
||||
import { formatDateTime } from '@/utils/dateTime';
|
||||
import { formatDateTime, recentBeijingDateRange } from '@/utils/dateTime';
|
||||
|
||||
type LogLevel = 'info' | 'success' | 'warning' | 'error';
|
||||
|
||||
@@ -33,7 +34,7 @@ export function ClientSystemLogsPage() {
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [level, setLevel] = useState('all');
|
||||
const [module, setModule] = useState('all');
|
||||
const [range, setRange] = useState('today');
|
||||
const [dateRange, setDateRange] = useState<DateRangeValue>(() => recentBeijingDateRange(7));
|
||||
const [page, setPage] = useState(1);
|
||||
const pageSize = 5;
|
||||
const [logs, setLogs] = useState<OperationLogItem[]>([]);
|
||||
@@ -42,7 +43,7 @@ export function ClientSystemLogsPage() {
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
clientApi.listSystemLogs({ keyword, level, module, range, page, pageSize })
|
||||
clientApi.listSystemLogs({ keyword, level, module, createdAtFrom: dateRange.start, createdAtTo: dateRange.end, page, pageSize })
|
||||
.then((data) => {
|
||||
setLogs(data.items);
|
||||
setModules(data.modules);
|
||||
@@ -54,7 +55,7 @@ export function ClientSystemLogsPage() {
|
||||
setTotal(0);
|
||||
setError(err instanceof Error ? err.message : '系统日志加载失败');
|
||||
});
|
||||
}, [keyword, level, module, range, page]);
|
||||
}, [keyword, level, module, dateRange.end, dateRange.start, page]);
|
||||
|
||||
const moduleOptions = useMemo(() => {
|
||||
return [{ label: '全部模块', value: 'all' }, ...modules.map((item) => ({ label: item, value: item }))];
|
||||
@@ -79,7 +80,7 @@ export function ClientSystemLogsPage() {
|
||||
<span className="sms-send-title__icon"><FileText size={22} /></span>
|
||||
<h1>系统日志</h1>
|
||||
</div>
|
||||
<SystemLogExport exportLogs={clientApi.exportSystemLogs} filters={{ keyword, level, module, range }} portal="client" />
|
||||
<SystemLogExport exportLogs={clientApi.exportSystemLogs} filters={{ keyword, level, module, createdAtFrom: dateRange.start, createdAtTo: dateRange.end }} portal="client" />
|
||||
</div>
|
||||
|
||||
<div className="system-log-filters">
|
||||
@@ -101,25 +102,7 @@ export function ClientSystemLogsPage() {
|
||||
value={level}
|
||||
/>
|
||||
<Select onChange={(event) => { setModule(event.target.value); setPage(1); }} options={moduleOptions} value={module} />
|
||||
</div>
|
||||
|
||||
<div className="system-log-range">
|
||||
<span><CalendarDays size={18} /> 时间范围:</span>
|
||||
{[
|
||||
{ label: '今天', value: 'today' },
|
||||
{ label: '近7天', value: '7d' },
|
||||
{ label: '近30天', value: '30d' },
|
||||
{ label: '全部', value: 'all' },
|
||||
].map((item) => (
|
||||
<Button
|
||||
key={item.value}
|
||||
onClick={() => setRange(item.value)}
|
||||
size="sm"
|
||||
variant={range === item.value ? 'primary' : 'secondary'}
|
||||
>
|
||||
{item.label}
|
||||
</Button>
|
||||
))}
|
||||
<DateRangeInput label="日志时间" onChange={(value) => { setDateRange(value); setPage(1); }} placeholder="选择日志时间区间" value={dateRange} />
|
||||
</div>
|
||||
|
||||
<div className="surface system-table-card">
|
||||
|
||||
@@ -9,6 +9,7 @@ export type DateRangeValue = {
|
||||
type DateRangeInputProps = {
|
||||
className?: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
value: DateRangeValue;
|
||||
onChange: (value: DateRangeValue) => void;
|
||||
};
|
||||
@@ -66,7 +67,7 @@ function getCalendarDays(viewDate: Date) {
|
||||
});
|
||||
}
|
||||
|
||||
export function DateRangeInput({ className = '', label, value, onChange }: DateRangeInputProps) {
|
||||
export function DateRangeInput({ className = '', label, placeholder = '选择提交时间区间', value, onChange }: DateRangeInputProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [viewDate, setViewDate] = useState(() => parseDate(value.start) ?? new Date());
|
||||
const startDate = parseDate(value.start);
|
||||
@@ -83,8 +84,8 @@ export function DateRangeInput({ className = '', label, value, onChange }: DateR
|
||||
return `${value.start} 至 ...`;
|
||||
}
|
||||
|
||||
return '选择提交时间区间';
|
||||
}, [value.end, value.start]);
|
||||
return placeholder;
|
||||
}, [placeholder, value.end, value.start]);
|
||||
|
||||
function moveMonth(offset: number) {
|
||||
setViewDate((current) => new Date(current.getFullYear(), current.getMonth() + offset, 1));
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Download, RotateCcw } from 'lucide-react';
|
||||
import type { SystemLogExportResult } from '@/api/adminApi';
|
||||
import { Button } from './Button';
|
||||
|
||||
type Filters = { keyword?: string; level?: string; module?: string; range?: string };
|
||||
type Filters = { keyword?: string; level?: string; module?: string; range?: string; createdAtFrom?: string; createdAtTo?: string };
|
||||
|
||||
export function SystemLogExport({
|
||||
portal,
|
||||
|
||||
+15
-12
@@ -580,20 +580,23 @@
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.client-amount-value,
|
||||
.client-amount-value .money-text {
|
||||
color: #111827 !important;
|
||||
font-family: inherit !important;
|
||||
font-size: 30px;
|
||||
.client-amount-value {
|
||||
background: none !important;
|
||||
color: #111111 !important;
|
||||
font-family: Arial, "Microsoft YaHei", sans-serif !important;
|
||||
font-size: 38px !important;
|
||||
font-style: normal;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
font-weight: 500 !important;
|
||||
letter-spacing: 0 !important;
|
||||
line-height: 1.2 !important;
|
||||
text-shadow: none !important;
|
||||
-webkit-background-clip: border-box !important;
|
||||
-webkit-text-fill-color: #111111 !important;
|
||||
}
|
||||
|
||||
.client-amount-value--compact,
|
||||
.client-amount-value--compact .money-text {
|
||||
font-size: 22px;
|
||||
.client-amount-value--compact {
|
||||
font-size: 28px !important;
|
||||
}
|
||||
|
||||
.send-submit-hint {
|
||||
@@ -3595,7 +3598,7 @@
|
||||
align-items: end;
|
||||
display: grid;
|
||||
gap: 22px;
|
||||
grid-template-columns: minmax(320px, 1fr) minmax(220px, 0.32fr) minmax(220px, 0.32fr);
|
||||
grid-template-columns: minmax(280px, 1.2fr) repeat(3, minmax(190px, .65fr)) auto;
|
||||
}
|
||||
|
||||
.system-log-range {
|
||||
@@ -3706,7 +3709,7 @@
|
||||
}
|
||||
|
||||
.protocol-log-filters {
|
||||
grid-template-columns: minmax(260px, 1.4fr) repeat(4, minmax(135px, .55fr));
|
||||
grid-template-columns: minmax(240px, 1.2fr) repeat(4, minmax(125px, .5fr)) minmax(230px, .8fr) auto;
|
||||
}
|
||||
|
||||
.protocol-log-table .ui-table {
|
||||
|
||||
Reference in New Issue
Block a user