feat(iit): V3.2 data consistency + project isolation + admin config redesign + Chinese labels

Summary:
- Refactor timeline API to read from qc_field_status (SSOT) instead of qc_logs
- Add field-issues paginated API with severity/dimension/recordId filters
- Add LEFT JOIN field_metadata + qc_event_status for Chinese display names
- Implement per-project ChatOrchestrator cache and SessionMemory isolation
- Redesign admin IIT config tabs (REDCap -> Fields -> KB -> Rules -> Members)
- Add AI-powered QC rule generation (D3 programmatic + D1/D5/D6 LLM-based)
- Add clickable warning/critical detail Modal in ReportsPage
- Auto-dispatch eQuery after batch QC via DailyQcOrchestrator
- Update module status documentation to v3.2

Backend changes:
- iitQcCockpitController: rewrite getTimeline from qc_field_status, add getFieldIssues
- iitQcCockpitRoutes: add field-issues route
- ChatOrchestrator: per-projectId cached instances
- SessionMemory: keyed by userId::projectId
- WechatCallbackController: resolve projectId from iitUserMapping
- iitRuleSuggestionService: dimension-based suggest + generateD3Rules
- iitBatchController: call DailyQcOrchestrator after batch QC

Frontend changes:
- AiStreamPage: adapt to new timeline structure with dimension tags
- ReportsPage: clickable stats cards with issue detail Modal
- IitProjectDetailPage: reorder tabs, add AI rule generation UI
- iitProjectApi: add TimelineIssue, FieldIssueItem types and APIs

Status: TypeScript compilation verified, no new lint errors
Made-with: Cursor
This commit is contained in:
2026-03-02 14:29:59 +08:00
parent 72928d3116
commit 71d32d11ee
38 changed files with 1597 additions and 546 deletions

View File

@@ -152,8 +152,52 @@ class ModuleService {
});
});
// 6. 合并所有模块(去重
const moduleSet = new Set(tenantModulesData.map(tm => tm.module_code));
// 5.5 查询用户级别的模块权限(精细化控制
const userModulesData = await prisma.user_modules.findMany({
where: {
user_id: userId,
tenant_id: { in: tenantIds },
},
select: {
tenant_id: true,
module_code: true,
is_enabled: true,
},
});
// 按租户分组 user_modules
const userModulesByTenant = new Map<string, Map<string, boolean>>();
for (const um of userModulesData) {
if (!userModulesByTenant.has(um.tenant_id)) {
userModulesByTenant.set(um.tenant_id, new Map());
}
userModulesByTenant.get(um.tenant_id)!.set(um.module_code, um.is_enabled);
}
// 6. 合并所有模块(去重),尊重 user_modules 精细化配置
const moduleSet = new Set<string>();
for (const tm of tenantModulesData) {
const userModulesForTenant = userModulesByTenant.get(tm.tenant_id);
if (userModulesForTenant && userModulesForTenant.size > 0) {
const isEnabled = userModulesForTenant.get(tm.module_code);
if (isEnabled) {
moduleSet.add(tm.module_code);
}
} else {
moduleSet.add(tm.module_code);
}
}
// 6.5 补充用户级独立配置的模块(如 AIA_PROTOCOL租户未订阅但用户单独开通
for (const [, userModuleMap] of userModulesByTenant) {
for (const [moduleCode, isEnabled] of userModuleMap) {
if (isEnabled) {
moduleSet.add(moduleCode);
}
}
}
const allModuleCodes = Array.from(moduleSet);
// 7. 获取模块详细信息