Files
thebet365/apps/api/src/domains/operations/audit/catalog-audit.ts
Mars 2a8b8415e7 feat: 赛事管理操作日志与审计页体验优化
- API 为赛事/联赛/盘口/赔率/冠军盘写操作写入 CATALOG 审计日志,并新增 catalog-audit-logs 接口

- 管理端将赛事日志独立至赛事管理子页,通用操作日志排除 CATALOG 模块

- 统一赛事子页 list-chrome 布局与面包屑;修复操作日志页表格滚动与分页不可见问题

- 补充中英文/马来语文案及 UAT 验收项 SEC013/SEC014

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 10:00:24 +08:00

88 lines
2.7 KiB
TypeScript

import { AuditService } from './audit.service';
export const CATALOG_AUDIT_MODULE = 'CATALOG';
export const CatalogAuditAction = {
CREATE_LEAGUE: 'CREATE_LEAGUE',
UPDATE_LEAGUE: 'UPDATE_LEAGUE',
ARCHIVE_LEAGUE: 'ARCHIVE_LEAGUE',
CREATE_TEAM: 'CREATE_TEAM',
CREATE_MATCH: 'CREATE_MATCH',
UPDATE_MATCH: 'UPDATE_MATCH',
DELETE_MATCH: 'DELETE_MATCH',
ARCHIVE_MATCH: 'ARCHIVE_MATCH',
IMPORT_MATCHES: 'IMPORT_MATCHES',
PUBLISH_MATCH: 'PUBLISH_MATCH',
UNPUBLISH_MATCH: 'UNPUBLISH_MATCH',
CLOSE_MATCH: 'CLOSE_MATCH',
REOPEN_MATCH: 'REOPEN_MATCH',
CANCEL_MATCH: 'CANCEL_MATCH',
CREATE_MARKET_TEMPLATE: 'CREATE_MARKET_TEMPLATE',
UPDATE_MARKET_TEMPLATE: 'UPDATE_MARKET_TEMPLATE',
DUPLICATE_MARKET_TEMPLATE: 'DUPLICATE_MARKET_TEMPLATE',
SET_DEFAULT_MARKET_TEMPLATE: 'SET_DEFAULT_MARKET_TEMPLATE',
GENERATE_MATCH_MARKETS: 'GENERATE_MATCH_MARKETS',
APPLY_MARKET_TEMPLATE: 'APPLY_MARKET_TEMPLATE',
BULK_SAVE_MATCH_MARKETS: 'BULK_SAVE_MATCH_MARKETS',
UPDATE_MATCH_ODDS: 'UPDATE_MATCH_ODDS',
UPDATE_MARKET: 'UPDATE_MARKET',
UPDATE_SELECTION: 'UPDATE_SELECTION',
CREATE_OUTRIGHT: 'CREATE_OUTRIGHT',
UPDATE_OUTRIGHT: 'UPDATE_OUTRIGHT',
UPDATE_OUTRIGHT_ODDS: 'UPDATE_OUTRIGHT_ODDS',
ADD_OUTRIGHT_SELECTION: 'ADD_OUTRIGHT_SELECTION',
BATCH_ADD_OUTRIGHT_SELECTIONS: 'BATCH_ADD_OUTRIGHT_SELECTIONS',
UPDATE_OUTRIGHT_SELECTION: 'UPDATE_OUTRIGHT_SELECTION',
REMOVE_OUTRIGHT_SELECTION: 'REMOVE_OUTRIGHT_SELECTION',
IMPORT_WC2026_OUTRIGHT: 'IMPORT_WC2026_OUTRIGHT',
} as const;
export type CatalogAuditActionCode =
(typeof CatalogAuditAction)[keyof typeof CatalogAuditAction];
export function catalogTargetId(id: string | bigint | null | undefined): string | undefined {
if (id == null) return undefined;
return String(id);
}
export function summarizeOddsUpdates(
selectionIds: Array<string | bigint>,
maxIds = 20,
): {
updatedCount: number;
selectionIds: string[];
selectionIdsTruncated?: boolean;
total?: number;
} {
const ids = selectionIds.map((id) => String(id));
const truncated = ids.length > maxIds;
return {
updatedCount: ids.length,
selectionIds: ids.slice(0, maxIds),
...(truncated ? { selectionIdsTruncated: true, total: ids.length } : {}),
};
}
export async function logCatalogAudit(
audit: AuditService,
data: {
operatorId: bigint;
action: CatalogAuditActionCode;
targetId?: string | bigint | null;
beforeData?: unknown;
afterData?: unknown;
ipAddress?: string;
},
) {
return audit.log({
operatorId: data.operatorId,
operatorType: 'ADMIN',
action: data.action,
module: CATALOG_AUDIT_MODULE,
targetId: catalogTargetId(data.targetId),
beforeData: data.beforeData,
afterData: data.afterData,
ipAddress: data.ipAddress,
});
}