15 KiB
工单需求规格说明
日期:2026-05-22
1. 背景
rdms-project 当前承载项目、产品、需求、执行、任务等核心交付对象。现有代码中产品需求、项目需求已经具备 sourceType / sourceBizId 来源字段,可以承接来自工单的需求派生关系;执行和任务也已经形成“项目需求 -> 执行 -> 任务”的后续交付链路。
本需求新增内部工单能力。工单作为独立业务对象存在,不复用需求、执行或任务主表。工单用于记录内部用户提交的诉求,经工单负责人受理后,可按归属类型派生产品需求或项目需求,并通过现有需求链路继续流转到执行、任务。
2. 目标
- 支持内部用户创建普通工单或父工单。
- 支持父工单逐步拆分子工单,父工单只汇总,不直接处理。
- 支持普通工单、子工单作为最小处理单位,由指定工单负责人受理、拒绝、处理和关闭。
- 支持工单单归属到一个产品或一个项目。
- 支持产品工单派生产品需求、项目工单派生项目需求。
- 支持有派生需求的工单在全部需求完成后自动关闭。
- 支持无派生需求的工单由工单负责人手动关闭。
3. 非目标
- 本期不做外部客户工单,不保留
sourceChannel、externalCustomerName、externalContact等外部来源字段。 - 本期不做工单编号
ticketNo。 - 工单不能直接派生执行或任务。
- 父工单不能受理、拒绝、派生需求或手动关闭。
- 本期不引入流程引擎,不做可配置审批流。
- 本期不自动判断工单是否涉及多个产品/项目,也不自动判断归属产品或项目;这些由录入人员人工选择。
4. 核心概念
4.1 工单形态
使用 ticketMode 表达工单形态:
| 值 | 含义 | 是否可处理 | 说明 |
|---|---|---|---|
single |
普通工单 | 是 | 单归属工单,不挂父工单 |
parent |
父工单 | 否 | 原始诉求汇总单,可持续拆分子工单 |
child |
子工单 | 是 | 挂在父工单下的最小处理单位 |
不使用 isParent。ticketMode 比布尔字段更准确,可以区分普通工单、父工单和子工单,避免在父工单尚未创建子工单时无法识别其形态。
4.2 归属类型
普通工单和子工单必须单归属:
belongType |
归属对象 | 可派生对象 | 后续链路 |
|---|---|---|---|
product |
一个产品 | 产品需求 | 产品需求 -> 指派项目 -> 项目需求 -> 执行 -> 任务 |
project |
一个项目 | 项目需求 | 项目需求 -> 执行 -> 任务 |
父工单不填写 belongType、productId、projectId。
5. 业务流程
5.1 总流程图
flowchart TD
A([开始]) --> B[录入人员创建工单]
B --> C{ticketMode}
C -->|parent| P1[父工单: splitting]
P1 --> P2[列表操作列拆分子工单]
P2 --> C1[创建子工单: pending_accept]
P1 --> P3{是否存在子工单且全部为终态}
P3 -->|否| P1
P3 -->|是| P4[父工单: closed]
P4 --> Z([结束])
C -->|single| S1[普通工单: pending_accept]
C1 --> S2[工单负责人待处理]
S1 --> S2
S2 --> D{是否受理}
D -->|否| R[工单: rejected]
R --> Z
D -->|是| E[工单: processing]
E --> F{是否派生需求}
F -->|否| G[负责人填写处理结论并手动关闭]
G --> H[工单: closed]
H --> I[触发父工单汇总检查]
I --> Z
F -->|是| J{归属类型}
J -->|product| K[派生一个或多个产品需求]
J -->|project| L[派生一个或多个项目需求]
K --> M[等待派生需求全部完成]
L --> M
M --> N{全部派生需求完成}
N -->|否| E
N -->|是| O[系统自动关闭工单: closed]
O --> I
5.2 父工单流程
- 录入人员创建父工单,状态为
splitting。 - 父工单只记录原始诉求和附件,不进入处理队列。
- 工单列表查询的操作列为父工单提供“拆分子工单”入口。
- 录入人员可以持续新增子工单。
- 父工单至少存在一个子工单,且所有子工单均进入终态后,系统自动关闭父工单。
- 父工单关闭后不再作为处理对象,但可继续作为历史汇总查看。
5.3 普通工单 / 子工单流程
- 创建后进入
pending_accept。 - 工单负责人判断是否受理。
- 不受理则进入
rejected,需要填写拒绝原因。 - 受理后进入
processing。 - 处理中可以派生需求,也可以在无派生需求时填写处理结论并手动关闭。
- 一旦存在派生需求,工单不能手动关闭,必须等待全部派生需求完成后自动关闭。
6. 状态模型
6.1 父工单状态
| 状态 | 含义 | 进入方式 | 退出方式 |
|---|---|---|---|
splitting |
拆分中 / 汇总中 | 创建父工单 | 所有子工单终态后自动关闭 |
closed |
已关闭 | 系统自动关闭 | 终态 |
父工单不允许进入 pending_accept、rejected、processing。
6.2 普通工单 / 子工单状态
| 状态 | 含义 | 进入方式 | 退出方式 |
|---|---|---|---|
pending_accept |
待受理 | 创建普通工单或子工单 | 受理或拒绝 |
rejected |
已拒绝 | 工单负责人拒绝 | 终态 |
processing |
处理中 | 工单负责人受理 | 手动关闭或自动关闭 |
closed |
已关闭 | 手动关闭或派生需求全部完成后自动关闭 | 终态 |
终态包括 rejected、closed。
6.3 自动关闭规则
- 普通工单 / 子工单存在派生需求时,只有全部派生需求完成后才自动关闭。
- 普通工单 / 子工单不存在派生需求时,允许工单负责人手动关闭,必须填写
closeResult。 - 父工单至少存在一个子工单,且所有子工单均为
rejected或closed后,自动关闭。 - 父工单不直接检查需求完成情况,只汇总子工单终态。
7. 数据模型
7.1 工单主表
建议表名:rdms_ticket
| 字段 | 类型建议 | 必填规则 | 说明 |
|---|---|---|---|
id |
bigint |
是 | 主键 |
parent_id |
bigint |
否 | 父工单 ID;child 必填 |
ticket_mode |
varchar(32) |
是 | single / parent / child |
title |
varchar(255) |
是 | 工单标题 |
description |
text |
否 | 工单描述,支持富文本 |
ticket_type |
varchar(32) |
是 | 工单类型,字典 |
priority |
varchar(32) |
否 | 优先级,建议复用需求优先级字典 |
status_code |
varchar(32) |
是 | 工单状态 |
submitter_id |
bigint |
是 | 提交人用户 ID |
submitter_nickname |
varchar(64) |
否 | 提交人昵称快照 |
owner_id |
bigint |
single / child 必填 |
工单负责人用户 ID |
owner_nickname |
varchar(64) |
否 | 工单负责人昵称快照 |
belong_type |
varchar(32) |
single / child 必填 |
product / project |
product_id |
bigint |
产品工单必填 | 归属产品 ID |
project_id |
bigint |
项目工单必填 | 归属项目 ID |
accept_time |
datetime |
否 | 受理时间 |
reject_reason |
varchar(500) |
拒绝时必填 | 拒绝原因 |
close_time |
datetime |
否 | 关闭时间 |
close_result |
varchar(1000) |
手动关闭时必填 | 处理结论 |
attachments |
json |
否 | 附件列表,沿用 AttachmentItem |
审计字段、逻辑删除字段复用现有 BaseDO 风格。
7.2 工单需求关联表
建议表名:rdms_ticket_requirement_link
| 字段 | 类型建议 | 必填规则 | 说明 |
|---|---|---|---|
id |
bigint |
是 | 主键 |
ticket_id |
bigint |
是 | 工单 ID |
target_type |
varchar(32) |
是 | product_requirement / project_requirement |
target_id |
bigint |
是 | 需求 ID |
不设置 relationType。本期关联表只表达“该工单派生了哪些需求”。
建议唯一约束:ticket_id + target_type + target_id。
8. 需求派生规则
8.1 产品工单派生产品需求
适用条件:
ticketMode为single或child。belongType = product。- 工单状态为
processing。
派生结果:
- 创建
ProductRequirementDO。 productId使用工单productId。sourceType = "work_order"。sourceBizId = ticketId。- 标题、描述、优先级、附件、提出人等字段可从工单带入,并允许派生表单二次编辑。
- 写入
rdms_ticket_requirement_link,targetType = product_requirement。
后续链路沿用现有产品需求分发到项目的能力。
8.2 项目工单派生项目需求
适用条件:
ticketMode为single或child。belongType = project。- 工单状态为
processing。
派生结果:
- 创建
ProjectRequirementDO。 projectId使用工单projectId。sourceType = "work_order"。sourceBizId = ticketId。- 标题、描述、优先级、附件、提出人等字段可从工单带入,并允许派生表单二次编辑。
- 写入
rdms_ticket_requirement_link,targetType = project_requirement。
后续执行和任务沿用现有项目需求、执行、任务链路。
8.3 完成判定
- 产品工单只检查由该工单派生的产品需求。
- 项目工单只检查由该工单派生的项目需求。
- 需求完成态应复用现有需求状态模型的终态配置,不在工单逻辑中写死具体状态码。
- 派生需求数量大于 0 且全部完成时,系统自动关闭对应工单。
9. 页面与待办
9.1 我的提交
展示当前用户提交的工单,包括:
- 父工单。
- 普通工单。
- 子工单。
建议支持按状态、工单类型、归属类型、归属对象、创建时间筛选。
9.2 我的待处理
展示当前用户负责的普通工单和子工单,不展示父工单。
筛选条件:
ownerId = 当前用户。ticketMode in (single, child)。statusCode in (pending_accept, processing)。
9.3 父工单列表操作
工单列表查询中,父工单行需要在操作列展示“拆分子工单”入口。该入口只对父工单提交人或具备工单管理权限的用户可见。
点击后进入新增子工单表单,表单需要携带父工单上下文,并要求录入人员填写子工单的归属类型、归属产品/项目、工单负责人、工单类型等处理字段。
9.4 父工单汇总展示
父工单汇总展示需要包含:
- 原始诉求信息。
- 子工单列表。
- 子工单归属产品/项目。
- 子工单负责人。
- 子工单状态。
- 子工单派生需求数量与完成数量。
- 父工单自动关闭结果。
10. 权限规则
- 创建工单走全域权限。
- 父工单新增子工单:父工单提交人或具备工单管理权限的用户可操作。
- 普通工单 / 子工单受理、拒绝、手动关闭:工单负责人或具备工单管理权限的用户可操作。
- 派生产品需求时,需要满足产品对象权限。
- 派生项目需求时,需要满足项目对象权限。
- 父工单不能执行受理、拒绝、派生需求、手动关闭动作。
11. 接口建议
接口路径建议落在 rdms-project 模块:
| 方法 | 路径 | 说明 |
|---|---|---|
POST |
/project/tickets |
创建普通工单或父工单 |
POST |
/project/tickets/{parentId}/children |
父工单新增子工单 |
GET |
/project/tickets/my-submitted/page |
我的提交 |
GET |
/project/tickets/my-pending/page |
我的待处理 |
GET |
/project/tickets/{id} |
工单详情 |
POST |
/project/tickets/{id}/accept |
受理 |
POST |
/project/tickets/{id}/reject |
拒绝 |
POST |
/project/tickets/{id}/close |
无派生需求时手动关闭 |
POST |
/project/tickets/{id}/derive-product-requirement |
派生产品需求 |
POST |
/project/tickets/{id}/derive-project-requirement |
派生项目需求 |
创建、更新类接口需要继续遵守仓库 HTTP 动词语义约定。部分动作使用语义化 POST 子动作接口,不引入 PATCH。
12. 校验规则
ticketMode = parent时,ownerId、belongType、productId、projectId必须为空。ticketMode = single时,ownerId、belongType必填,且productId/projectId按归属类型二选一。ticketMode = child时,parentId、ownerId、belongType必填,且父工单必须存在且ticketMode = parent。- 产品工单必须填写
productId,不能填写projectId。 - 项目工单必须填写
projectId,不能填写productId。 - 只有
pending_accept状态的普通工单 / 子工单可以受理或拒绝。 - 只有
processing状态的普通工单 / 子工单可以派生需求。 - 有派生需求的工单不能手动关闭。
- 无派生需求的
processing工单可以手动关闭,必须填写closeResult。 - 父工单不能手动关闭。
- 父工单至少有一个子工单,且全部子工单终态后才能自动关闭。
13. 错误处理
建议新增明确错误码覆盖以下场景:
- 工单不存在。
- 工单形态非法。
- 父工单不能处理。
- 子工单父级非法。
- 工单归属类型非法。
- 产品工单不能派生项目需求。
- 项目工单不能派生产品需求。
- 工单状态不允许当前动作。
- 有派生需求的工单不能手动关闭。
- 派生需求未全部完成,工单不能自动关闭。
14. 测试重点
- 创建父工单后状态为
splitting,且不能受理、拒绝、派生需求、手动关闭。 - 父工单可以持续新增多个子工单。
- 子工单必须单归属产品或项目。
- 普通工单 / 子工单创建后进入
pending_accept。 - 工单负责人拒绝后进入
rejected。 - 工单负责人受理后进入
processing。 - 无派生需求的
processing工单可手动关闭。 - 有派生需求的工单不能手动关闭。
- 产品工单只能派生产品需求。
- 项目工单只能派生项目需求。
- 派生需求全部完成后自动关闭工单。
- 所有子工单终态后自动关闭父工单。
15. 风险与约束
- 自动关闭依赖需求完成态判定,实施时必须和现有需求状态模型对齐。
- 产品需求分发到项目后的项目需求、执行、任务链路不属于工单直接职责,工单只追踪自己直接派生的需求。
- 父工单允许持续补子工单,会带来“父工单已关闭后是否允许继续补子单”的边界。本规格默认父工单关闭后不再补子单;如需重开父工单,需要单独设计重开动作。
- 现有前端已有
/ticket/my-submitted、/ticket/my-pending资源入口,后端接口落地时需要与前端路由和菜单权限同步。