Commit 48dc0d0a authored by David Yang's avatar David Yang

fix: bugs

parent 527e29e5
...@@ -102,10 +102,6 @@ AI 在编写 API 调用或数据转换逻辑时,必须遵守: ...@@ -102,10 +102,6 @@ AI 在编写 API 调用或数据转换逻辑时,必须遵守:
- 流程文档更新内容至少应包含:功能入口、关键交互步骤、状态变化、核心字段/筛选条件、异常处理或限制条件。 - 流程文档更新内容至少应包含:功能入口、关键交互步骤、状态变化、核心字段/筛选条件、异常处理或限制条件。
- 提交结果时,除了说明改动代码,也要简要说明同步更新了哪个流程文档、补充了哪部分逻辑。 - 提交结果时,除了说明改动代码,也要简要说明同步更新了哪个流程文档、补充了哪部分逻辑。
## 业务文档索引
- 项目管理模块 - 请阅读 `/src/pages/project_manager_xy/project-management.business-flow.md`
## 获取当前登录用户信息 ## 获取当前登录用户信息
可使用 src/hooks/user.jsx 中的 hook useUserInfo 数据结构如下 { "username": "yang_admin", "password_changed": true, "token_expires_at": null, "\_id": "69b12a47e92b1de77c94a9f5", "display_name": "杨康", "type": "admin", "avatar": null, "profile": {}, "groups": [ { "\_id": "649e5a55ad1c1038911baabd", "name": "default", "display_name": "默认用户组", "users": [ { "_id": "649e5a55ad1c1038911baac0", "username": "system", "display_name": "系统" } ], "settings": {} } ], "applications": [ { "name": "fund_audit", "title": "数智报告平台", "groups": [ "default" ], "roles": [ "成员" ], "permissions": [] }, { "name": "aml_tables", "title": "反洗钱数据表", "groups": [ "default" ], "roles": [ "管理" ], "permissions": [] } ] } 可使用 src/hooks/user.jsx 中的 hook useUserInfo 数据结构如下 { "username": "yang_admin", "password_changed": true, "token_expires_at": null, "\_id": "69b12a47e92b1de77c94a9f5", "display_name": "杨康", "type": "admin", "avatar": null, "profile": {}, "groups": [ { "\_id": "649e5a55ad1c1038911baabd", "name": "default", "display_name": "默认用户组", "users": [ { "_id": "649e5a55ad1c1038911baac0", "username": "system", "display_name": "系统" } ], "settings": {} } ], "applications": [ { "name": "fund_audit", "title": "数智报告平台", "groups": [ "default" ], "roles": [ "成员" ], "permissions": [] }, { "name": "aml_tables", "title": "反洗钱数据表", "groups": [ "default" ], "roles": [ "管理" ], "permissions": [] } ] }
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
"@ant-design/icons": "^6.3.2", "@ant-design/icons": "^6.3.2",
"@headlessui/react": "^2.2.10", "@headlessui/react": "^2.2.10",
"@heroicons/react": "^2.2.0", "@heroicons/react": "^2.2.0",
"@heroui-pro/react": "file:../project_manager_new/node_modules/@heroui-pro/react", "@heroui-pro/react": "1.0.0-beta.7",
"@heroui/react": "^3.2.2", "@heroui/react": "^3.2.2",
"@heroui/styles": "^3.2.2", "@heroui/styles": "^3.2.2",
"@internationalized/date": "^3.12.2", "@internationalized/date": "^3.12.2",
......
allowBuilds:
core-js: false
core-js-pure: false
es5-ext: false
esbuild: false
...@@ -88,6 +88,7 @@ import { ...@@ -88,6 +88,7 @@ import {
loadProblemRectificationListData, loadProblemRectificationListData,
loadProblemRectificationListResources, loadProblemRectificationListResources,
loadProblemRectificationViewCountMap, loadProblemRectificationViewCountMap,
mergeDepartmentRectificationItems,
normalizeDelayRequest, normalizeDelayRequest,
normalizeDelayRequestItems, normalizeDelayRequestItems,
normalizeDepartmentRectificationItems, normalizeDepartmentRectificationItems,
...@@ -1418,8 +1419,13 @@ export default function ProblemRectificationListPage() { ...@@ -1418,8 +1419,13 @@ export default function ProblemRectificationListPage() {
const progressIdEditor = []; const progressIdEditor = [];
if (isDepartmentFlow) { if (isDepartmentFlow) {
const sourceRecord = await loadRecord(selectedRecords[0].id);
if (sourceRecord?.metadata?.is_locked === true) {
throw new Error('当前问题整改记录已锁定,不允许推送,请先解锁。');
}
const departmentItems = buildDepartmentRectificationItems( const departmentItems = buildDepartmentRectificationItems(
selectedRecords[0].raw || selectedRecords[0], sourceRecord,
departmentAssignments, departmentAssignments,
currentUserInfo, currentUserInfo,
); );
...@@ -1442,19 +1448,34 @@ export default function ProblemRectificationListPage() { ...@@ -1442,19 +1448,34 @@ export default function ProblemRectificationListPage() {
progressIdEditor.push(item.progress_id_editor); progressIdEditor.push(item.progress_id_editor);
} }
const nextDepartmentItems = mergeDepartmentRectificationItems(
sourceRecord,
departmentItems,
);
await batchUpdateRecords({ await batchUpdateRecords({
subject: PROBLEM_RECTIFICATION_SUBJECT, subject: PROBLEM_RECTIFICATION_SUBJECT,
filter: { filter: {
_id: selectedRecords[0].id, _id: selectedRecords[0].id,
}, },
data: buildDepartmentParentBatchUpdateData(departmentItems), data: buildDepartmentParentBatchUpdateData(nextDepartmentItems),
}); });
const savedRecord = await loadRecord(selectedRecords[0].id); const savedRecord = await loadRecord(selectedRecords[0].id);
const savedItems = normalizeDepartmentRectificationItems(savedRecord); const savedItems = normalizeDepartmentRectificationItems(savedRecord);
if (savedItems.length !== departmentItems.length) { if (
savedItems.length !== nextDepartmentItems.length ||
departmentItems.some(
(item) =>
!savedItems.some(
(savedItem) =>
savedItem.item_id === item.item_id &&
savedItem.progress_id_editor === item.progress_id_editor,
),
)
) {
throw new Error( throw new Error(
`部门整改明细写入失败:预期 ${departmentItems.length} 条,后台实际返回 ${savedItems.length} 条。`, `部门整改明细写入失败:预期保留 ${nextDepartmentItems.length} 条,后台实际返回 ${savedItems.length} 条。`,
); );
} }
} else { } else {
......
...@@ -189,14 +189,14 @@ ...@@ -189,14 +189,14 @@
- 记录被推送问题的 `_id` 列表到 `restriction_value` - 记录被推送问题的 `_id` 列表到 `restriction_value`
- 将选中问题的 `metadata.fill_in_the_people` 批量更新为填报人 - 将选中问题的 `metadata.fill_in_the_people` 批量更新为填报人
- 给填报人发送内部通知 - 给填报人发送内部通知
- 创建 OA 待办,链接使用相对地址 `/problem_rectification_new/problem_rectification_flow_table?id=<message_record_name>`,不拼接浏览器当前域名 - 创建 OA 待办,链接使用当前应用基准路径拼出流程页地址,并在浏览器环境下补齐 `window.location.origin`
### 5.5 推送延期申请 ### 5.5 推送延期申请
- 每次只允许选择 1 条问题记录,按该问题责任部门生成部门延期申请配置行;已有 `department_rectification_items` 时优先沿用其部门明细 ID - 每次只允许选择 1 条问题记录,按该问题责任部门生成部门延期申请配置行;已有 `department_rectification_items` 时优先沿用其部门明细 ID
- 弹窗为每个保留的责任部门选择 1 个填写人和 1 个一级审批人,发起人为最终审批人 - 弹窗为每个保留的责任部门选择 1 个填写人和 1 个一级审批人,发起人为最终审批人
- 延期申请保存于 `metadata.problem_rectification_delay_request_items`,与 `department_rectification_items` 平级;历史 `metadata.problem_rectification_delay_request` 仅用于兼容旧问题级延期;每条问题最多保存 1 份延期申请流程 - 延期申请保存于 `metadata.problem_rectification_delay_request_items`,与 `department_rectification_items` 平级;历史 `metadata.problem_rectification_delay_request` 仅用于兼容旧问题级延期;每条问题最多保存 1 份延期申请流程
- 创建 `message_reference_id` 流程主单和 OA 填写待办;延期明细的 `flow_record_id` 必须使用流程主单 `_id`。创建接口未直接返回 `_id` 时,系统按返回的 `name` 回读流程主单后再写入;流程链接仍使用相对地址 `/problem_rectification_new/problem_rectification_flow_table?id=<message_record_name>`,不拼接浏览器当前域名 - 创建 `message_reference_id` 流程主单和 OA 填写待办;延期明细的 `flow_record_id` 必须使用流程主单 `_id`。创建接口未直接返回 `_id` 时,系统按返回的 `name` 回读流程主单后再写入;流程链接使用当前应用基准路径拼出表格处理页,并在浏览器环境下补齐 `window.location.origin`
- 延期申请明细写入问题主记录时使用 `/record/batch/update` 的 `metadata.problem_rectification_delay_request_items` 点路径更新,避免覆盖主记录其它业务字段 - 延期申请明细写入问题主记录时使用 `/record/batch/update` 的 `metadata.problem_rectification_delay_request_items` 点路径更新,避免覆盖主记录其它业务字段
- 历史 OA 待办若仍使用 `/self_inspection_form?id=<message_record_name>`,应用入口会保留原流程 ID 并重定向到上述表格处理页 - 历史 OA 待办若仍使用 `/self_inspection_form?id=<message_record_name>`,应用入口会保留原流程 ID 并重定向到上述表格处理页
- 已锁定、未选择流程人员或已有延期申请的问题不能发起 - 已锁定、未选择流程人员或已有延期申请的问题不能发起
...@@ -324,10 +324,11 @@ ...@@ -324,10 +324,11 @@
- `editor_status = "未完成"` - `editor_status = "未完成"`
- `auditor_status = "未完成"` - `auditor_status = "未完成"`
- `sender_status = "未完成"` - `sender_status = "未完成"`
6. 单条和多条推送均回写每个问题的 `metadata.department_rectification_items`;每个部门明细保存责任部门、填写人、审批人、整改字段、审批状态和待办 ID。已有明细时保留整改业务内容,只重置本次流程人员、状态和待办 6. 单条和多条推送均回写每个问题的 `metadata.department_rectification_items`;每个部门明细保存责任部门、填写人、审批人、整改字段、审批状态和待办 ID。已有明细时必须保留原明细记录和 `item_id`,在原明细行上更新本次流程人员、状态和待办,整改进度、整改情况、预计完成时间、证明材料、创建人和创建时间继续沿用原值
- 写入后逐条重新读取父问题并校验明细数量和 `item_id`;后台未返回预期明细时,本次推送显示失败,不展示成功提示 - 再次推送时,只更新本次弹窗中保留并参与推送的部门明细;未参与本次推送的原明细行保持不变,不因删除弹窗部门配置而从父记录中移除
- 写入后逐条重新读取父问题并校验原明细是否保留、本次推送明细的 `item_id` 和待办 ID 是否回写;后台未返回预期明细时,本次推送显示失败,不展示成功提示
7. 多选简单推送按批次只创建 1 条填写 OA 待办,同一批次全部部门明细共享流程主单状态和待办 ID;明细业务内容仍按各自 `item_id` 独立保存 7. 多选简单推送按批次只创建 1 条填写 OA 待办,同一批次全部部门明细共享流程主单状态和待办 ID;明细业务内容仍按各自 `item_id` 独立保存
8. 发送内部通知和 OA 待办时,链接使用浏览器当前域名拼接完整地址 `<window.location.origin>/problem_rectification_new/problem_rectification_flow_table?id=<message_record_name>`;问题整改首次推送、流程内复核/驳回/最终审批和延期流程统一遵守该规则 8. 发送内部通知和 OA 待办时,链接使用当前应用基准路径拼出 `<appBasePath>/problem_rectification_flow_table?id=<message_record_name>`,并在浏览器环境下补齐 `window.location.origin`;问题整改首次推送、流程内复核 / 驳回 / 最终审批和延期流程统一遵守该规则
推荐新交互: 推荐新交互:
...@@ -339,7 +340,7 @@ ...@@ -339,7 +340,7 @@
### 7.3 填报人补录整改内容 ### 7.3 填报人补录整改内容
1. 填报人通过消息链接进入新表格处理页 `/problem_rectification_new/problem_rectification_flow_table` 1. 填报人通过消息链接进入新表格处理页 `<appBasePath>/problem_rectification_flow_table`
2. 页面展示本次流程绑定的所有部门明细行;单条和多条推送使用相同的明细字段与表格结构。参与人可查看全部行,填写人只能编辑自己负责的行,流程推送人可以编辑全部部门明细行。表格同时只读展示父问题的 `problem_brief`(问题简述)和 `related_issue_domain`(涉及业务领域);填写人和审批人字段保存用户 `_id`,表格展示用户姓名,姓名查询不到时才回退显示 ID 2. 页面展示本次流程绑定的所有部门明细行;单条和多条推送使用相同的明细字段与表格结构。参与人可查看全部行,填写人只能编辑自己负责的行,流程推送人可以编辑全部部门明细行。表格同时只读展示父问题的 `problem_brief`(问题简述)和 `related_issue_domain`(涉及业务领域);填写人和审批人字段保存用户 `_id`,表格展示用户姓名,姓名查询不到时才回退显示 ID
3. 填报人可在表格行内编辑: 3. 填报人可在表格行内编辑:
- `rectification_progress` - `rectification_progress`
...@@ -419,7 +420,7 @@ ...@@ -419,7 +420,7 @@
4. 问题主记录已有 `problem_rectification_delay_request` 或 `problem_rectification_delay_request_items` 时不能重复推送 4. 问题主记录已有 `problem_rectification_delay_request` 或 `problem_rectification_delay_request_items` 时不能重复推送
5. 确认推送后创建 `message_reference_id`,尝试写入 `flow_mode = "delay_department"`、问题 ID、部门延期申请 `item_id` 列表、填写人、一级审批人和推送人;创建响应未带 `_id` 时按 `name` 回读流程主单。若线上流程对象过滤了 `flow_mode` 或部门明细 ID,流程页先按 `restriction_value` 逐条读取关联问题详情,确保取得完整的延期嵌套字段,再按问题延期明细的 `flow_record_id._id` 与当前流程主单 `_id` 关联,或按二者共享的 `progress_id_editor` 自动识别为延期流程;这两类关联信息均被过滤时,仅对标题包含“延期申请”且关联问题确有延期明细的流程主单回退显示延期表单 5. 确认推送后创建 `message_reference_id`,尝试写入 `flow_mode = "delay_department"`、问题 ID、部门延期申请 `item_id` 列表、填写人、一级审批人和推送人;创建响应未带 `_id` 时按 `name` 回读流程主单。若线上流程对象过滤了 `flow_mode` 或部门明细 ID,流程页先按 `restriction_value` 逐条读取关联问题详情,确保取得完整的延期嵌套字段,再按问题延期明细的 `flow_record_id._id` 与当前流程主单 `_id` 关联,或按二者共享的 `progress_id_editor` 自动识别为延期流程;这两类关联信息均被过滤时,仅对标题包含“延期申请”且关联问题确有延期明细的流程主单回退显示延期表单
6. 在 `metadata.problem_rectification_delay_request_items` 初始化部门延期申请数组:`flow_record_id` 写入流程主单 `_id`;原预计完成时间优先取对应部门明细当前 `the_time_of_rectification`,没有部门明细时取主记录当前 `the_time_of_rectification`;填写、一级审批、最终审批状态分别初始化为“未完成 / 未审批 / 未审批” 6. 在 `metadata.problem_rectification_delay_request_items` 初始化部门延期申请数组:`flow_record_id` 写入流程主单 `_id`;原预计完成时间优先取对应部门明细当前 `the_time_of_rectification`,没有部门明细时取主记录当前 `the_time_of_rectification`;填写、一级审批、最终审批状态分别初始化为“未完成 / 未审批 / 未审批”
7. 系统为每个部门填写人创建 OA 待办并发送内部通知,链接使用相对地址 `/problem_rectification_new/problem_rectification_flow_table?id=<message_record_name>` 7. 系统为每个部门填写人创建 OA 待办并发送内部通知,链接使用当前应用基准路径拼出 `<appBasePath>/problem_rectification_flow_table?id=<message_record_name>`,并在浏览器环境下补齐 `window.location.origin`
8. 填写人进入流程页后按部门填写 `requested_due_date`(申请完成时间)和 `delay_reason`(延期事由);草稿可以暂存,提交时两项均必填 8. 填写人进入流程页后按部门填写 `requested_due_date`(申请完成时间)和 `delay_reason`(延期事由);草稿可以暂存,提交时两项均必填
#### 7.6.2 一级审批 #### 7.6.2 一级审批
...@@ -444,6 +445,46 @@ ...@@ -444,6 +445,46 @@
- 延期申请主记录写入成功后,前端以部门延期明细条数和 `flow_record_id` 回读校验,并以退避间隔最多等待约 8 秒;服务端读取时可能将 `flow_record_id` 填充为引用对象,前端统一提取其 `_id`。若后端未返回 `item_id`,按保存数组顺序匹配;重试后仍不一致才提示“延期申请写入失败”。若回读返回 `401` 或 `403`,提示登录状态失效且写入已完成,避免误报写入失败 - 延期申请主记录写入成功后,前端以部门延期明细条数和 `flow_record_id` 回读校验,并以退避间隔最多等待约 8 秒;服务端读取时可能将 `flow_record_id` 填充为引用对象,前端统一提取其 `_id`。若后端未返回 `item_id`,按保存数组顺序匹配;重试后仍不一致才提示“延期申请写入失败”。若回读返回 `401` 或 `403`,提示登录状态失效且写入已完成,避免误报写入失败
- OA 待办创建、业务记录写入或内部通知发送失败时展示错误,不显示成功提示 - OA 待办创建、业务记录写入或内部通知发送失败时展示错误,不显示成功提示
### 7.7 三类流程闭环总览
本模块的“推送整改”“推送延期申请”“一键归档”都从问题整改主列表发起,但三者写入对象和状态含义不同,后续开发需按以下口径区分。
#### 7.7.1 推送整改闭环
- 功能入口:`/problem_rectification` 主列表,用户勾选记录后点击顶部批量操作栏“推送问题整改表”
- 数据对象:主记录使用 `problem_rectification`;流程主单使用 `message_reference_id`;部门级处理事实来源使用 `metadata.department_rectification_items[]`
- 发起校验:至少选择 1 条问题;已锁定记录不能推送;单条问题按责任部门配置填写人和复核人,多条问题只允许 1 个填写人和 1 个复核人
- 发起写入:先创建 `message_reference_id`,写入 `restriction_value`、`reference_record_id`、`subject_name = "problem_rectification"`、`flow_mode`、`department_item_ids` 和三段流程状态;再通过 `/record/batch/update` 回写每条问题的部门整改明细。已推送过的问题再次推送时,不新建同部门的替代明细,不清空原整改内容,而是在原 `item_id` 对应行上更新本次流程人员、状态和待办 ID
- 待办通知:单条部门流程按部门明细创建填写 OA 待办;多条批量流程只创建 1 条填写 OA 待办并由整批问题共享;内部通知发送给本次填写人
- 填写阶段:填写人只编辑本人负责明细的整改进度、整改情况、预计完成时间和整改证明材料;推送人可保存本次流程关联的全部部门明细业务字段,但不能代替填写人送审
- 复核阶段:复核通过写入 `audit_status = "已审批"` 并创建最终审批待办;复核驳回写入 `audit_status = "已驳回"`、重置 `editor_status = "未完成"`,并重新创建填写待办
- 最终审批阶段:最终审批通过写入 `final_audit_status = "已审批"`;最终审批驳回写入 `final_audit_status = "已驳回"`、重置 `editor_status = "未完成"`,并重新通知填写人
- 父记录汇总:父记录的整改进度、审批状态、最终审批状态按部门明细中最落后的状态汇总,父字段只做兼容展示
- 异常限制:部门明细写入后必须回读校验原明细保留情况、本次推送明细 `item_id` 和待办 ID;锁定记录禁止保存、送审、复核和最终审批;待办、通知或业务写入任一失败时不展示成功提示
#### 7.7.2 延期申请闭环
- 功能入口:`/problem_rectification` 主列表,用户只勾选 1 条记录后点击“推送延期申请”
- 数据对象:主记录仍为 `problem_rectification`;新延期流程写入 `metadata.problem_rectification_delay_request_items[]`;历史 `metadata.problem_rectification_delay_request` 只兼容读取;流程主单仍为 `message_reference_id`
- 发起校验:每次只能选择 1 条问题;已锁定记录、已有历史问题级延期或已有部门级延期明细时不能再次发起;保留的每个责任部门必须选择 1 个填写人和 1 个一级审批人
- 发起写入:创建 `message_reference_id`,写入 `flow_mode = "delay_department"`、问题 ID、部门延期申请 `item_id`、填写人、一级审批人和最终审批人;若创建响应缺少 `_id`,按 `name` 回读后再写入延期明细的 `flow_record_id`
- 明细初始化:每个部门延期申请保存 `item_id`、`source_department_item_id`、责任部门、原预计完成时间、填写人、一级审批人、最终审批人、状态和填写待办 ID;原预计完成时间优先从对应部门整改明细读取,无部门明细时从父记录读取
- 填写阶段:填写人在流程页按部门填写 `requested_due_date` 和 `delay_reason`;草稿可暂存,提交申请时两项必填并写入 `editor_status = "已完成"`、`submitted_at`
- 一级审批阶段:通过写入 `auditor_status = "已审批"`、`auditor_decided_at` 并创建最终审批待办;驳回写入 `auditor_status = "已驳回"`、重置 `editor_status = "未完成"`,并重新创建填写待办
- 最终审批阶段:通过写入 `sender_status = "已审批"`、`sender_decided_at`,再把 `requested_due_date` 回写到对应 `department_rectification_items[].the_time_of_rectification`;无对应部门明细时才回退更新父记录 `the_time_of_rectification`
- 生效边界:最终审批通过前不修改原预计完成时间;延期流程不修改整改进度、整改情况、证明材料、整改复核状态或最终审批状态
- 异常限制:延期明细写入后按条数、`item_id` 和 `flow_record_id` 回读校验;若线上流程主单过滤 `flow_mode` 或 `department_item_ids`,流程页需从关联问题的延期明细反查,不得误判为普通整改流程
#### 7.7.3 归档闭环
- 功能入口:`/problem_rectification` 主列表,用户勾选一条或多条问题后点击“一键归档”
- 数据对象:归档明细写入 `problem_rectification_archive_records`;归档批次写入 `issue_rectification_archive`
- 发起校验:必须至少选择 1 条问题;归档动作生成快照,不改变原问题记录状态,也不自动锁定、删除或结束流程
- 明细转换:按 `problem_rectification_archive_records` 字段结构从主记录 `metadata` 映射归档字段;`responsible_department` 转为逗号拼接文本;`fill_in_the_people`、`review_people` 只保存用户 `_id[]`;`rectification_proof_materials` 作为文件对象保留;日期字段继续保存 ISO 8601 字符串
- 批次写入:归档明细导入成功后创建 1 条 `issue_rectification_archive` 批次记录,标题格式为“问题整改归档 YYYY-MM-DD”,并在 `metadata.reference_records` 中保存本批次所有归档明细 `_id`
- 归档查看:`/issue_rectification_archive` 展示归档批次列表;批次详情通过 `reference_records` 追溯归档明细数量和具体快照内容
- 异常限制:归档是一次性历史快照,原问题记录后续编辑不会反向更新归档明细;若归档明细导入失败,不创建批次;若批次创建失败,需要提示用户本次归档未完整闭环,避免出现孤立快照无人可查
--- ---
## 8. 状态流转约定 ## 8. 状态流转约定
...@@ -650,9 +691,10 @@ ...@@ -650,9 +691,10 @@
1. 在问题整改列表中勾选一条或多条记录 1. 在问题整改列表中勾选一条或多条记录
2. 点击“一键归档” 2. 点击“一键归档”
3. 将当前问题数据按归档结构转换后导入 `problem_rectification_archive_records` 3. 前端按 `problem_rectification_archive_records` 的字段结构转换当前问题数据,调用 `/record/import` 导入归档明细快照
4. 创建 `issue_rectification_archive` 记录,写入 `reference_records` 4. 归档明细导入成功后,创建 1 条 `issue_rectification_archive` 批次记录,写入 `metadata.reference_records = 归档明细 _id[]`
5. 归档列表页展示归档批次及批次下明细 5. 归档列表页展示归档批次,批次详情通过 `reference_records` 追溯批次下明细
6. 归档动作不修改原 `problem_rectification` 记录,不自动锁定,不自动删除,也不改变正在进行中的推送或延期流程
### 10.2 归档数据转换规则 ### 10.2 归档数据转换规则
...@@ -660,6 +702,7 @@ ...@@ -660,6 +702,7 @@
- `fill_in_the_people`、`review_people` 继续保存为用户 `_id[]` - `fill_in_the_people`、`review_people` 继续保存为用户 `_id[]`
- `rectification_proof_materials` 作为文件对象原样保留 - `rectification_proof_materials` 作为文件对象原样保留
- 归档是快照,后续原问题记录再变化,不应反向污染归档结果 - 归档是快照,后续原问题记录再变化,不应反向污染归档结果
- 归档明细导入失败时不创建归档批次;归档批次创建失败时应提示用户归档未完整完成,避免出现无法从批次入口追溯的孤立明细
--- ---
...@@ -886,6 +929,7 @@ const result = await queryUsers(0, 20, { ...@@ -886,6 +929,7 @@ const result = await queryUsers(0, 20, {
- 功能入口 - 功能入口
- 关键交互步骤 - 关键交互步骤
- 推送、填报、复核、最终审核流程 - 推送、填报、复核、最终审核流程
- 推送整改、延期申请和归档三类流程闭环
- `message_reference_id`(信息关联id)的当前字段协议与回流规则 - `message_reference_id`(信息关联id)的当前字段协议与回流规则
- 驾驶舱统计口径 - 驾驶舱统计口径
- 归档流程 - 归档流程
......
...@@ -423,27 +423,78 @@ export function buildDepartmentAssignmentsFromRecord(record) { ...@@ -423,27 +423,78 @@ export function buildDepartmentAssignmentsFromRecord(record) {
export function buildDepartmentRectificationItems(record, assignments = [], currentUser = null) { export function buildDepartmentRectificationItems(record, assignments = [], currentUser = null) {
const now = dayjs().toISOString(); const now = dayjs().toISOString();
const currentUserId = normalizeUserId(currentUser); const currentUserId = normalizeUserId(currentUser);
const existingItems = normalizeDepartmentRectificationItems(record);
const existingByItemId = new Map(
existingItems
.filter((item) => item.item_id)
.map((item) => [item.item_id, item]),
);
const existingByDepartment = new Map(
existingItems
.filter((item) => normalizeText(item.responsible_department))
.map((item) => [normalizeText(item.responsible_department), item]),
);
return assignments.map((assignment, index) => ({ return assignments.map((assignment, index) => {
item_id: assignment.item_id || makeDepartmentItemId(record?.raw || record, assignment.responsible_department, index), const department = normalizeText(assignment.responsible_department) || '未指定责任部门';
responsible_department: normalizeText(assignment.responsible_department) || '未指定责任部门', const itemId =
editor: normalizeUserId(assignment.editor), assignment.item_id ||
auditor: normalizeUserId(assignment.auditor), makeDepartmentItemId(record?.raw || record, department, index);
rectification_progress: '', const existingItem = existingByItemId.get(itemId) || existingByDepartment.get(department) || {};
rectification_situation: '',
the_time_of_rectification: null, return {
rectification_proof_materials: null, ...existingItem,
editor_status: EDITOR_STATUS_PENDING, item_id: existingItem.item_id || itemId,
audit_status: AUDIT_STATUS_PENDING, responsible_department: department,
final_audit_status: AUDIT_STATUS_PENDING, editor: normalizeUserId(assignment.editor),
progress_id_editor: '', auditor: normalizeUserId(assignment.auditor),
progress_id_auditor: '', rectification_progress: existingItem.rectification_progress || '',
progress_id_sender: '', rectification_situation: existingItem.rectification_situation || '',
created_by: currentUserId, the_time_of_rectification: existingItem.the_time_of_rectification || null,
created_at: now, rectification_proof_materials: existingItem.rectification_proof_materials || null,
updated_by: currentUserId, editor_status: EDITOR_STATUS_PENDING,
updated_at: now, audit_status: AUDIT_STATUS_PENDING,
})); final_audit_status: AUDIT_STATUS_PENDING,
progress_id_editor: '',
progress_id_auditor: '',
progress_id_sender: '',
created_by: existingItem.created_by || currentUserId,
created_at: existingItem.created_at || now,
updated_by: currentUserId,
updated_at: now,
};
});
}
export function mergeDepartmentRectificationItems(record, updatedItems = []) {
const existingItems = normalizeDepartmentRectificationItems(record);
if (existingItems.length === 0) {
return updatedItems.filter(Boolean);
}
const updatedByItemId = new Map(
updatedItems
.filter((item) => item?.item_id)
.map((item) => [item.item_id, item]),
);
const mergedItemIds = new Set();
const nextItems = existingItems.map((item) => {
const updatedItem = updatedByItemId.get(item.item_id);
if (!updatedItem) {
return item;
}
mergedItemIds.add(item.item_id);
return updatedItem;
});
updatedItems.forEach((item) => {
if (item?.item_id && !mergedItemIds.has(item.item_id)) {
nextItems.push(item);
}
});
return nextItems;
} }
export function buildDepartmentDelayRequestItems({ export function buildDepartmentDelayRequestItems({
......
...@@ -187,7 +187,7 @@ type Record = { ...@@ -187,7 +187,7 @@ type Record = {
- 新增驾驶舱扩展字段当前在线上 Subject 中大多仍为 `text` 类型,前端工作台按标准值引导录入,用于支持后续严重程度、问题分类、问题原因和整改质效分析 - 新增驾驶舱扩展字段当前在线上 Subject 中大多仍为 `text` 类型,前端工作台按标准值引导录入,用于支持后续严重程度、问题分类、问题原因和整改质效分析
- 其余 `select` 类型的可选值必须以线上 Subject 配置中的 `settings.options` 为准,禁止在页面内硬编码 - 其余 `select` 类型的可选值必须以线上 Subject 配置中的 `settings.options` 为准,禁止在页面内硬编码
- `problem_discovery_situation` 曾在历史文档和归档对象中出现,但本次主对象字段定义未包含;新页面只应在 Subject View 或线上字段配置存在时展示,不能作为 `problem_rectification` 固定字段依赖 - `problem_discovery_situation` 曾在历史文档和归档对象中出现,但本次主对象字段定义未包含;新页面只应在 Subject View 或线上字段配置存在时展示,不能作为 `problem_rectification` 固定字段依赖
- `department_rectification_items` 用于新转发流程的部门级整改明细。无论单条还是多条推送,每个问题都按责任部门生成明细;单条问题即使只有 1 个责任部门,也生成 1 条明细。转发后 `rectification_progress`、`rectification_situation`、`the_time_of_rectification`、`rectification_proof_materials`、`audit_status`、`final_audit_status` 以明细为事实来源,父字段仅做兼容汇总展示;整改进度、审批状态和最终审批状态按部门明细中最落后的状态汇总,例如“整改中”优先于“已整改”,“未审批”优先于“已审批” - `department_rectification_items` 用于新转发流程的部门级整改明细。无论单条还是多条推送,每个问题都按责任部门生成明细;单条问题即使只有 1 个责任部门,也生成 1 条明细。已推送过的问题再次推送时,必须保留原部门明细记录和 `item_id`,只在本次参与推送的原明细行上更新填写人、审批人、流程状态和待办 ID;未参与本次推送的原明细行保持不变,不因弹窗删除部门配置而从父记录移除。转发后 `rectification_progress`、`rectification_situation`、`the_time_of_rectification`、`rectification_proof_materials`、`audit_status`、`final_audit_status` 以明细为事实来源,父字段仅做兼容汇总展示;整改进度、审批状态和最终审批状态按部门明细中最落后的状态汇总,例如“整改中”优先于“已整改”,“未审批”优先于“已审批”
- `problem_rectification_delay_request` 为历史问题级单次延期申请嵌套对象,与 `department_rectification_items` 平级;新发起延期默认按部门写入 `problem_rectification_delay_request_items` - `problem_rectification_delay_request` 为历史问题级单次延期申请嵌套对象,与 `department_rectification_items` 平级;新发起延期默认按部门写入 `problem_rectification_delay_request_items`
- `problem_rectification_delay_request_items` 为部门级延期申请明细数组;每个责任部门 1 条延期申请,独立保存填写人、一级审批人、最终审批人、申请完成时间、延期事由、状态和待办 ID - `problem_rectification_delay_request_items` 为部门级延期申请明细数组;每个责任部门 1 条延期申请,独立保存填写人、一级审批人、最终审批人、申请完成时间、延期事由、状态和待办 ID
- `is_locked` 为问题整改主记录的状态锁定标记;仅整改进度汇总值为 `已整改` 的记录允许写入 `true`,锁定后所有业务字段和流程状态均不可修改,只有解锁操作可以将其写回 `false` - `is_locked` 为问题整改主记录的状态锁定标记;仅整改进度汇总值为 `已整改` 的记录允许写入 `true`,锁定后所有业务字段和流程状态均不可修改,只有解锁操作可以将其写回 `false`
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment