feat(iit): QC deep fix + V3.1 architecture plan + project member management

QC System Deep Fix:
- HardRuleEngine: add null tolerance + field availability pre-check (skipped status)
- SkillRunner: baseline data merge for follow-up events + field availability check
- QcReportService: record-level pass rate calculation + accurate LLM XML report
- iitBatchController: legacy log cleanup (eventId=null) + upsert RecordSummary
- seed-iit-qc-rules: null/empty string tolerance + applicableEvents config

V3.1 Architecture Design (docs only, no code changes):
- QC engine V3.1 plan: 5-level data structure (CDISC ODM) + D1-D7 dimensions
- Three-batch implementation strategy (A: foundation, B: bubbling, C: new engines)
- Architecture team review: 4 whitepapers reviewed + feedback doc + 4 critical suggestions
- CRA Agent strategy roadmap + CRA 4-tool explanation doc for clinical experts

Project Member Management:
- Cross-tenant member search and assignment (remove tenant restriction)
- IIT project detail page enhancement with tabbed layout (KB + members)
- IitProjectContext for business-side project selection
- System-KB route access control adjustment for project operators

Frontend:
- AdminLayout sidebar menu restructure
- IitLayout with project context provider
- IitMemberManagePage new component
- Business-side pages adapt to project context

Prisma:
- 2 new migrations (user-project RBAC + is_demo flag)
- Schema updates for project member management

Made-with: Cursor
This commit is contained in:
2026-03-01 15:27:05 +08:00
parent c3f7d54fdf
commit 0b29fe88b5
61 changed files with 6877 additions and 524 deletions

View File

@@ -5,7 +5,6 @@
*/
import React, { useState, useEffect, useCallback } from 'react';
import { useSearchParams } from 'react-router-dom';
import {
Card,
Timeline,
@@ -31,6 +30,7 @@ import {
} from '@ant-design/icons';
import * as iitProjectApi from '../api/iitProjectApi';
import type { TimelineItem } from '../api/iitProjectApi';
import { useIitProject } from '../context/IitProjectContext';
const { Text } = Typography;
@@ -48,8 +48,7 @@ const TRIGGER_TAG: Record<string, { color: string; label: string }> = {
};
const AiStreamPage: React.FC = () => {
const [searchParams] = useSearchParams();
const projectId = searchParams.get('projectId') || '';
const { projectId } = useIitProject();
const [items, setItems] = useState<TimelineItem[]>([]);
const [total, setTotal] = useState(0);
@@ -82,10 +81,6 @@ const AiStreamPage: React.FC = () => {
fetchData();
};
if (!projectId) {
return <div style={{ padding: 24 }}><Empty description="请先选择一个项目" /></div>;
}
const timelineItems = items.map((item) => {
const dotCfg = STATUS_DOT[item.status] || { color: 'gray', icon: <ClockCircleOutlined /> };
const triggerCfg = TRIGGER_TAG[item.triggeredBy] || { color: 'default', label: item.triggeredBy };