9.9 KiB
OA 审批实例评论(Web 先)设计文档
日期: 2026-09-20
状态: 已批准
关联: docs/superpowers/specs/2026-07-14-oa-approval-dingtalk-design.md(审批意见 ≠ 本能力)
交付策略: Web 管理端先实现 → 同一套 API 再推广到微信小程序 / 移动端
1. 目标与范围
1.1 目标
在 OA 审批实例详情中提供独立「讨论评论」能力:参与人可发表文字评论、引用流程表单中已有附件,并可撤回自己的评论。评论不改变审批结果、不推进流程。
1.2 V1 包含(Web)
| 能力 | 说明 |
|---|---|
| 发表评论 | 纯文字,必填,最长 1024 字 |
| 引用表单附件 | 从当前实例 form_data_json 中 upload 类字段勾选已有文件;存引用快照,不复制对象存储文件 |
| 撤回自己的评论 | 仅作者;撤回后时间线保留「已撤回」痕迹 |
| 评论列表 | 详情页独立评论区展示;含正文、引用附件、人、时间 |
| 状态门禁 | 仅实例 status=running 可发表 / 撤回;结束后只读 |
| 权限 | 与「能打开该实例详情」同一套可见性;租户隔离 |
1.3 V1 明确不做
- 一键通知审批人 / 抄送人 / 参与人
- @ 某人
- 快捷回复词、表情面板
- 评论区新上传图片 / 附件(仅引用表单已有附件)
- 楼中楼回复、编辑已发评论
- 将讨论评论混入审批进度链(
OaProgressChain)中的办理意见 - 本期不交付小程序 UI(接口按双端预留)
1.4 与「审批意见」的边界(SHALL)
| 审批评论(本能力) | 审批意见(现有) | |
|---|---|---|
| 存储 | oa_instance_comment |
oa_task.comment_text / oa_instance_event.comment_text |
| 时机 | 详情评论区随时(running) | 同意 / 拒绝 / 打回 / 加签 / 转交 |
| 是否推进流程 | 否 | 是 |
| UI | 独立「评论」区块 | 办理弹窗 / 进度链「意见」 |
禁止把讨论评论写入 OaTask.comment_text。
2. 方案决策
曾对比:独立表 / 复用 oa_instance_event / 塞入实例 JSON。
选定:独立表 oa_instance_comment。
理由:发表 / 撤回 / 列表语义清晰;Web 与小程序共用同一 API;不污染办理事件模型。
3. 数据模型
3.1 表 oa_instance_comment
字段顺序:id → 业务字段 → 公共字段(tenant_no … del_flag),与工程 database-conventions 一致。
| 字段 | 类型 | 说明 |
|---|---|---|
id |
VARCHAR(64) PK | UUID |
instance_id |
VARCHAR(64) NOT NULL | 所属 oa_instance.id |
content |
VARCHAR(1024) NOT NULL | 评论文本(撤回后仍保留原文,便于审计;前端对 revoked 不展示正文或仅展示占位) |
attachment_refs_json |
TEXT NULL | 引用快照 JSON 数组,见 §3.2 |
status |
VARCHAR(16) NOT NULL | normal | revoked,默认 normal |
revoked_time |
DATETIME NULL | 撤回时间 |
tenant_no |
… | 公共 |
order_num |
… | 公共 |
remark |
… | 公共 |
create_by |
… | 评论作者 user_id(与现网一致用 create_by) |
create_dept_id |
… | 公共 |
create_time |
… | 发表时间 |
update_by / update_time |
… | 撤回时更新 |
del_flag |
… | 默认 0;列表默认排除硬删;撤回用 status=revoked,不靠 del_flag 表示撤回 |
索引建议:(tenant_no, instance_id, create_time)。
SQL:新增迁移脚本 docs/sql/migrations/2026-09-20-oa-instance-comment.sql,并按仓库惯例评估是否同步全量 docs/sql/zkxw.sql。
3.2 attachment_refs_json 结构
数组元素:
{
"fieldKey": "protocolFiles",
"fieldLabel": "协议附件",
"url": "https://minio.example/.../xx.pdf",
"fileName": "协议.pdf"
}
- 发表时服务端根据实例
form_schema_json+form_data_json校验:每个url必须出现在该实例某一 upload 类字段的当前值中(规范化逻辑对齐oa_related_approval._extract_upload_urls)。 - 校验失败 → 400,中文错误(如「引用的附件不存在或不属于本表单」)。
- 快照在发表时刻固化;之后表单数据变更不影响已发表评论中的展示名 / url(若对象存储文件被删导致 404,属存储层问题,本期不处理)。
Upload 类字段类型(与现网一致):upload / fcUpload / image / oaAttachment。
4. API 设计
前缀:现有 /web/admin/oa-approval(与 backend/app/api/oa_approval.py 一致)。
权限:调用方须通过与 GET /instances/{instance_id} 相同的实例可见性校验;所有查询 / 写入强制 tenant_no = 当前用户租户。
4.1 可引用附件列表
GET /instances/{instance_id}/form-attachments
响应概念:
{
"items": [
{
"fieldKey": "protocolFiles",
"fieldLabel": "协议附件",
"files": [
{ "url": "...", "fileName": "协议.pdf" }
]
}
]
}
无附件时 items: []。
4.2 评论列表
GET /instances/{instance_id}/comments
- 返回该实例全部评论(含
revoked),按create_time升序。 - 每条含:
id、content、attachmentRefs、status、revokedTime、createBy、createByName(若详情已有用户名投影则对齐)、createTime、canRevoke(当前用户且 status=normal 且实例 running)。
另:GET /instances/{instance_id} 详情响应 SHALL 增加字段 comments(结构同列表项数组),Web 首屏少一次请求;独立列表接口供刷新 / 小程序复用。
4.3 发表评论
POST /instances/{instance_id}/comments
Body:
{
"content": "请补充第二页签字页",
"attachmentRefs": [
{ "fieldKey": "protocolFiles", "url": "...", "fileName": "协议.pdf" }
]
}
规则:
contenttrim 后非空,长度 ≤ 1024。attachmentRefs可选;有则服务端补全 / 校验fieldLabel,拒绝非法 url。- 仅
oa_instance.status == running;否则 400「仅审批进行中可评论」。 - 成功返回新建评论对象(同列表项)。
4.4 撤回评论
POST /instances/{instance_id}/comments/{comment_id}/revoke
规则:
- 评论存在、未删、属于该实例与租户。
- 仅
create_by == 当前用户。 - 仅
status == normal;已撤回 → 幂等成功或 400(实现选一种,建议 幂等成功)。 - 仅实例
running;结束后不可撤回。 - 置
status=revoked、revoked_time=now,更新update_by/update_time。 - 不物理删除,不清空
content/attachment_refs_json(审计保留;UI 对 revoked 不展示正文与附件)。
5. 后端实现要点
| 位置 | 说明 |
|---|---|
backend/app/models/models.py |
新增 OaInstanceComment |
backend/app/schemas/ |
CreateBody / 列表项 Pydantic(camelCase 对外) |
backend/app/crud/ 或 services/ |
列表、发表校验、撤回;附件枚举可放 oa_related_approval 旁或 oa_instance_comment 服务模块 |
backend/app/api/oa_approval.py |
注册上述 4 个路由;详情组装注入 comments |
| 字段一致性 | _row_to_dict 为可读字段真相源;CreateBody 与之对齐可写字段 |
6. Web 管理端 UI
6.1 页面
- 主入口:
web-admin/src/views/oa-approval/instances/detail.vue - 若存在详情弹窗复用同一详情数据源:
OaInstanceDetailDialog.vue等亦需挂载同一面板(与现网详情入口对齐,避免漏入口)。
6.2 组件
新建 web-admin/src/components/oa-approval/OaCommentPanel.vue(建议):
- 列表区:升序;normal 展示头像/姓名、时间、正文、引用附件芯片(预览/下载对齐现有附件交互);revoked 展示灰条「{姓名} 撤回了一条评论」+ 撤回时间。
- 操作:本人 + normal + 实例 running → 「撤回」按钮(需确认框)。
- 输入区(仅 running):
el-inputtextarea;「引用附件」打开多选(数据来自 form-attachments);「发送」。 - 按钮带图标(ui-conventions);遮罩无 blur;文案中文。
视觉:Fresh Mint;独立 section,不要并入 OaProgressChain。
2026-09-21 更新(钉钉对齐):展示已改为并入审批进度时间线,见
docs/superpowers/specs/2026-09-21-oa-comment-in-progress-timeline-design.md。OaCommentPanel仅保留底部输入;列表/撤回在OaProgressChain(kind=comment)。
6.3 API 模块
web-admin/src/api/oaApproval.ts 增加对应 fetch 方法。
7. 移动端 / 小程序(二期,本期仅约束)
- V1 不实现小程序页面。
- API 不得依赖仅 Web 才有的请求头或 Element 专用结构;响应字段稳定、camelCase。
- 二期在微信小程序审批详情复用:
form-attachments/commentsCRUD;UI 遵循docs/miniprogram-ui-spec-sanqiang.md。 - 现有
mobile-oa-h5若与小程序并行,二期可择一或双端同接,不在本设计展开。
8. 验收标准
- 有详情可见权限的用户,在 running 实例详情可发表非空文字评论。
- 可多选引用表单附件;非法 url 被拒绝;评论区可预览/下载引用文件。
- 作者可撤回自己的 normal 评论;他人不可撤;撤回后见「已撤回」占位,正文与附件对用户隐藏。
- 非 running(completed / rejected / cancelled / returned 等)不可发、不可撤,仍可读历史评论。
- 同意/拒绝意见与进度链行为与改前一致。
- 租户隔离:不可读写他租户实例评论。
9. 非目标与后续
- 评论通知、@、新上传附件、楼中楼 → 另开变更。
- 小程序 UI → V2。
- 是否把评论摘要打进打印 PDF → 不在 V1。
10. 实现顺序建议
- SQL + Model + Schema
- 服务:附件枚举、发表校验、撤回、列表
- API 路由 + 详情注入
comments - 前端 API +
OaCommentPanel+ 挂到详情页 - 手工验收(Web)
- (可选)
./scripts/update-graph.sh backend/frontend