Establish .doc/{status,audit,remediation,archive} structure with README
maps, migrate 15 root audit/remediation docs, and add redirect stubs.
Co-authored-by: Cursor <cursoragent@cursor.com>
49 KiB
Chat Agent System 发布级账户、权限与工作流治理改进方案
1. 执行摘要
当前系统已经具备良好的业务原型基础:登录/注册、项目、项目成员表、受控文档三权流转、工作流运行、文档血缘、系统设置弹窗和用户设置弹窗均已有不同程度的实现;近期还补上了统一 API 登录中间件、Goose 数据库迁移、生产镜像中的前端构建,以及数据库不可用时的生产失败关闭。这些能力应保留并演进,而不是推倒重来。1 2 3
系统目前仍不适合直接作为多人生产协作产品发布。关键问题已经从“有没有功能”转变为“谁是权威、配置何时生效、运行状态能否被绕过”:
- 用户、密码、登录令牌和认证配置仍保存在 Go 进程内存中,重启即变化,令牌没有持久化、过期、轮换和全端撤销闭环。4
- 角色定义、权限矩阵、人员扩展资料、项目成员编辑与大量交付物状态仍由浏览器
localStorage保存;当前后端管理员判断仍硬编码为role-admin。5 6 - 工作流目录是 Go 代码中的三个固定定义,每个定义只有一个
stage_key/task_key,还不是管理员可编辑、可发布、多阶段、多文档的工作流模型。7 - 新项目默认立即激活
main;现有工作流控件会直接切换 active 指针,甚至在版本冲突后自动刷新重试,没有“配置—预览—二次确认—冻结—结束”的完整生命周期。8 9 - 项目成员角色当前混用了“创建人、编制人、审核人、批准人、查阅者”。编制/审核/批准本质上是每份文档的职责,不应作为一个人在整个项目中的单一角色。10
- 项目类型仍主要是自由文本
device_type,前端阶段键固定为V0.1/V0.5/V1.0,无法自然表达非医疗纯软件项目或其他生命周期。11
建议把目标系统定义为一个“受控的项目工作流平台”,核心由五层组成:
账户主体 ── 组织/系统角色 ── 项目成员关系
│
▼
不可变工作流版本 ──> 项目工作流实例 ──> 阶段实例 ──> 文档任务实例
│ │
├─ 对话参与策略 └─ 编制/审核/批准职责
├─ 文档可见策略
└─ 阶段推进门禁 + 审计 + 通知
最重要的产品决策是:
- 取消任意身份切换:生产包中删除
DevRoleSwitcher和“模拟账号切换”;登录身份只能来自服务端会话。测试身份切换只保留在不随产品发布的测试夹具中。 - 角色不等于文档职责:角色回答“这个人通常能做什么”;项目成员关系回答“这个人是否属于这个项目”;文档职责回答“这个人在这份文档中是编制、审核还是批准”。三者必须分表、分权限、分审计。
- 工作流采用版本化模板:管理员编辑的是草稿版本,发布后不可原地修改。项目激活时复制一个完整快照,后续修改全局模板不得改变正在运行的项目。
- 同一项目最多一个运行中的工作流:不能“切换”正在运行的工作流,只能在其完成或受控终止后选择新的工作流。
- 激活即冻结:阶段开关、文档任务开关、模板版本、文档三权与参与策略在激活前配置;激活后普通路径不可修改。确需变更时走“受控变更单”,而不是直接改字段。
- 阶段不自动推进:所有启用且必需的文档任务完成批准、无阻断性 stale/开放变更后,系统生成一次去重的“可推进”建议,主动通知项目创建者;由创建者确认后才原子进入下一阶段。
- 项目分类与合规范围分离:不要只设
is_medical=true/false。至少拆为“产品域”和“合规配置”。纯软件可能是非医疗软件,也可能是医疗器械软件;两者不能由“是不是软件”推断。
2. 需求重述与边界
2.1 三类设置
系统应形成三个边界清晰的设置入口:
| 入口 | 可进入者 | 管理对象 | 不应包含 |
|---|---|---|---|
| 用户设置 | 当前登录用户 | 本人资料、安全、通知、界面偏好、活动会话 | 角色提升、项目权限、他人账号 |
| 项目设置 | 项目创建者 | 项目成员、待激活工作流、阶段/文档开关、文档三权、激活/结束、通知 | 全局角色、全局模板原件、系统安全策略 |
| 系统设置 | 具备相应系统管理权限者 | 账户、角色、权限、工作流设计、模板库、项目类型/合规配置、审计和安全策略 | 以管理员身份直接替项目创建者完成日常业务决策 |
“系统管理员能看一切”不应成为默认业务规则。管理员需要处理平台配置,但项目私密资料和个人私密文档应遵守独立的数据访问策略。必要的运维介入应使用带理由、再认证、限时和全量审计的 break-glass 流程。
2.2 关键术语
| 术语 | 建议定义 |
|---|---|
| 账户 | 唯一自然人的登录主体,不可在前端任意切换 |
| 角色 | 一组可复用权限,可以是系统范围或项目范围 |
| 项目成员 | 用户与项目之间的关系,含状态和项目角色绑定 |
| 文档职责 | 某个用户对某个文档任务实例承担 author/reviewer/approver |
| 工作流定义 | 管理员维护的逻辑标识,如 medical_device_development |
| 工作流版本 | 已发布且不可变的完整流程快照,如 v3 |
| 项目工作流实例 | 某项目基于某个已发布版本创建的实际运行实例 |
| 阶段实例 | 项目工作流中的一个可推进单元 |
| 文档任务实例 | 阶段中的具体交付任务,连接模板、文档记录与三权职责 |
3. 当前实现评估
3.1 值得保留的基础
| 现有能力 | 评价 | 后续用法 |
|---|---|---|
| 统一 API 认证中间件 | 已覆盖非公开 /api 路由,方向正确 |
改接持久化会话,继续作为统一入口 |
project_members |
已有项目成员关系和 owner 回填 | 扩展角色绑定、成员管理 API 和成员状态 |
document_records |
已有状态、锁定、版本、三权和审计字段 | 关联 document_task_instance,逐步去除冗余姓名字段 |
| DocGov 状态机 | 已有编制、审核、批准、驳回基础 | 增加职责分离、电子签名/再认证和阶段门禁事件 |
project_workflows/workflow_runs |
已有项目工作流、运行和激活审计雏形 | 升级为定义版本 + 项目实例 + 阶段实例模型 |
| 文档血缘/stale | 已能表达上下游版本变化 | 纳入阶段完成条件和变更影响评估 |
| Goose 迁移与前端生产构建 | 近期已补齐基本链路 | 加迁移兼容测试、回滚预案和镜像验收 |
| 互动卡片/提醒类型 | 前端已有确认与提醒数据结构 | 用于“阶段可推进”和“工作流激活确认” |
3.2 必须纠正的结构性问题
第一,当前 RBAC 主要在前端执行。DEFAULT_ROLES 和 hasPermission 用于隐藏按钮、过滤文档,但服务端没有同构的角色/权限表和统一策略决策。浏览器过滤只能改善体验,不能构成安全边界。OWASP 建议默认拒绝,并在每个请求上重新验证用户对具体资源和动作的权限。12
第二,现有 ProjectWorkflowSwitcher 的语义是“切换当前焦点”,而目标语义是“运行中的流程互斥”。当前控件点击后直接调用激活 API,遇到乐观锁冲突还会自动用最新版本重试;对高影响业务操作,这会掩盖“另一个操作者刚刚改变了项目”的事实。冲突应该展示差异并要求用户重新确认,而不是静默覆盖。9
第三,现有项目工作流唯一约束是 (project_id, workflow_key),无法表示同一项目先后运行同一流程两次,也没有数据库约束保证“每个项目最多一个 active”。应保留历史实例,以 instance_no 或独立实例 ID 区分,并用部分唯一索引约束活动态。
第四,现有文档记录已支持三权,但项目成员 UI 又把成员角色设成 author/reviewer/approver,导致一个人加入项目时就被迫成为所有文档的某一职责。这会阻碍不同文档采用不同责任人,也无法表达 QA 同时参与多个文档但只批准其中一份。
4. 目标权限模型
4.1 RBAC + 项目关系 + 工作流属性
只用 RBAC 无法表达“BD 在商务阶段可对话,进入研发阶段后不可对话且不可看研发文档”。建议采用组合策略:
允许 = 已认证
AND 账户有效
AND 具备基础动作权限
AND 是该项目的有效成员(或合规的系统级例外)
AND 当前工作流/阶段允许其项目角色参与
AND 目标文档可见策略允许
AND 当前状态允许该动作
AND 未违反职责分离约束
任一条件未知或没有匹配规则时拒绝。RBAC 适合降低组织权限管理成本,但阶段、对象归属、文档责任人和状态属于属性/关系条件,需要与 ABAC/ReBAC 组合。NIST 的 RBAC 模型明确区分用户、角色、权限、操作和对象,也支持职责分离约束;NIST 对 RBAC 加属性的研究同样说明二者结合更适合动态条件。13 14
4.2 角色分层
| 层级 | 示例 | 作用域 | 建议 |
|---|---|---|---|
| 系统角色 | 系统管理员、用户管理员、工作流管理员、安全审计员 | 全系统 | 内置角色至少保留一个不可删除的超级管理员,但可编辑角色不得靠前端标志保护 |
| 业务/项目角色 | BD、产品经理、研发、测试、法规、QA、项目观察者 | 某项目 | 用户可在不同项目承担不同角色;可从组织默认角色复制后由项目创建者调整 |
| 文档职责 | 编制、审核、批准 | 单个文档任务实例 | 必须指派到具体用户,不等于项目角色 |
建议允许一个用户绑定多个角色,但权限计算不是简单无条件求并集。若两个角色触发静态或动态职责分离冲突,应阻止绑定或阻止同一会话中的冲突动作。例如,同一文档默认不得由同一人同时编制和批准;如小团队必须例外,工作流版本需显式允许,并要求理由和额外审计。
4.3 权限键建议
权限键使用稳定动作,不把具体角色名编码进代码:
| 权限域 | 建议权限 |
|---|---|
| 系统 | system.settings.view、system.audit.view、system.security.manage |
| 用户 | users.view、users.create、users.edit、users.disable、users.reset_password |
| 角色 | roles.view、roles.create、roles.edit、roles.delete、roles.assign |
| 工作流模板 | workflow_defs.view、workflow_defs.create、workflow_defs.edit_draft、workflow_defs.publish、workflow_defs.retire |
| 项目 | projects.create、projects.view、projects.edit、projects.members.manage、projects.workflow.configure、projects.workflow.activate、projects.workflow.terminate、projects.stage.advance |
| 工作流参与 | workflow.chat.send、workflow.chat.view、workflow.task.run、workflow.progress.view |
| 文档 | documents.view、documents.edit、documents.submit_review、documents.review、documents.approve、documents.export、documents.visibility.manage |
| 模板文件 | templates.upload、templates.download、templates.publish、templates.retire |
项目创建者的专属能力不应仅靠一个通用 permission。服务端应同时校验 projects.creator_user_id == principal.user_id。系统管理员若需替代操作,只能走单独的 break-glass API,不能复用普通项目设置接口。
4.4 阶段策略示例
| 角色 | 商务阶段对话 | 商务文档 | 研发阶段对话 | 研发文档 | 审批动作 |
|---|---|---|---|---|---|
| 项目创建者 | 是 | 是 | 是 | 是 | 仅被指派时;阶段推进始终由其确认 |
| BD | 是 | 按文档规则 | 否 | 否 | 仅商务文档且被指派时 |
| 产品经理 | 可选 | 按规则 | 是 | 需求类与被授权文档 | 被指派时 |
| 研发 | 否/可选 | 默认否 | 是 | 是 | 被指派时 |
| 测试 | 否 | 默认否 | 是 | 测试及依赖文档 | 被指派时 |
| 法规/QA | 可选 | 合规文档 | 是 | 合规与质量文档 | 被指派时 |
| 审计员 | 否 | 只读 | 否 | 只读 | 否 |
“否”必须在服务端同时作用于列表、详情、搜索、导出、附件下载、SSE、对话发送、RAG 上下文组装和 Agent 工具读取,不能只隐藏 UI。
5. 账户与身份治理
5.1 去掉任意身份切换
发布版本应:
- 删除
DevRoleSwitcher的产品入口; - 删除
UserProjectRightsModal中的快速模拟账号切换; - 删除前端本地认证 fallback、内置明文测试账户和
X-User-ID发送逻辑; - CI 检查生产 bundle 不包含种子密码、测试用户列表、
allowDevSwitch或模拟切换文案; - E2E 测试通过独立测试登录接口、数据库 fixture 或浏览器多上下文完成,不在产品 UI 留后门。
如果未来确实需要客服“代用户查看”,应作为独立的 impersonation/break-glass 功能:管理员再认证、填写工单与理由、限时、只读优先、页面持续红色提示、用户可见审计、禁止电子签名和批准。首个可发布版本建议不做。
5.2 持久化账户与会话
建议新增:
| 表 | 关键字段 |
|---|---|
users |
username、display_name、email、status、department、employee_id、locale、timezone、created_at、updated_at |
user_credentials |
user_id、password_hash、password_changed_at、failed_count、locked_until、mfa_state |
auth_sessions |
id、token_hash、user_id、created_at、last_seen_at、idle_expires_at、absolute_expires_at、revoked_at、ip_hash、user_agent |
password_reset_tokens |
token_hash、user_id、expires_at、used_at |
user_role_bindings |
user_id、role_id、scope_type、scope_id、valid_from、valid_until、assigned_by |
令牌仅以哈希形式持久化。浏览器优先使用 HttpOnly + Secure + SameSite Cookie;如果继续使用 Bearer token,也必须有短期访问令牌、刷新/轮换、撤销和 XSS 风险控制。NIST 已在 2025 年发布 SP 800-63B-4,替代旧版数字身份认证指南;账户安全设计应以新版为基线,而不是继续依赖永久内存 token。15
5.3 用户设置
用户设置建议分为四页:
- 个人资料:头像、姓名展示、邮箱、电话、语言、时区;员工号、部门和职位为组织受控字段,只读或需管理员审批。
- 安全:修改密码、MFA、已登录设备、注销其他设备、最近登录与安全事件。
- 通知:待审核、待批准、阶段可推进、文档 stale、任务失败的站内/邮件策略及免打扰时间。
- 偏好:主题、默认输出格式、沟通风格、自定义指令;明确哪些偏好会进入 Agent 上下文。
高风险变更如密码、MFA、主邮箱和电子批准应要求近期认证。账号被禁用、密码重置或角色收回时,相关会话立即撤销并使权限缓存失效。
6. 系统设置的信息架构
系统设置不宜继续放在单个 980px 模态框内。建议改为有独立路由和权限保护的管理中心:/admin/*。
| 页面 | 核心能力 |
|---|---|
| 概览 | 服务健康、待审批用户、失败工作流、存储/队列、近期安全事件 |
| 用户与账户 | 创建、编辑、启停、邀请、重置密码、撤销会话、批量导入、角色绑定 |
| 角色与权限 | 角色增删改、权限矩阵、作用域、职责分离、受影响用户预览 |
| 工作流设计器 | 草稿创建、阶段排序、文档任务、参与策略、门禁、校验、测试、发布、退役 |
| 模板库 | 上传、预览、变量、版本、哈希、扫描状态、被哪些工作流引用 |
| 项目类型与合规 | 产品域、合规配置、字段 schema、可用工作流、默认策略 |
| 通知与自动化 | 事件模板、渠道、提醒频率、升级规则、去重规则 |
| 审计日志 | 用户/权限/工作流/模板/项目变更查询与导出 |
| 安全与会话 | 注册策略、密码/MFA、会话时限、CORS、上传上限、保留期 |
角色删除必须先做影响分析。若仍有用户或工作流策略引用,默认不允许删除,只允许停用/归档或迁移绑定。内置超级管理员角色不可删除、不可清空关键权限;系统必须始终至少有一个有效超级管理员账户。
7. 工作流定义与版本管理
7.1 定义结构
WorkflowDefinition (稳定业务标识)
└─ WorkflowVersion (draft | published | retired,发布后不可变)
├─ StageDefinition [有序]
│ ├─ DocumentTaskDefinition [0..N]
│ │ ├─ TemplateVersion
│ │ ├─ default duties / eligible roles
│ │ └─ visibility / participation policy
│ └─ entry/exit gate
└─ workflow-level participation / completion policy
首期建议只支持线性阶段,拖拽排序即可。不要一开始引入任意 DAG、循环和复杂表达式;但表结构可以预留 transition_rules。线性模型已经满足“本阶段文档全部批准后,由创建者确认进入下一阶段”。
7.2 工作流版本状态
| 状态 | 可编辑 | 可用于新项目 | 可影响已激活项目 |
|---|---|---|---|
| draft | 是 | 否 | 否 |
| published | 否 | 是 | 否,项目只引用固定版本 |
| retired | 否 | 否 | 已运行实例继续按原快照执行 |
编辑已发布版本时实际执行“复制为新草稿”。发布前执行确定性校验:键唯一、阶段顺序连续、至少一个启用阶段、阶段至少有文档任务或显式人工门禁、模板均为可用版本、变量 schema 完整、权限策略有兜底、三权候选角色存在、无不可达节点、无角色死锁。
7.3 文档模板
模板不是直接覆盖文件,而是版本化资产:
- 上传后生成 CAS blob、SHA-256、MIME/扩展名、大小、上传人和扫描状态;
- 只允许业务所需格式,如 DOCX、XLSX、PPTX、Markdown;宏文档默认拒绝;
- 经过扩展名、MIME、文件签名、大小、解压后大小、恶意文件扫描/隔离后才能标记
ready; - 发布的工作流只能引用
ready模板版本; - 模板更新创建新版本,不改变历史项目实例;
- 变量采用白名单 schema,如
project.name、project.owner.name,未知变量阻止发布。
OWASP 对上传的建议包括扩展名白名单、不要信任客户端 Content-Type、应用生成存储名、限制大小、仅授权用户上传、放在 Web 根目录外并进行恶意文件扫描;模板上传应按此设计。16
8. 项目类型与合规配置
8.1 不使用单一“医疗/非医疗”开关
建议拆为两个维度:
| 维度 | 示例 | 用途 |
|---|---|---|
product_domain |
medical_device、non_medical_software、hardware_general、service |
决定项目字段、推荐工作流和术语 |
compliance_profile |
nmpa_device、eu_mdr、fda_qmsr、internal_qms、none |
决定法规步骤、必需文档、签名和审计强度 |
“纯软件”不能自动等同“无需法规”。非医疗 SaaS 可以选择 none;SaMD 则仍属于医疗器械域并选择对应市场合规配置。创建项目时以向导明确询问产品用途、目标市场和是否用于诊断/治疗等,由用户确认分类及理由,系统只提供建议。
FDA 的 QMSR 已于 2026 年 2 月 2 日生效,并将 ISO 13485:2016 纳入美国医疗器械质量体系框架;因此医疗项目的默认工作流与合规配置应可版本化,不应把旧法规口径写死在 Agent prompt 中。17 ISO 官方也说明 ISO 13485 面向医疗器械质量管理,并强调法规要求和风险管理;它不适合无差别施加给所有非医疗软件项目。18
8.2 首批项目类型建议
医疗器械研发建议阶段:商务摸底 → 需求与法规策划 → 设计开发 → 设计验证/确认 → 设计转移/发布。文档清单由所选市场、器械类型与组织 QMS 决定。
非医疗纯软件建议阶段:机会与范围 → 产品需求 → 架构与计划 → 实现 → 测试与验收 → 发布复盘。默认无法规检索步骤,可包含 SRS、架构设计、测试计划、发布清单、用户文档;若选择 internal_qms,仍可要求评审、批准和审计。
项目类型只负责推荐和约束可选工作流,不直接生成项目实例。这样未来可增加科研、咨询、硬件或组合产品,而无需修改 projects 表的大量专用字段。
9. 项目设置与激活冻结
9.1 项目设置页面
仅项目创建者可进入普通项目设置。页面建议分为:
- 基本信息:名称、编号、产品域、合规配置、目标市场;工作流激活后影响流程的字段冻结,修改需受控变更。
- 成员与项目角色:邀请、移出、项目角色绑定;不能在这里把成员设为全项目 author/reviewer/approver。
- 工作流配置:从与项目类型兼容的已发布版本中选择一个待激活工作流。
- 阶段与文档:激活前开关可选阶段/文档,查看依赖和影响;必需节点不可关闭。
- 责任分配:逐文档指派编制/审核/批准,实时验证角色资格与职责分离。
- 激活与结束:预览快照、风险提示、二次确认;运行中只展示状态和受控操作。
- 项目审计:成员、配置、激活、推进、终止和职责变更记录。
9.2 激活流程
新项目不应默认激活 main,而应处于 planning。推荐交互:
选择工作流版本
→ 配置阶段/文档开关
→ 指派每份启用文档的三权
→ 系统校验
→ 生成只读预览与 plan_hash
→ 用户二次确认
→ 单事务创建实例/快照/首阶段/文档任务/审计
激活确认弹窗应明确展示:工作流名称和版本、启用阶段数、启用/关闭文档数、三权缺口、激活后哪些配置会冻结、当前参与者权限将如何变化。用户勾选“我理解激活后不能直接修改”并再次点击确认。
服务端不能只信任 confirmed=true。建议 POST /activation-preview 返回短期 confirmation_token + plan_hash + expected_project_version;真正激活时必须三者一致。若期间成员、责任人、模板或项目配置变化,返回 409 和差异摘要,要求重新预览确认。
9.3 工作流实例状态机
planning ──activate──> active
│ ├─> awaiting_stage_confirmation ──advance──> active
│ ├─> completed
│ └─> termination_requested ──confirm──> terminated
└─delete draft
数据库增加部分唯一约束:一个项目在 active/awaiting_stage_confirmation/termination_requested 中最多有一个实例。新的工作流只有在前一个实例为 completed 或 terminated 后才能选择和激活。
正常“结束”与异常“终止”必须区分:
completed:所有启用阶段按门禁完成;terminated:创建者因范围取消等原因提前结束,必须填写原因,保留已有文档和历史;paused可作为后续版本能力,首期不建议增加以免模糊“是否仍占用 active 槽位”。
9.4 配置冻结与受控变更
激活后以下内容冻结:工作流版本、阶段和文档启用状态、模板版本、基线参与策略、文档三权。首期可严格禁止修改。
第二阶段再提供 workflow_change_requests:创建者提出变更 → 系统分析影响 → 相关批准人确认 → 创建新配置 revision → 使受影响下游文档 stale。即使通过变更单,也不应回写全局工作流版本。
10. 阶段、文档与主动推进
10.1 阶段状态机
pending → active → ready_to_advance → completed
│ │
└─ blocked <───┘
阶段进入 ready_to_advance 的确定性条件:
- 所有
enabled=true && required=true的文档任务均为approved; - 没有运行中的 Agent/task run;
- 没有阻断性开放批注、变更提案或 stale 文档;
- 阶段专用 exit gate 通过;
- 下一阶段至少有一个有效参与者,避免推进后无人能工作。
可选文档如果启用,也建议默认纳入阶段门禁;工作流管理员可以把某文档配置为 blocking=false,但发布时需要解释用途。阶段开关关闭时,其文档节点自动关闭并记录在项目快照中。启用阶段若关闭全部文档,必须有 manual_gate=true,否则激活校验失败。
10.2 文档任务状态
文档任务实例与文档正文分离:任务状态负责“是否启用、负责人是谁、是否完成”,document_records 继续负责正文版本和受控生命周期。
disabled(仅激活前可选)
enabled: template → drafting → reviewing → approving → approved
↑ │ │
└─ rejected┴────────────┘
approved --upstream changed--> stale(阻止阶段推进,直到评估或新版本批准)
三权规则:
- 编制人可编辑并提交审核,不能批准自己的版本;
- 审核人只能在
reviewing决定通过/驳回; - 批准人只能在审核通过的固定内容哈希上批准;
- 审核或批准动作记录 user、role binding、时间、内容哈希、意见和认证上下文;
- 责任人被禁用/移出项目时,任务进入
assignment_blocked,不能继续流转; - 更换责任人属于受控事件,不覆盖历史签署信息。
若系统承载受法规要求约束的电子记录/签名,应先做适用性评估。21 CFR 11.10 对闭合系统强调授权访问、按许可顺序执行、权限检查、记录保护和带时间戳审计轨迹;因此这里推荐的服务端状态机、不可覆盖审计和固定内容哈希不仅是产品体验要求,也是更稳妥的合规架构基础。19 这不等于仅靠实现这些功能就自动达到 Part 11 合规,仍需结合组织程序、验证、培训、留存和适用法规完成独立评估。FDA 指南也建议基于对产品质量、安全和记录完整性的风险评估确定控制强度。20
10.3 主动询问创建者
当状态首次满足推进条件时,由事务 Outbox 写入 stage.ready_to_advance 事件。通知服务创建一条去重的待办和互动卡片:
“研发输入阶段的 4/4 个必需文档已批准,没有阻断项。是否进入‘设计开发’阶段?推进后 BD 将失去本工作流对话与研发文档访问权限。”
卡片提供:查看完成清单、查看权限变化、暂不推进、确认推进。确认接口再次实时校验条件和 expected_stage_version;若已有文档被撤回、stale 或成员发生变化,拒绝推进并列出差异。
提醒去重键可为 (stage_instance_id, readiness_revision)。首次立即站内提醒,24 小时未处理再次提醒,超过组织配置阈值可升级给项目创建者本人或指定项目监督人,但绝不自动推进。
10.4 阶段切换时的权限变化
阶段推进和权限生效在同一数据库事务中完成。以 BD 为例:
- 事务提交前仍按商务阶段策略工作;
- 提交后新的对话发送、文档列表、详情、下载、搜索、引用和 Agent 上下文读取立即按研发阶段策略拒绝;
- 已打开页面下一次 API/SSE 校验时收到权限变化事件并转为只读/关闭;
- 是否还能查看历史商务阶段记录由工作流策略单独配置,不与研发文档权限混在一起;
- 默认建议允许 BD 查看自己参与的已结束商务阶段记录,但不能查看研发阶段消息和文档。
为此,聊天会话/消息必须绑定 workflow_instance_id 和 stage_instance_id。否则只有项目级 session,系统无法安全区分同一项目中商务与研发阶段的对话可见性。
11. 推荐数据模型
11.1 核心表
| 表 | 关键字段与说明 |
|---|---|
roles |
id、code、name、scope_type、status、is_system、version |
permissions |
key、resource_type、action、risk_level |
role_permissions |
role_id、permission_id、effect;首期只支持 allow,未匹配即 deny |
role_bindings |
user_id、role_id、scope_type、scope_id、有效期、assigned_by |
role_constraints |
SoD、互斥角色、作用域限制 |
workflow_definitions |
id、key、name、status、latest_published_version |
workflow_versions |
workflow_id、version、status、schema_version、published_by/at、definition_hash |
workflow_stage_defs |
workflow_version_id、key、name、position、optional、manual_gate、entry/exit_rule |
workflow_doc_task_defs |
stage_def_id、key、title、template_version_id、required、blocking、default_enabled |
workflow_access_policies |
target_type/id、project_role_id、action、effect |
template_assets |
logical key、name、status |
template_versions |
asset_id、version、blob_sha256、mime、size、scan_status、variable_schema |
project_types |
key、name、product_domain、field_schema、status |
compliance_profiles |
key、name、ruleset_version、status |
project_type_workflows |
project_type_id、workflow_id、compliance_profile_id、recommended/allowed |
project_workflow_instances |
project_id、workflow_version_id、instance_no、status、config_revision、created_by |
project_stage_instances |
workflow_instance_id、stage_def_id、position、enabled、status、version |
project_doc_task_instances |
stage_instance_id、doc_task_def_id、enabled、blocking、status、document_id |
document_duty_assignments |
doc_task_instance_id、duty、user_id、assigned_by、valid_from/to |
stage_advance_requests |
stage_instance_id、readiness_revision、status、requested_at、decided_by/at、reason |
workflow_change_requests |
instance_id、from_revision、patch、impact、status、decisions |
notifications |
user_id、event_id、type、status、dedupe_key、read_at、acted_at |
audit_events |
actor、action、resource、before/after hash、reason、request_id、occurred_at |
11.2 关键约束
CREATE UNIQUE INDEX uq_one_live_workflow_per_project
ON project_workflow_instances(project_id)
WHERE status IN ('active', 'awaiting_stage_confirmation', 'termination_requested');
CREATE UNIQUE INDEX uq_doc_duty_once
ON document_duty_assignments(doc_task_instance_id, duty)
WHERE valid_to IS NULL;
CREATE UNIQUE INDEX uq_stage_advance_revision
ON stage_advance_requests(stage_instance_id, readiness_revision);
此外应使用外键和 CHECK/触发器或领域事务确保:已发布版本不可更新;激活实例至少有一个启用阶段;一个实例最多一个 active stage;文档任务只能引用本实例阶段;批准动作绑定的内容哈希等于当前待批准版本。
11.3 现有表迁移映射
| 现有对象 | 迁移方式 |
|---|---|
projects.active_workflow_key |
过渡期作为缓存字段,最终由 live instance 派生;不再作为唯一事实源 |
project_workflows |
回填成 project_workflow_instances;已有 active 记录生成 legacy snapshot |
workflow_runs |
增加 instance/stage/task 外键,保留 run_id 与 input manifest |
document_records.task_key/stage |
回填关联 project_doc_task_instances;保留只读兼容字段一版 |
document_records.author/reviewer/approver |
回填 document_duty_assignments;姓名字段只用于历史展示,不再鉴权 |
project_members.project_role |
从单字符串迁移到 role_bindings;owner 单独保留创建人关系 |
前端 localStorage roles/projects/deliverables |
首次升级只读取并提示导入;服务端确认成功后停止作为权威源 |
12. API 与领域服务
12.1 建议领域服务
IdentityService:账户、凭据、会话、MFA、撤销;AuthorizationService:集中计算Decision{allowed, reason_code, policy_version};RoleService:角色版本、绑定、影响分析和职责分离;WorkflowDefinitionService:草稿、校验、发布、退役;ProjectWorkflowService:配置预览、激活、推进、完成、终止、受控变更;DocumentTaskService:任务实例、三权指派、DocGov 联动;NotificationService:Outbox 消费、去重、提醒与升级;AuditService:不可覆盖审计事件、查询、导出和留存。
12.2 主要 API
GET/POST/PATCH /api/v1/admin/users
POST /api/v1/admin/users/{id}/revoke-sessions
GET/POST/PATCH/DELETE /api/v1/admin/roles
GET/POST /api/v1/admin/workflows
POST /api/v1/admin/workflows/{id}/versions/{v}/validate
POST /api/v1/admin/workflows/{id}/versions/{v}/publish
POST /api/v1/admin/templates
GET/POST/PATCH /api/v1/admin/project-types
GET/PATCH /api/v1/me
POST /api/v1/me/password
GET/DELETE /api/v1/me/sessions
PATCH /api/v1/me/preferences
GET /api/v1/projects/{id}/settings
PUT /api/v1/projects/{id}/workflow-plan
POST /api/v1/projects/{id}/activation-preview
POST /api/v1/projects/{id}/workflow-instances/activate
POST /api/v1/workflow-instances/{id}/stages/{stage}/advance
POST /api/v1/workflow-instances/{id}/complete
POST /api/v1/workflow-instances/{id}/terminate-preview
POST /api/v1/workflow-instances/{id}/terminate
PUT /api/v1/document-tasks/{id}/duties
所有写操作带 Idempotency-Key 和 expected_version。错误使用稳定代码,如 WORKFLOW_ALREADY_ACTIVE、PLAN_CHANGED_RECONFIRM、STAGE_GATE_NOT_READY、ROLE_NOT_ELIGIBLE、SEPARATION_OF_DUTY_VIOLATION、WORKFLOW_ACCESS_DENIED。
12.3 服务端授权位置
路由中间件只解决“是否登录”;对象级授权必须在领域入口再做。所有项目、会话、消息、文档、附件、搜索、导出、任务和 SSE API 都调用同一个 AuthorizationService。不允许 handler 自行散落 if role == ...,也不允许相信请求体中的 user/role/project ownership。
13. UI 交互方案
13.1 主工作台
- 顶部展示固定登录用户,不提供身份下拉切换;
- 项目标题旁显示当前工作流、阶段和状态,不再展示可随点随切的三个按钮;
- 有权限且无运行中工作流时显示“配置工作流”;运行中显示“查看流程”;
- 对当前阶段无参与权的用户,输入框禁用并说明原因;不能仅靠禁用,发送接口也返回 403;
- 右栏只加载服务端已授权的文档,避免先取全量再在浏览器过滤;
- 创建者看到“待我确认”角标和阶段推进卡片。
13.2 工作流设计器
建议三栏布局:左侧阶段树,中间阶段/文档配置,右侧属性和权限策略。顶部固定展示 草稿/已发布/已退役、版本号、校验结果和发布按钮。发布操作弹出变更摘要:新增/删除阶段、模板变化、角色访问变化、与上一版本兼容性。
13.3 项目工作流配置器
采用向导而不是大表单:选择 → 裁剪阶段 → 裁剪文档 → 指派职责 → 权限影响 → 校验 → 激活确认。所有关闭项显示影响,例如“关闭《法规路径评估》会使 FDA QMSR 合规配置不完整”,对合规配置定义的必需项则直接禁止关闭。
14. 生产发布基线
本方案的业务功能不能脱离发布工程。近期代码已补上统一认证中间件、数据库迁移调用和前端多阶段镜像,但仍至少需要完成以下发布阻断项:
- 持久化认证:替换
MemoryStore和进程内 token;生产不创建固定密码种子用户。 - 服务端 RBAC/ABAC:把角色、权限、策略和项目成员修改迁入数据库/API,前端只消费
capabilities。 - 会话安全:过期、轮换、撤销、登录限速、失败锁定、MFA、敏感动作再认证。
- 上传安全:请求硬上限、配额、类型/签名检查、扫描、隔离、Web 根外存储。
- 审计完整性:用户/角色/策略/模板/工作流/阶段/签署/导出都进入只追加审计;日志访问也审计。OWASP 建议记录用户管理、权限变更、管理操作、导入导出和文件上传,并保护日志不被未授权修改或删除。21
- 网络安全:移除
Access-Control-Allow-Origin: *,使用允许域名;TLS、HSTS、CSP、frame/referrer 等由网关和应用共同设置。22 - 持久异步任务:task run/event、worker lease、重启恢复、Outbox;只能在文档与事件落库成功后发布终态。
- 机密管理:生产拒绝默认密码,密钥不进入镜像/前端/日志,使用 Secret 管理并轮换。
- 备份与恢复:数据库、CAS、审计、模板版本一致性备份;实际演练 RPO/RTO。
- 验证与发布证据:需求—设计—测试追溯、迁移报告、权限负例、性能、安全扫描、SBOM、部署/回滚记录。
15. 分阶段实施路线
Phase 0:决策冻结与安全止血(3–5 天)
- 冻结新的本地状态功能;明确服务端为唯一权威;
- 从生产 UI 删除任意身份切换和模拟入口;
- 定稿角色、权限、项目成员、文档职责术语;
- 关闭生产默认账号/密码、宽 CORS 和本地认证 fallback;
- 编制所有现有 API 的资源—动作—条件矩阵。
完成门禁:生产 bundle 不含切换入口/种子密码;匿名 API 401;跨项目、跨阶段、跨文档负例 403。
Phase 1:账户与授权权威化(2 周)
- 持久化 users/credentials/sessions/roles/permissions/bindings;
- 实现统一
AuthorizationService; - 把系统设置中的用户和角色操作改成真实 API;
- 建立项目成员 CRUD 与项目角色绑定;
- 前端不再从 localStorage 判定权限,后端返回 capabilities。
完成门禁:重启后账户、角色、会话策略仍在;禁用账户立即失效;每个资源动作均有允许/拒绝测试。
Phase 2:工作流设计器与模板库(2–3 周)
- 建立工作流定义/版本/阶段/文档/策略表;
- 管理端完成草稿、校验、发布、退役;
- 模板上传、扫描、版本和预览;
- 建立医疗器械与非医疗纯软件两套首批工作流;
- 旧静态 catalog 作为 bootstrap seed,运行时改读数据库缓存。
完成门禁:已发布版本不可变;旧项目不受新版本影响;非法/死锁工作流不能发布。
Phase 3:项目配置、冻结和阶段门禁(2–3 周)
- 项目设置独立页面;
- planning、activation preview、二次确认、单活动实例约束;
- 阶段/文档开关和三权分配;
- 文档批准驱动 readiness,创建者确认推进;
- 阶段级聊天、文档、搜索和 Agent 上下文权限。
完成门禁:运行中不能选择其他工作流;激活后不能直接改开关/职责;BD 进入研发阶段后无法通过任何接口访问研发内容。
Phase 4:可靠任务、审计与通知(2 周)
- 持久化任务和事件、重连/重启恢复;
- Outbox 通知、推进卡片、去重和升级;
- 审计日志防篡改、查询和导出;
- stale、责任人失效和配置变更成为正式阻断项。
Phase 5:生产验收与灰度(2 周)
- 空库/旧库升级双测,备份恢复演练;
- 完整权限矩阵、并发、幂等、故障注入、上传和渗透测试;
- 医疗/非医疗两套黄金项目 E2E;
- test 环境稳定运行一个发布周期,随后灰度 main。
按熟悉现有代码的 3–4 人团队估算,发布级闭环约 8–12 周。若只有 1–2 人,应先交付 Phase 0–1 的安全账户/RBAC,再做工作流设计器,不建议把所有能力压缩进一次大改。
16. 验收测试矩阵
16.1 身份与权限
- 用户不能在 UI、请求头、请求体或 localStorage 中更换自己的身份;
- 修改 URL 中 project/workflow/stage/doc/run ID 不会越权;
- 角色权限变更后旧会话在下一请求即使用新策略;
- 角色删除前能列出用户、项目和工作流策略引用;
- 至少一个超级管理员保护有效,普通管理员不能删除最后一个;
- 同一文档的 author 与 approver 默认不能为同一用户。
16.2 工作流定义
- 草稿可编辑,已发布版本任何更新均被拒绝;
- 模板新版本不改变已激活项目的哈希;
- 零阶段、重复键、无模板、未知角色、无可参与人员、不可达阶段无法发布;
- 退役版本不能用于新项目,但旧实例可继续。
16.3 项目实例
- 新项目初始无 active 工作流;
- 两个并发激活请求最多一个成功;
- 激活预览后配置变化会要求重新确认;
- active 工作流存在时,选择另一个返回 409;
- completed/terminated 后可新建下一实例;
- 激活后关闭阶段/文档、替换模板或直接换责任人均被拒绝。
16.4 阶段与文档
- 任一必需文档未批准、stale、有阻断提案或任务运行中时不能推进;
- 最后一份必需文档批准只产生一个推进待办;
- 创建者确认时条件已变化则不推进;
- 推进事务失败时阶段和权限均保持旧状态;
- BD 在研发阶段的对话、列表、详情、搜索、下载、导出、SSE 和 Agent 工具读取均为拒绝;
- 文档批准绑定内容哈希,批准后内容变化会产生新版本并重新走审核。
16.5 发布与恢复
- 应用/worker/数据库短暂故障不会产生两个终态或丢失推进通知;
- 数据库和 CAS 能从备份恢复到一致点;
- 审计可重建用户、权限、模板、工作流、阶段和签署变更顺序;
- 医疗项目启用所选合规配置的必需节点,非医疗软件不被强制加载法规步骤;
- 生产镜像、配置和数据库没有默认密码或测试身份入口。
17. 建议优先创建的研发 Epic
| Epic | 目标 | 依赖 | 优先级 |
|---|---|---|---|
| IAM-01 持久化账户与会话 | 替换内存用户/token,支持撤销与过期 | DB | P0 |
| AUTHZ-01 权限策略服务 | 服务端 RBAC + 项目/阶段/文档条件 | IAM-01 | P0 |
| UI-01 移除身份模拟与本地权威状态 | 生产 fail closed,权限来自服务端 | IAM-01/AUTHZ-01 | P0 |
| ROLE-01 角色与权限管理 API | 管理端真实持久化、影响分析、绑定 | AUTHZ-01 | P0 |
| WFDEF-01 工作流版本模型 | 定义、阶段、文档、策略、发布 | ROLE-01 | P1 |
| TMPL-01 安全模板库 | 上传、扫描、版本、变量 schema | WFDEF-01 | P1 |
| PTYPE-01 项目类型/合规配置 | 医疗与非医疗首批模型 | WFDEF-01 | P1 |
| WFINST-01 项目配置与激活冻结 | planning、preview、单活、快照 | WFDEF-01/TMPL-01 | P1 |
| DOC-01 文档任务与三权解耦 | 文档任务实例、职责分离、DocGov 接入 | WFINST-01 | P1 |
| STAGE-01 阶段门禁与推进 | readiness、创建者确认、权限原子变化 | DOC-01/AUTHZ-01 | P1 |
| NOTIFY-01 主动提醒 | Outbox、待办、互动卡片、去重升级 | STAGE-01 | P1 |
| RELEASE-01 发布验收 | CI、迁移、恢复、安全、灰度 | 全部 | P0/P1 |
18. 最终建议
这次改造不应被理解为“给现有设置弹窗再加几个 Tab”。真正需要建立的是一条不可绕过的权威链:固定登录主体 → 服务端角色与项目关系 → 已发布工作流版本 → 项目冻结快照 → 阶段与文档状态机 → 责任人签署 → 创建者推进 → 全程审计。
建议先完成账户和授权权威化,再建设工作流设计器。否则管理员即使能在页面上创建角色和流程,服务端仍无法可靠执行这些策略,功能越丰富,越权和状态不一致的风险越大。工作流首期采用线性阶段、单活动实例、激活后严格冻结,是在可用性、实现复杂度和受控性之间最稳妥的平衡。
对医疗器械项目,应通过合规配置逐步提高控制强度,并保留适用性评估、版本、审计和签署证据;对非医疗纯软件项目,应复用同一工作流引擎,但允许选择 compliance_profile=none,避免把法规流程硬编码进产品域。这样系统才能从当前的医疗器械业务原型,演进为可发布、可扩展、可审计的通用项目工作流平台。
Sources
-
仓库源码,
internal/server/auth_middleware.go,统一保护非公开 API;internal/db/migrate.go,Goose 迁移执行,访问于 2026-09-11。 ↩︎ -
仓库源码,
deploy/app/Dockerfile,包含 Node 前端构建与web/dist复制,访问于 2026-09-11。 ↩︎ -
仓库源码,
internal/bizdev/service.go,数据库连接、迁移及生产失败关闭逻辑,访问于 2026-09-11。 ↩︎ -
仓库源码,
internal/auth/store.go与internal/auth/service.go,内存用户存储与进程内 token,访问于 2026-09-11。 ↩︎ -
仓库源码,
web/src/App.tsx与web/src/data/storage.ts,角色、人员、项目和交付物的浏览器持久化,访问于 2026-09-11。 ↩︎ -
仓库源码,
internal/server/auth_handlers.go,管理员接口以role-admin硬编码判断;web/src/components/admin/AdminSettingsModal.tsx,角色权限编辑主要调用前端状态,访问于 2026-09-11。 ↩︎ -
仓库源码,
internal/projectworkflow/catalog.go,三个静态工作流及单一 stage/task 定义,访问于 2026-09-11。 ↩︎ -
仓库源码,
internal/projectworkflow/service.go与deploy/postgres/goose/00010_2026-09-11-project-workflows-and-lineage.sql,新项目默认main及当前切换模型,访问于 2026-09-11。 ↩︎ -
仓库源码,
web/src/components/workflow/ProjectWorkflowSwitcher.tsx,直接切换及冲突后自动重试行为,访问于 2026-09-11。 ↩︎ -
仓库源码,
web/src/types/workbench.ts与web/src/components/project/ProjectMembersModal.tsx,项目成员角色与文档职责混用,访问于 2026-09-11。 ↩︎ -
仓库源码,
web/src/types/workbench.ts与internal/projectworkflow/types.go,固定阶段枚举和自由文本设备类型,访问于 2026-09-11。 ↩︎ -
OWASP Cheat Sheet Series, “Authorization Cheat Sheet,” 访问于 2026-09-11。 ↩︎
-
NIST, “Role Based Access Control,” 含 INCITS 359 RBAC 模型说明,更新于 2026-03-04。 ↩︎
-
NIST, Kuhn, Coyne, Weil, “Adding Attributes to Role-Based Access Control,” 2010。 ↩︎
-
NIST, “SP 800-63B-4: Digital Identity Guidelines — Authentication and Authenticator Management,” 2025-07。 ↩︎
-
OWASP Cheat Sheet Series, “File Upload Cheat Sheet,” 访问于 2026-09-11。 ↩︎
-
U.S. FDA, “Quality Management System Regulation (QMSR),” 更新于 2026-02-02。 ↩︎
-
ISO, “ISO 13485:2016 — Medical devices — Quality management systems — Requirements for regulatory purposes,” 2016,2025 年复审确认。 ↩︎
-
Electronic Code of Federal Regulations, “21 CFR § 11.10 — Controls for closed systems,” 访问于 2026-09-11。 ↩︎
-
U.S. FDA, “Part 11, Electronic Records; Electronic Signatures — Scope and Application,” 2003-09,访问于 2026-09-11。 ↩︎
-
OWASP Cheat Sheet Series, “Logging Cheat Sheet,” 访问于 2026-09-11。 ↩︎
-
仓库源码,
internal/server/server.go,当前 CORS 允许任意来源,访问于 2026-09-11。 ↩︎