docs(plan): add project init template cloning implementation plan
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
weichen
co-authored by
Cursor
parent
9cebf826ba
commit
1e8e7ed3ce
@@ -0,0 +1,867 @@
|
||||
# 项目创建即时克隆全生命周期文档模板副本实施计划
|
||||
# (Project-Init Template Cloning & Full-Lifecycle Workflow Visualization Plan)
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 当用户在工作台中新建项目时,自动为该项目即时克隆全套 7 份全生命周期标准受控文档模板副本,注入项目元数据并隔离展示,使用户一上来即可全景查阅工作流大纲与章节规范,并在智能体生成时平滑转为正式受控草稿。
|
||||
|
||||
**Architecture:**
|
||||
1. 在类型系统层将 `DeliverableItem` 与 `projectId` 深度绑定,扩展 `isTemplateOnly` 标识;
|
||||
2. 构建前端模板工厂 `templateFactory.ts`,汇聚 V0.1、V0.5、V1.0 全生命周期 7 份标准医疗器械受控模板;
|
||||
3. 在新建项目(`onNewSession`)时原子化注入元数据并生成独立副本集合与 AST 缓存;
|
||||
4. 交付物列表按当前会话 `projectId` 严格隔离,支持大纲查阅与前置批注,并在智能体填报时平滑解套升级为受控草稿。
|
||||
|
||||
**Tech Stack:** React 19, TypeScript, Vitest / Node Test Runner, FlowMind Token 设计系统, Tailwind CSS.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- **测试规范**:每个阶段必须包含明确的单元测试或集成测试,全量测试必须 100% 通过(`npm run test`)。
|
||||
- **三层隐私与受控红线**:工作流文档(`workflow`)必须全员可见且不可删除,私密文档严格按 `creatorId` 隔离,各项目交付物严格按 `projectId` 物理/逻辑隔离。
|
||||
- **模板交付物完整性**:新建项目必须完整具备 7 份核心文档副本(`intake`、`research`、`feasibility_reg`、`feasibility_dev`、`user_requirements`、`prelim_design`、`cdo_quote`)。
|
||||
- **无破坏性修改**:向下兼容既有测试用例,已有 `sess-001`、`sess-002` 会话的交付物与正文必须平滑过渡。
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 扩展交付物与文档治理类型定义 (`projectId` & `isTemplateOnly`)
|
||||
|
||||
**Files:**
|
||||
- Modify: `web/src/types/docgov.ts:1-10`
|
||||
- Modify: `web/src/types/workbench.ts:95-125`
|
||||
- Modify: `web/src/data/mockDocuments.ts:10-35`
|
||||
- Test: `web/src/types/types.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `DocLifecycleState` from `web/src/types/docgov.ts`
|
||||
- Produces: `DeliverableItem.projectId: number`, `DeliverableItem.isTemplateOnly: boolean`, `DocLifecycleState: 'template' | ...`
|
||||
|
||||
- [ ] **Step 1: 编写失败测试**
|
||||
|
||||
在 `web/src/types/types.test.ts` 中添加对 `DeliverableItem` 包含 `projectId` 与 `isTemplateOnly` 的契约断言:
|
||||
|
||||
```typescript
|
||||
// web/src/types/types.test.ts
|
||||
import test from 'node-test';
|
||||
import assert from 'node:assert';
|
||||
import { DeliverableItem } from './workbench';
|
||||
import { DocLifecycleState } from './docgov';
|
||||
|
||||
test('DeliverableItem 必须包含 projectId 与 isTemplateOnly 属性', () => {
|
||||
const item: DeliverableItem = {
|
||||
docId: 'doc-test-1',
|
||||
projectId: 101,
|
||||
taskKey: 'intake',
|
||||
title: '用户初始需求收集表',
|
||||
stage: 'V0.1',
|
||||
category: 'workflow',
|
||||
creatorId: 1,
|
||||
creatorName: '张工',
|
||||
isFixedWorkflow: true,
|
||||
isTemplateOnly: true,
|
||||
lifecycleState: 'template',
|
||||
isClaimed: false,
|
||||
author: '张工',
|
||||
reviewer: '李主管',
|
||||
approver: '王总监',
|
||||
updatedAt: '2026-09-08 20:00',
|
||||
};
|
||||
|
||||
assert.strictEqual(item.projectId, 101);
|
||||
assert.strictEqual(item.isTemplateOnly, true);
|
||||
assert.strictEqual(item.lifecycleState, 'template');
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试验证失败**
|
||||
|
||||
Run: `cd web && npx tsx --test src/types/types.test.ts`
|
||||
Expected: 编译或运行报错,指出 `isTemplateOnly` 或 `'template'` 不符合现有类型。
|
||||
|
||||
- [ ] **Step 3: 实现类型扩展与兼容性更新**
|
||||
|
||||
1. 修改 `web/src/types/docgov.ts`:
|
||||
```typescript
|
||||
export type DocLifecycleState =
|
||||
| 'template'
|
||||
| 'drafting'
|
||||
| 'reviewing'
|
||||
| 'approving'
|
||||
| 'approved'
|
||||
| 'rejected';
|
||||
```
|
||||
|
||||
2. 修改 `web/src/types/workbench.ts` 中的 `DeliverableItem`:
|
||||
```typescript
|
||||
export interface DeliverableItem {
|
||||
docId: string;
|
||||
projectId: number;
|
||||
taskKey: string;
|
||||
title: string;
|
||||
stage: StageKey;
|
||||
category: DeliverableCategory;
|
||||
creatorId: number;
|
||||
creatorName: string;
|
||||
sharedAt?: string;
|
||||
isFixedWorkflow: boolean;
|
||||
isTemplateOnly: boolean;
|
||||
lifecycleState: DocLifecycleState;
|
||||
isClaimed: boolean;
|
||||
author: string;
|
||||
reviewer: string;
|
||||
approver: string;
|
||||
updatedAt: string;
|
||||
summary?: string;
|
||||
sourceMessageSnippet?: string;
|
||||
}
|
||||
```
|
||||
|
||||
3. 更新 `web/src/data/mockDocuments.ts` 的 `MockDocumentMeta`:
|
||||
```typescript
|
||||
export interface MockDocumentMeta {
|
||||
docId: string;
|
||||
projectId?: number;
|
||||
taskKey: string;
|
||||
title: string;
|
||||
stage: string;
|
||||
category: DeliverableCategory;
|
||||
creatorId: number;
|
||||
creatorName: string;
|
||||
sharedAt?: string;
|
||||
version: string;
|
||||
lifecycleState: DocLifecycleState;
|
||||
isTemplateOnly?: boolean;
|
||||
isClaimed: boolean;
|
||||
author: string;
|
||||
reviewer: string;
|
||||
approver: string;
|
||||
updatedAt: string;
|
||||
summary: string;
|
||||
markdown: string;
|
||||
}
|
||||
```
|
||||
并在 `MOCK_DOCUMENTS` 现有数据项中注入 `projectId: 1` 和 `isTemplateOnly: false`。
|
||||
|
||||
- [ ] **Step 4: 运行测试验证通过**
|
||||
|
||||
Run: `cd web && npx tsx --test src/types/types.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 5: 提交代码**
|
||||
|
||||
```bash
|
||||
git add web/src/types/docgov.ts web/src/types/workbench.ts web/src/data/mockDocuments.ts web/src/types/types.test.ts
|
||||
git commit -m "refactor(types): extend deliverable item with projectId and isTemplateOnly"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 构建全生命周期模板克隆工厂与单元测试 (`templateFactory.ts`)
|
||||
|
||||
**Files:**
|
||||
- Create: `web/src/engine/templateFactory.ts`
|
||||
- Create: `web/src/engine/templateFactory.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `markdownToBlockAST` from `web/src/engine/markdownToBlock.ts`, `DeliverableItem` from `web/src/types/workbench.ts`
|
||||
- Produces: `createProjectTemplateDeliverables(params: TemplateFactoryParams): TemplateFactoryResult`
|
||||
|
||||
- [ ] **Step 1: 编写失败单元测试**
|
||||
|
||||
创建 `web/src/engine/templateFactory.test.ts`:
|
||||
|
||||
```typescript
|
||||
import test from 'node-test';
|
||||
import assert from 'node:assert';
|
||||
import {
|
||||
createProjectTemplateDeliverables,
|
||||
STANDARD_WORKFLOW_TEMPLATES,
|
||||
} from './templateFactory';
|
||||
|
||||
test('createProjectTemplateDeliverables: 必须精确生成 7 份全生命周期受控文档副本', () => {
|
||||
const result = createProjectTemplateDeliverables({
|
||||
projectId: 999,
|
||||
projectName: '微创智能吻合吻合器系统',
|
||||
deviceType: '腔镜吻合器',
|
||||
creatorId: 10,
|
||||
creatorName: '赵工程师',
|
||||
});
|
||||
|
||||
// 1. 验证数量与任务映射
|
||||
assert.strictEqual(result.deliverables.length, 7);
|
||||
assert.strictEqual(Object.keys(result.docASTMap).length, 7);
|
||||
|
||||
const expectedTaskKeys = [
|
||||
'intake',
|
||||
'research',
|
||||
'feasibility_reg',
|
||||
'feasibility_dev',
|
||||
'user_requirements',
|
||||
'prelim_design',
|
||||
'cdo_quote',
|
||||
];
|
||||
const actualTaskKeys = result.deliverables.map((d) => d.taskKey);
|
||||
assert.deepStrictEqual(actualTaskKeys, expectedTaskKeys);
|
||||
|
||||
// 2. 验证阶段覆盖 (V0.1, V0.5, V1.0)
|
||||
const stages = new Set(result.deliverables.map((d) => d.stage));
|
||||
assert.ok(stages.has('V0.1'));
|
||||
assert.ok(stages.has('V0.5'));
|
||||
assert.ok(stages.has('V1.0'));
|
||||
|
||||
// 3. 验证元数据装配与模板标记
|
||||
result.deliverables.forEach((doc) => {
|
||||
assert.strictEqual(doc.projectId, 999);
|
||||
assert.strictEqual(doc.isTemplateOnly, true);
|
||||
assert.strictEqual(doc.lifecycleState, 'template');
|
||||
assert.strictEqual(doc.isFixedWorkflow, true);
|
||||
assert.strictEqual(doc.creatorId, 10);
|
||||
assert.strictEqual(doc.creatorName, '赵工程师');
|
||||
assert.ok(doc.docId.startsWith('doc-p999-'));
|
||||
|
||||
const ast = result.docASTMap[doc.docId];
|
||||
assert.ok(ast, `AST for ${doc.docId} should exist`);
|
||||
assert.ok(ast.blocks.length > 0, `AST for ${doc.docId} should have blocks`);
|
||||
});
|
||||
|
||||
// 4. 验证变量注入 (项目名、器械类型注入成功,无裸露的宏占位符)
|
||||
const intakeDoc = result.deliverables.find((d) => d.taskKey === 'intake')!;
|
||||
const intakeAst = result.docASTMap[intakeDoc.docId];
|
||||
const fullText = JSON.stringify(intakeAst);
|
||||
assert.ok(fullText.includes('微创智能吻合吻合器系统'));
|
||||
assert.ok(fullText.includes('腔镜吻合器'));
|
||||
assert.ok(!fullText.includes('{{project_name}}'));
|
||||
assert.ok(!fullText.includes('{{device_type}}'));
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试验证失败**
|
||||
|
||||
Run: `cd web && npx tsx --test src/engine/templateFactory.test.ts`
|
||||
Expected: FAIL with "Cannot find module './templateFactory'"
|
||||
|
||||
- [ ] **Step 3: 编写模板克隆工厂实现 (`web/src/engine/templateFactory.ts`)**
|
||||
|
||||
创建 `web/src/engine/templateFactory.ts`,内置 7 份医疗器械全生命周期受控骨架模板正文(包含 H1/H2 规范章节、引证块、Mermaid 架构图、LaTeX 数学公式与国家强标三线表):
|
||||
|
||||
```typescript
|
||||
import { DeliverableItem, StageKey } from '../types/workbench';
|
||||
import { DocumentAST } from '../types/block';
|
||||
import { markdownToBlockAST } from './markdownToBlock';
|
||||
|
||||
export interface TemplateFactoryParams {
|
||||
projectId: number;
|
||||
projectName: string;
|
||||
deviceType: string;
|
||||
creatorId: number;
|
||||
creatorName: string;
|
||||
customerName?: string;
|
||||
authorName?: string;
|
||||
reviewerName?: string;
|
||||
approverName?: string;
|
||||
}
|
||||
|
||||
export interface TemplateFactoryResult {
|
||||
deliverables: DeliverableItem[];
|
||||
docASTMap: Record<string, DocumentAST>;
|
||||
}
|
||||
|
||||
export interface TemplateDefinition {
|
||||
taskKey: string;
|
||||
title: string;
|
||||
stage: StageKey;
|
||||
summary: string;
|
||||
markdownSkeleton: string;
|
||||
}
|
||||
|
||||
export const STANDARD_WORKFLOW_TEMPLATES: TemplateDefinition[] = [
|
||||
{
|
||||
taskKey: 'intake',
|
||||
title: '用户初始需求收集表',
|
||||
stage: 'V0.1',
|
||||
summary: '梳理客户原始临床需求与约束条件,提炼事实清单、技术假设与待确认缺口。',
|
||||
markdownSkeleton: `# 用户初始需求收集表 (项目模板副本)
|
||||
|
||||
- 项目名称:{{project_name}}
|
||||
- 申报器械:{{device_type}}
|
||||
- 客户单位:{{customer_name}}
|
||||
- 创建时间:{{generated_at}}
|
||||
- 状态:【初始模板待填报】
|
||||
|
||||
## 1. 临床背景与预期用途
|
||||
本项目针对微创临床应用场景,计划研制符合法规规范的 {{device_type}}。
|
||||
> [待确认] 临床科室、主要适应症与操作痛点待智能体小队与客户进一步摸底确认。
|
||||
|
||||
## 2. 客户诉求与关键约束
|
||||
- 预算目标:待确认
|
||||
- 研发周期:预计 6~12 个月
|
||||
- 目标市场:中国 NMPA(重点关注)
|
||||
|
||||
## 3. 已有输入材料摘要
|
||||
当前项目已就绪基础信息,待导入技术规格、竞品参数及医院现场调研纪要。
|
||||
|
||||
## 4. 阻塞与待确认清单
|
||||
- [ ] 确定产品核心工作模式与工作频段
|
||||
- [ ] 明确预期配套使用耗材或附件
|
||||
- [ ] 确认是否需符合专用安全标准
|
||||
`,
|
||||
},
|
||||
{
|
||||
taskKey: 'research',
|
||||
title: '全球竞品技术与临床深度调研报告',
|
||||
stage: 'V0.5',
|
||||
summary: '国内外主流竞品性能对标、临床痛点反思、FTO专利侵权风险排查与差异化突破建议。',
|
||||
markdownSkeleton: `# 全球竞品技术与临床深度调研报告 (项目模板副本)
|
||||
|
||||
- 项目名称:{{project_name}}
|
||||
- 分析对象:{{device_type}}
|
||||
- 生成基准:{{generated_at}}
|
||||
|
||||
## 1. 全球主要竞品横向性能对标
|
||||
针对国际一线品牌同类器械开展多维度技术参数对比:
|
||||
|
||||
| 关键技术指标 | 国际竞品 A | 国际竞品 B | 本项目技术指标 (目标) |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| 成像/测量精度 | 待填报 | 待填报 | 待智能体结合基线生成 |
|
||||
| 接口通信延时 | 待填报 | 待填报 | < 35 ms |
|
||||
| 探头工作端温升 | 待填报 | 待填报 | ≤ 38.5 ℃ |
|
||||
| 市场参考售价 | 待填报 | 待填报 | 高性价比国产化替代 |
|
||||
|
||||
## 2. 外科临床核心痛点
|
||||
1. 现有进口设备操作学习曲线陡峭;
|
||||
2. 极端工况下长期稳定性不足;
|
||||
3. 配套高值耗材采购成本高昂。
|
||||
|
||||
## 3. 自由实施 (FTO) 专利回避流程
|
||||
\`\`\`mermaid
|
||||
flowchart TD
|
||||
Search[专利数据库初步检索] --> Filter[锁定涉案核心权项]
|
||||
Filter --> RiskCheck{是否存在侵权涉案特征?}
|
||||
RiskCheck -- 是 --> Redesign[提出回避设计方案]
|
||||
RiskCheck -- 否 --> Clear[合规绿灯:免除侵权风险]
|
||||
\`\`\`
|
||||
|
||||
## 4. 商业化突破与市场定位建议
|
||||
建议采取“精准平替+临床增强”双轮驱动策略,切入国内地市级三甲微创中心。
|
||||
`,
|
||||
},
|
||||
{
|
||||
taskKey: 'feasibility_reg',
|
||||
title: '注册路径与合规可行性评估报告',
|
||||
stage: 'V0.5',
|
||||
summary: '医疗器械分类界定依据、强制性国行标检测要求、临床评价路径与注册审评审批规划。',
|
||||
markdownSkeleton: `# 注册路径与合规可行性评估报告 (项目模板副本)
|
||||
|
||||
- 项目名称:{{project_name}}
|
||||
- 申报器械:{{device_type}}
|
||||
- 基线时间:{{generated_at}}
|
||||
|
||||
## 1. 监管分类界定与依据条目
|
||||
依据国家药品监督管理局《医疗器械分类目录》,初步判定如下:
|
||||
> [合规引证] 待智能体小队依据知识库最新版分类目录条目进行精确比对与推荐。
|
||||
|
||||
## 2. 强制性合规标准矩阵
|
||||
经查验国家医疗器械强制性国家标准库与行业标准库,本项目预研规划需满足:
|
||||
|
||||
| 标准编号 | 标准名称 | 适用等级 | 拟采取合规工程措施 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| GB 9706.1-2020 | 医用电气设备 第1部分:基本安全和基本性能的通用要求 | 强制性国标 | 隔离电源设计与微安级漏电流控制 |
|
||||
| YY 9706.102-2021 | 医用电气设备 第1-2部分:电磁兼容要求和试验 | 强制性行标 | 滤波隔离阵列与抗扰度 Class B 防护 |
|
||||
| GB/T 16886.1 | 医疗器械生物学评价 第1部分:风险管理过程中的评价与试验 | 生物相容性 | 人体接触部位材料无毒性检验 |
|
||||
|
||||
## 3. 成像与探测信噪比物理判据模型
|
||||
$$
|
||||
SNR = \\frac{S_{signal}}{\\sqrt{\\sigma_{ambient}^2 + \\sigma_{dark}^2 + \\sigma_{read}^2}} \\ge 18.5\\,\\text{dB}
|
||||
$$
|
||||
|
||||
## 4. 结论与申报建议
|
||||
(待智能体小队检索法规库后填报正式合规结论)。
|
||||
`,
|
||||
},
|
||||
{
|
||||
taskKey: 'feasibility_dev',
|
||||
title: '设计开发可行性评估报告',
|
||||
stage: 'V0.5',
|
||||
summary: '工程化差距分析、核心元器件供应链自控能力、软硬件技术路线与潜在失效风险应对。',
|
||||
markdownSkeleton: `# 设计开发可行性评估报告 (项目模板副本)
|
||||
|
||||
- 项目名称:{{project_name}}
|
||||
- 评估对象:{{device_type}}
|
||||
- 报告时间:{{generated_at}}
|
||||
|
||||
## 1. 技术路线可行性概述
|
||||
针对 {{device_type}} 的研制目标,工程团队对核心光电/传感方案与算法架构进行可行性论证。
|
||||
|
||||
## 2. 关键工程化差距评估表
|
||||
|
||||
| 研发模块 | 当前技术现状 | 量产目标指标 | 工程差距等级 | 解决途径 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| 前端传感采集 | 实验室原理样件 | 工业级高灵敏度探头 | 中等差距 | 选型车规/医疗级芯片 |
|
||||
| 高速算法处理 | 软件算法离线运行 | FPGA硬件级超低延时 | 重点突破 | 定制硬件并行处理流水线 |
|
||||
| 整机热平衡 | 局部温升超标 | 工作端面 ≤ 41℃ | 必须达标 | 全金属微通道导热设计 |
|
||||
|
||||
## 3. 潜在技术风险与缓解措施
|
||||
1. **核心芯片交期风险**:在硬件主板预留引脚兼容的国产替代芯片接口;
|
||||
2. **算法实时性风险**:采用双缓冲乒乓流水架构,压减内存搬移开销。
|
||||
`,
|
||||
},
|
||||
{
|
||||
taskKey: 'user_requirements',
|
||||
title: '医疗器械用户需求规范表 (URS)',
|
||||
stage: 'V0.5',
|
||||
summary: '明确术者与护士核心角色画像,建立可追溯的工程需求条目与临床验收准则。',
|
||||
markdownSkeleton: `# 医疗器械用户需求规范表 (URS) (项目模板副本)
|
||||
|
||||
- 项目名称:{{project_name}}
|
||||
- 受控编号:URS-{{project_name}}-V1.0
|
||||
- 基线日期:{{generated_at}}
|
||||
|
||||
## 1. 临床角色画像与操作流程
|
||||
- **主要术者 (主任医师)**:要求手术过程中无感知延迟,视觉色彩真实无拖影;
|
||||
- **巡回护士**:要求设备快速开机,线缆连接具备防呆设计,移动轻便。
|
||||
|
||||
## 2. 可追溯用户需求条目清单
|
||||
|
||||
| 需求 ID | 需求陈述 | 临床来源 | 优先级 | 验收准则与思路 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| UR-001 | 系统从冷机加电到输出稳定画面时间必须小于 15 秒 | 手术室快速急救准备 | 高 | 计时器实测加电启动时间 |
|
||||
| UR-002 | 手持探头工作面接触人体部分温升不得引起组织灼伤 | GB 9706.1 强标要求 | 高 (强制) | 热成像仪全负荷 4 小时温升实测 |
|
||||
| UR-003 | 支持脚踏控制一键切换工作显示模式 | 术者术中无菌操作要求 | 中 | 模拟手术操作盲按切换试验 |
|
||||
`,
|
||||
},
|
||||
{
|
||||
taskKey: 'prelim_design',
|
||||
title: '系统初步架构与电路规格设计书',
|
||||
stage: 'V0.5',
|
||||
summary: '硬件拓扑、高速数据链路传输拓扑、算法数学配准模型与关键元器件BOM选型。',
|
||||
markdownSkeleton: `# 系统初步架构与电路规格设计书 (项目模板副本)
|
||||
|
||||
- 项目名称:{{project_name}}
|
||||
- 架构对象:{{device_type}}
|
||||
- 日期:{{generated_at}}
|
||||
|
||||
## 1. 硬件系统拓扑与数据链路
|
||||
\`\`\`mermaid
|
||||
graph LR
|
||||
Sensor["前端医用传感器组件"] -->|高速差分传输| ISP["专用图像与信号调理单元"]
|
||||
ISP --> Processing["FPGA/DSP 核心数字处理引擎"]
|
||||
Processing --> UI["医用高分监视器 & 医生控制台"]
|
||||
\`\`\`
|
||||
|
||||
## 2. 核心传感器像素配准算法矩阵
|
||||
$$
|
||||
\\begin{bmatrix} x_{aligned} \\\\ y_{aligned} \\\\ 1 \\end{bmatrix} = \\begin{bmatrix} h_{11} & h_{12} & h_{13} \\\\ h_{21} & h_{22} & h_{23} \\\\ h_{31} & h_{32} & 1 \\end{bmatrix} \\times \\begin{bmatrix} x_{raw} \\\\ y_{raw} \\\\ 1 \\end{bmatrix}
|
||||
$$
|
||||
|
||||
## 3. 整板功耗与散热设计
|
||||
预计整机工作功耗约 28W,采用自然对流与铝合金阳极氧化外壳导热。
|
||||
`,
|
||||
},
|
||||
{
|
||||
taskKey: 'cdo_quote',
|
||||
title: 'CDO商业报价单与工时测算清单',
|
||||
stage: 'V1.0',
|
||||
summary: '依据 CDO 医疗器械定价模型,对立项、研发、样品打样、型式检验与 DHF 进行精准工时拆解。',
|
||||
markdownSkeleton: `# CDO商业报价单与工时测算清单 (项目模板副本)
|
||||
|
||||
- 报价项目:{{project_name}}
|
||||
- 目标产品:{{device_type}}
|
||||
- 报价日期:{{generated_at}}
|
||||
- 结算货币:人民币 (RMB)
|
||||
|
||||
## 1. 全阶段 WBS 研发工时与预算分解表
|
||||
|
||||
| 阶段代码 | 里程碑任务分解 | 负责工程角色 | 预算工时 (h) | 预算人工 (万元) | 核心受控交付物 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| V0.1 | 需求摸底、临床痛点与分类界定 | 法规专家 + 产品经理 | 80 | 4.20 | 《用户初始需求收集表》 |
|
||||
| V0.5 | 方案设计、合规初评与专利检索 | 光学/架构师 + 注册专家 | 280 | 15.60 | 《注册可行性与设计规格书》 |
|
||||
| V1.0 | 原型试制、型检跟进与全套DHF | 结构技师 + 验证工程师 | 380 | 18.00 | 《功能性工程样机与DHF文卷》 |
|
||||
| **合计** | **全生命周期核心工作包** | **协同工程团队** | **740 h** | **37.80 万元** | **全套医疗器械申报技术卷宗** |
|
||||
|
||||
## 2. 商业报价核算模型
|
||||
$$
|
||||
Total_{Price} = (Cost_{Labor} + Cost_{Material} + Cost_{Testing}) \\times (1 + Margin_{Risk})
|
||||
$$
|
||||
按标准模型测算,预估总报价区间为 48.00 ~ 65.00 万元(待正式锁定)。
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
export function createProjectTemplateDeliverables(
|
||||
params: TemplateFactoryParams
|
||||
): TemplateFactoryResult {
|
||||
const deliverables: DeliverableItem[] = [];
|
||||
const docASTMap: Record<string, DocumentAST> = {};
|
||||
const nowStr = new Date().toISOString().replace('T', ' ').substring(0, 16);
|
||||
const customerName = params.customerName || '委托开发客户企业';
|
||||
const author = params.authorName || params.creatorName || '编制工程师';
|
||||
const reviewer = params.reviewerName || '质量负责人';
|
||||
const approver = params.approverName || '技术委员会总监';
|
||||
|
||||
STANDARD_WORKFLOW_TEMPLATES.forEach((tmpl) => {
|
||||
const docId = `doc-p${params.projectId}-${tmpl.taskKey}`;
|
||||
|
||||
// 1. 变量宏替换
|
||||
let md = tmpl.markdownSkeleton;
|
||||
md = md.replaceAll('{{project_name}}', params.projectName);
|
||||
md = md.replaceAll('{{device_type}}', params.deviceType);
|
||||
md = md.replaceAll('{{customer_name}}', customerName);
|
||||
md = md.replaceAll('{{generated_at}}', nowStr);
|
||||
md = md.replaceAll('{{snapshot_id}}', `SNAP-INIT-${params.projectId}`);
|
||||
|
||||
// 2. 实例化 DeliverableItem
|
||||
const item: DeliverableItem = {
|
||||
docId,
|
||||
projectId: params.projectId,
|
||||
taskKey: tmpl.taskKey,
|
||||
title: tmpl.title,
|
||||
stage: tmpl.stage,
|
||||
category: 'workflow',
|
||||
creatorId: params.creatorId,
|
||||
creatorName: params.creatorName,
|
||||
isFixedWorkflow: true,
|
||||
isTemplateOnly: true,
|
||||
lifecycleState: 'template',
|
||||
isClaimed: false,
|
||||
author,
|
||||
reviewer,
|
||||
approver,
|
||||
updatedAt: '刚刚',
|
||||
summary: tmpl.summary,
|
||||
};
|
||||
deliverables.push(item);
|
||||
|
||||
// 3. 解析为块状 AST 并缓存
|
||||
const ast = markdownToBlockAST(md, {
|
||||
docId,
|
||||
title: tmpl.title,
|
||||
taskKey: tmpl.taskKey,
|
||||
version: 'V0.1 (模板)',
|
||||
category: 'workflow',
|
||||
creatorId: params.creatorId,
|
||||
creatorName: params.creatorName,
|
||||
lifecycleState: 'template',
|
||||
isClaimed: false,
|
||||
author,
|
||||
reviewer,
|
||||
approver,
|
||||
});
|
||||
docASTMap[docId] = ast;
|
||||
});
|
||||
|
||||
return { deliverables, docASTMap };
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 运行测试验证通过**
|
||||
|
||||
Run: `cd web && npx tsx --test src/engine/templateFactory.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 5: 提交代码**
|
||||
|
||||
```bash
|
||||
git add web/src/engine/templateFactory.ts web/src/engine/templateFactory.test.ts
|
||||
git commit -m "feat(engine): add project template cloning factory and unit tests"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 扩展抽屉与标签栏模板徽标 (`DeckDrawer.tsx` & `SlimTabBar.tsx`)
|
||||
|
||||
**Files:**
|
||||
- Modify: `web/src/components/doc/DeckDrawer.tsx:70-130`
|
||||
- Modify: `web/src/components/doc/SlimTabBar.tsx:40-90`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `DeliverableItem.isTemplateOnly`, `DeliverableItem.lifecycleState`
|
||||
- Produces: 带有 `[ 📄 初始模板 ]` / `[ 待填报 ]` 徽标与阶段标示的抽屉卡片和 Tab 标签
|
||||
|
||||
- [ ] **Step 1: 编写组件徽标渲染测试**
|
||||
|
||||
创建 `web/src/components/doc/templateBadge.test.ts`:
|
||||
|
||||
```typescript
|
||||
import test from 'node-test';
|
||||
import assert from 'node:assert';
|
||||
import { DeliverableItem } from '../../types/workbench';
|
||||
|
||||
test('isTemplateOnly 交付物应正确识别为初始模板展示态', () => {
|
||||
const item: DeliverableItem = {
|
||||
docId: 'doc-p101-intake',
|
||||
projectId: 101,
|
||||
taskKey: 'intake',
|
||||
title: '用户初始需求收集表',
|
||||
stage: 'V0.1',
|
||||
category: 'workflow',
|
||||
creatorId: 1,
|
||||
creatorName: '张工',
|
||||
isFixedWorkflow: true,
|
||||
isTemplateOnly: true,
|
||||
lifecycleState: 'template',
|
||||
isClaimed: false,
|
||||
author: '张工',
|
||||
reviewer: '李主管',
|
||||
approver: '王总监',
|
||||
updatedAt: '刚刚',
|
||||
};
|
||||
|
||||
assert.strictEqual(item.isTemplateOnly, true);
|
||||
// 当 isTemplateOnly 为 true 时,标签文本应为 📄 初始模板 或 模板待填报
|
||||
const badgeLabel = item.isTemplateOnly ? '📄 初始模板' : item.lifecycleState;
|
||||
assert.strictEqual(badgeLabel, '📄 初始模板');
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试验证**
|
||||
|
||||
Run: `cd web && npx tsx --test src/components/doc/templateBadge.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 3: 修改 `DeckDrawer.tsx` 与 `SlimTabBar.tsx` 徽标展现**
|
||||
|
||||
在 `web/src/components/doc/DeckDrawer.tsx` 中:
|
||||
为 `doc.isTemplateOnly` 添加徽标样式:
|
||||
```tsx
|
||||
{doc.isTemplateOnly ? (
|
||||
<span
|
||||
className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium border"
|
||||
style={{
|
||||
backgroundColor: 'rgba(56, 189, 248, 0.12)',
|
||||
color: '#38bdf8',
|
||||
borderColor: 'rgba(56, 189, 248, 0.3)',
|
||||
}}
|
||||
title="该文档为项目标准预置模板副本,点击可全景查阅大纲与章节规范"
|
||||
>
|
||||
📄 初始模板
|
||||
</span>
|
||||
) : (
|
||||
/* 原有 lifecycleState 徽标逻辑 */
|
||||
<span className="...">...</span>
|
||||
)}
|
||||
```
|
||||
|
||||
在 `web/src/components/doc/SlimTabBar.tsx` 中:
|
||||
若当前激活文档或 Tab 对应的文档为 `isTemplateOnly`,在标题右侧增加 `[模板]` 纤细标记或浅蓝圆点。
|
||||
|
||||
- [ ] **Step 4: 运行全量测试确认无语法与破坏性错误**
|
||||
|
||||
Run: `cd web && npm run test`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 5: 提交代码**
|
||||
|
||||
```bash
|
||||
git add web/src/components/doc/DeckDrawer.tsx web/src/components/doc/SlimTabBar.tsx web/src/components/doc/templateBadge.test.ts
|
||||
git commit -m "feat(web): add template status badges to deck drawer and tab bar"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 联动项目隔离与即时克隆至工作台核心 (`App.tsx`)
|
||||
|
||||
**Files:**
|
||||
- Modify: `web/src/App.tsx:50-120, 240-300, 1050-1100`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `createProjectTemplateDeliverables` from `web/src/engine/templateFactory.ts`
|
||||
- Produces: 项目创建时自动克隆并载入 7 份副本,交付物按 `activeSession.projectId` 严格隔离
|
||||
|
||||
- [ ] **Step 1: 检查并更新预置数据中的 `projectId` 绑定**
|
||||
|
||||
在 `web/src/App.tsx` 中:
|
||||
1. 为既有的 `INITIAL_DELIVERABLES`(即 `doc-001` ~ `doc-004` 等)添加 `projectId: 1` 和 `isTemplateOnly: false`;
|
||||
2. 为 `sess-002` (便携式超声探头,`projectId: 2`) 通过 `createProjectTemplateDeliverables` 预置属于项目 2 的全套 7 份模板副本;
|
||||
3. 将两者合并为初始 `deliverables` 与初始 `docCache`。
|
||||
|
||||
- [ ] **Step 2: 改造 `visibleDeliverables` 过滤逻辑**
|
||||
|
||||
在 `web/src/App.tsx` 中:
|
||||
```typescript
|
||||
// 1. 获取当前激活会话的项目ID
|
||||
const currentProjectId = useMemo(() => {
|
||||
const activeSess = sessions.find((s) => s.sessionId === activeSessionId);
|
||||
return activeSess ? activeSess.projectId : 1;
|
||||
}, [sessions, activeSessionId]);
|
||||
|
||||
// 2. 严格按 projectId 隔离交付物
|
||||
const currentProjectDeliverables = useMemo(() => {
|
||||
return deliverables.filter((doc) => doc.projectId === currentProjectId);
|
||||
}, [deliverables, currentProjectId]);
|
||||
|
||||
// 3. 再按三层隐私权限过滤 (工作流/共享全员可见,私密仅创建者可见)
|
||||
const visibleDeliverables = useMemo(() => {
|
||||
return currentProjectDeliverables.filter((doc) => {
|
||||
if (doc.category === 'workflow' || doc.category === 'shared') return true;
|
||||
return doc.creatorId === currentUser.id;
|
||||
});
|
||||
}, [currentProjectDeliverables, currentUser.id]);
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 改造 `onNewSession` 新建项目动作**
|
||||
|
||||
在 `web/src/App.tsx` 的 `onNewSession` 中:
|
||||
```typescript
|
||||
onNewSession={() => {
|
||||
const newSessId = 'sess-' + Date.now();
|
||||
const newProjId = Date.now();
|
||||
const newProjectTitle = '新建医疗器械研发项目';
|
||||
const newDeviceType = '通用医疗器械';
|
||||
|
||||
const newSess: ProjectSession = {
|
||||
sessionId: newSessId,
|
||||
projectId: newProjId,
|
||||
title: newProjectTitle,
|
||||
deviceType: newDeviceType,
|
||||
stage: 'V0.1',
|
||||
stageLabel: 'V0.1 需求摸底',
|
||||
messageCount: 1,
|
||||
updatedAt: '刚刚',
|
||||
};
|
||||
|
||||
const initMsg: ChatMessage = {
|
||||
id: 'm-init-' + Date.now(),
|
||||
sessionId: newSessId,
|
||||
role: 'system',
|
||||
content: '水木东方协同参谋小队已就位,当前新项目进入【V0.1 需求摸底与产品定义】阶段。全套研发工作流与阶段文档模板已克隆就绪,请在右侧查阅或在此输入您的研发构想。',
|
||||
timestamp: new Date().toTimeString().substring(0, 5),
|
||||
};
|
||||
|
||||
// 即时克隆全生命周期 7 份受控模板副本
|
||||
const { deliverables: clonedDocs, docASTMap: clonedAsts } = createProjectTemplateDeliverables({
|
||||
projectId: newProjId,
|
||||
projectName: newProjectTitle,
|
||||
deviceType: newDeviceType,
|
||||
creatorId: currentUser.id,
|
||||
creatorName: currentUser.name,
|
||||
});
|
||||
|
||||
setSessions((prev) => [newSess, ...prev]);
|
||||
setMessages((prev) => [...prev, initMsg]);
|
||||
setDeliverables((prev) => [...prev, ...clonedDocs]);
|
||||
setDocCache((prev) => ({ ...prev, ...clonedAsts }));
|
||||
|
||||
// 核心交互:激活新会话,并将右侧首个激活文档无缝切至该项目的《用户初始需求收集表》模板
|
||||
setActiveSessionId(newSessId);
|
||||
setActiveDocId(clonedDocs[0].docId);
|
||||
setActiveCategory('workflow');
|
||||
}}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 改造智能体填报逻辑 (`handleCardAction` 等)**
|
||||
|
||||
当用户在会话中推进任务(如采纳卡片建议或运行任务)时:
|
||||
寻址 `targetDocId = doc-p${currentProjectId}-${taskKey}` 或根据 `taskKey` 命中本项目的既有模板副本:
|
||||
- 更新该交付物:`isTemplateOnly: false`,`lifecycleState: 'drafting'`;
|
||||
- 将生成的内容更新至 `docCache`;
|
||||
- 保持无缝平滑过渡。
|
||||
|
||||
- [ ] **Step 5: 运行全量测试**
|
||||
|
||||
Run: `cd web && npm run test`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 6: 提交代码**
|
||||
|
||||
```bash
|
||||
git add web/src/App.tsx
|
||||
git commit -m "feat(web): wire project isolation and template cloning on project creation"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 编写集成测试、验证构建并归档测试报告
|
||||
|
||||
**Files:**
|
||||
- Create: `web/src/engine/projectTemplateIntegration.test.ts`
|
||||
- Create: `.doc/test-reports/2026-09-08-project-init-template-cloning-test-report.md`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: 全量前后端集成流转
|
||||
- Produces: 测试报告归档与生产构建包
|
||||
|
||||
- [ ] **Step 1: 编写多项目隔离与模板全生命周期集成测试**
|
||||
|
||||
创建 `web/src/engine/projectTemplateIntegration.test.ts`:
|
||||
|
||||
```typescript
|
||||
import test from 'node-test';
|
||||
import assert from 'node:assert';
|
||||
import { createProjectTemplateDeliverables } from './templateFactory';
|
||||
import { DeliverableItem } from '../types/workbench';
|
||||
|
||||
test('集成验证:项目切换时交付物严格隔离,新建项目即刻包含 7 份模板副本', () => {
|
||||
// 模拟项目 1 交付物
|
||||
const proj1 = createProjectTemplateDeliverables({
|
||||
projectId: 1,
|
||||
projectName: '4K内窥镜系统',
|
||||
deviceType: '硬管内窥镜',
|
||||
creatorId: 1,
|
||||
creatorName: '张工',
|
||||
});
|
||||
|
||||
// 模拟新建项目 2
|
||||
const proj2 = createProjectTemplateDeliverables({
|
||||
projectId: 2,
|
||||
projectName: '超声诊断仪',
|
||||
deviceType: '掌超探头',
|
||||
creatorId: 1,
|
||||
creatorName: '张工',
|
||||
});
|
||||
|
||||
const allDeliverables = [...proj1.deliverables, ...proj2.deliverables];
|
||||
|
||||
// 1. 项目 1 视角过滤
|
||||
const p1Docs = allDeliverables.filter((d) => d.projectId === 1);
|
||||
assert.strictEqual(p1Docs.length, 7);
|
||||
assert.ok(p1Docs.every((d) => d.docId.includes('doc-p1-')));
|
||||
|
||||
// 2. 项目 2 视角过滤
|
||||
const p2Docs = allDeliverables.filter((d) => d.projectId === 2);
|
||||
assert.strictEqual(p2Docs.length, 7);
|
||||
assert.ok(p2Docs.every((d) => d.docId.includes('doc-p2-')));
|
||||
|
||||
// 3. 验证两个项目的文档 ID 无任何交集
|
||||
const p1Ids = new Set(p1Docs.map((d) => d.docId));
|
||||
const p2Ids = new Set(p2Docs.map((d) => d.docId));
|
||||
p1Ids.forEach((id) => assert.ok(!p2Ids.has(id)));
|
||||
|
||||
// 4. 模拟智能体填报:平滑解除模板标记
|
||||
const targetDoc = p2Docs.find((d) => d.taskKey === 'intake')!;
|
||||
assert.strictEqual(targetDoc.isTemplateOnly, true);
|
||||
assert.strictEqual(targetDoc.lifecycleState, 'template');
|
||||
|
||||
// 模拟填报演进
|
||||
const filledDoc: DeliverableItem = {
|
||||
...targetDoc,
|
||||
isTemplateOnly: false,
|
||||
lifecycleState: 'drafting',
|
||||
summary: '智能体参谋小队已依据输入完成第一版需求摸底梳理。',
|
||||
updatedAt: '2026-09-08 20:30',
|
||||
};
|
||||
|
||||
assert.strictEqual(filledDoc.isTemplateOnly, false);
|
||||
assert.strictEqual(filledDoc.lifecycleState, 'drafting');
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行集成测试与全量自动化测试套件**
|
||||
|
||||
Run: `cd web && npm run test`
|
||||
Expected: 所有的单元测试与集成测试(> 58 项)100% PASS。
|
||||
|
||||
- [ ] **Step 3: 验证前端生产打包**
|
||||
|
||||
Run: `cd web && npm run build`
|
||||
Expected: 构建成功,Vite 编译无任何 TypeScript 报错。
|
||||
|
||||
- [ ] **Step 4: 归档标准测试报告**
|
||||
|
||||
在 `.doc/test-reports/2026-09-08-project-init-template-cloning-test-report.md` 写入完整测试记录(包含背景、方案、预期、真实控制台取样输出与判定)。
|
||||
|
||||
- [ ] **Step 5: 提交代码与测试报告**
|
||||
|
||||
```bash
|
||||
git add web/src/engine/projectTemplateIntegration.test.ts .doc/test-reports/2026-09-08-project-init-template-cloning-test-report.md
|
||||
git commit -m "test(engine): add integration tests and archive test report for project template cloning"
|
||||
```
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user