feat(M08-D): 补审计日志与系统配置

This commit is contained in:
Codex
2026-08-10 13:28:54 +08:00
parent bd00aa6275
commit 7e170e8743
13 changed files with 794 additions and 3 deletions
@@ -42,6 +42,7 @@ import { DeviceCommandService } from '../dist/devices/device-command-service.js'
import { DeviceControlService } from '../dist/devices/device-control-service.js';
import { MemberProfileService } from '../dist/wallets/member-profile-service.js';
import { BusinessStatisticsRepository } from '../dist/operations/business-statistics-repository.js';
import { SystemOperationsRepository } from '../dist/operations/system-operations-repository.js';
import {
executeMigrationPlan,
loadMigrationPlan,
@@ -1795,6 +1796,49 @@ async function assertFranchiseManagement(pool, context) {
assert.equal(auditRows.some((row) => row.metadata.includes('13800138000')), false);
}
async function assertSystemOperations(pool, context) {
const [adminRows] = await pool.query(
`SELECT u.id FROM qipai_users u
INNER JOIN qipai_user_roles ur ON ur.tenant_id = u.tenant_id AND ur.user_id = u.id
INNER JOIN qipai_roles r ON r.id = ur.role_id AND r.tenant_id = ur.tenant_id
WHERE u.tenant_id = ? AND r.code = 'TENANT_ADMIN' LIMIT 1`, [context.tenantId]
);
const adminId = String(adminRows[0].id);
const access = await new RbacRepository(pool).getAccessProfile(context.tenantId, adminId);
const actor = { tenantId: context.tenantId, userId: adminId, access,
traceId: 'm08d-system-live', ip: '172.18.20.42', userAgent: 'M08-D system live test' };
await pool.execute(
`INSERT INTO qipai_audit_logs
(tenant_id, actor_type, actor_id, action, resource_type, resource_id,
trace_id, ip, user_agent, metadata) VALUES (?, 'USER', ?, 'M08D_REDACTION_PROBE',
'TENANT', ?, ?, ?, ?, ?)`,
[context.tenantId, adminId, context.tenantId, actor.traceId, actor.ip, actor.userAgent,
JSON.stringify({ orderId: 'safe-order', phone: '13800138000', nested: { apiKey: 'secret', safe: 'visible' } })]
);
const repository = new SystemOperationsRepository(pool);
const logs = await repository.listAuditLogs({
tenantId: context.tenantId, page: 1, pageSize: 20, action: 'M08D_REDACTION_PROBE'
});
assert.equal(logs.total, 1);
assert.equal(logs.items[0].tenantId, context.tenantId);
assert.equal(logs.items[0].ip, '172.18.***.***');
assert.equal(logs.items[0].metadata.phone, '[REDACTED]');
assert.equal(logs.items[0].metadata.nested.apiKey, '[REDACTED]');
assert.equal(logs.items[0].metadata.nested.safe, 'visible');
const overview = await repository.getSystemOverview(context.tenantId);
assert.equal(overview.tenant.id, context.tenantId);
assert.equal(overview.latestMigration.version, '2026081003');
assert.ok(overview.counts.userCount > 0);
await repository.updateTenant(actor, context.tenantId, {
name: overview.tenant.name, timezone: overview.tenant.timezone
});
const updatedLogs = await repository.listAuditLogs({
tenantId: context.tenantId, page: 1, pageSize: 20, action: 'TENANT_SYSTEM_CONFIG_UPDATED'
});
assert.equal(updatedLogs.total, 1);
assert.equal(updatedLogs.items[0].actorId, adminId);
}
async function assertDeviceTopology(pool, context) {
const [adminRows] = await pool.query(
`SELECT u.id FROM qipai_users u
@@ -2083,6 +2127,7 @@ try {
await assertStoreRoomDomain(pool, loginContext);
await assertContentManagement(pool, loginContext);
await assertFranchiseManagement(pool, loginContext);
await assertSystemOperations(pool, loginContext);
await assertStoreDiscovery(pool, loginContext);
await assertSceneAndWifiAccess(pool, loginContext);
await assertPricingAndReservations(pool, loginContext);
@@ -2173,6 +2218,10 @@ try {
'versioned decoration publish and archive',
'store advertisement delivery scope',
'platform advertisement rejection',
'franchise application idempotency and phone-free audit metadata',
'franchise assignment and controlled follow-up status transition',
'tenant-scoped audit filtering with recursive sensitive metadata redaction',
'system overview and audited tenant configuration update',
'city fallback store filtering',
'server-side distance sorting',
'empty manual city result',