Commit 3e89ebbc authored by David Yang's avatar David Yang

fix: 调整廉洁风险部门明细合并规则

parent 528ffedf
...@@ -699,6 +699,50 @@ function normalizeSnapshotValue(value, field) { ...@@ -699,6 +699,50 @@ function normalizeSnapshotValue(value, field) {
return value === undefined ? null : value; return value === undefined ? null : value;
} }
function hasFieldValue(item, fieldName) {
return Object.prototype.hasOwnProperty.call(item, fieldName)
&& item[fieldName] !== undefined
&& item[fieldName] !== null
&& item[fieldName] !== '';
}
function mergeTextValues(items, fieldName) {
return items
.map((item) => item[fieldName])
.filter((value) => value !== undefined && value !== null && value !== '')
.map((value) => String(value))
.join('\n');
}
function mergeReferenceValues(items, fieldName) {
return uniqueValues(items.flatMap((item) => normalizeIds(item[fieldName])));
}
function mergeDepartmentBusinessValue(items, field) {
const fieldName = field?.name;
const fieldType = String(field?.type || '').toLowerCase();
const valuedItems = items.filter((item) => hasFieldValue(item, fieldName));
if (valuedItems.length === 0) {
return field?.multiple ? [] : null;
}
if (['text', 'long_text', 'rich_text'].includes(fieldType)) {
return mergeTextValues(valuedItems, fieldName);
}
if (fieldType === 'reference') {
const mergedIds = mergeReferenceValues(valuedItems, fieldName);
return field?.multiple ? mergedIds : mergedIds[0] || null;
}
if (field?.multiple) {
return uniqueValues(valuedItems.flatMap((item) => toArray(item[fieldName])));
}
return normalizeSnapshotValue(valuedItems[valuedItems.length - 1][fieldName], field);
}
function buildDepartmentSnapshot(record, assignment, subject, currentUserId, now) { function buildDepartmentSnapshot(record, assignment, subject, currentUserId, now) {
const metadata = record?.metadata || {}; const metadata = record?.metadata || {};
const snapshot = departmentBusinessFields(subject).reduce((result, field) => { const snapshot = departmentBusinessFields(subject).reduce((result, field) => {
...@@ -810,17 +854,26 @@ function requireDepartmentRole(item, role, user) { ...@@ -810,17 +854,26 @@ function requireDepartmentRole(item, role, user) {
} }
} }
async function mergeDepartmentItemsIntoParent(parentId) { async function mergeDepartmentItemsIntoParent(parentId, messageRecord) {
const [latestParent, departmentSubject] = await Promise.all([ const [latestParent, departmentSubject] = await Promise.all([
loadRecord(parentId), loadRecord(parentId),
loadDepartmentSubject(), loadDepartmentSubject(),
]); ]);
const items = normalizeDepartmentControlItems(latestParent); const items = normalizeDepartmentControlItems(latestParent);
if (!allDepartmentItemsApproved(items) || items.every((item) => item.merged_at)) { const scopedItemIds = new Set(normalizeIds(flowMetadata(messageRecord).department_item_ids));
const scopedItems = scopedItemIds.size > 0
? items.filter((item) => scopedItemIds.has(normalizeId(item.item_id)))
: items;
if (
scopedItems.length === 0
|| !allDepartmentItemsApproved(scopedItems)
|| scopedItems.every((item) => item.merged_at)
) {
return false; return false;
} }
const sortedItems = [...items].sort((left, right) => { const sortedItems = [...scopedItems].sort((left, right) => {
const leftTime = dayjs(left.submitted_at || 0).valueOf(); const leftTime = dayjs(left.submitted_at || 0).valueOf();
const rightTime = dayjs(right.submitted_at || 0).valueOf(); const rightTime = dayjs(right.submitted_at || 0).valueOf();
return leftTime - rightTime; return leftTime - rightTime;
...@@ -828,21 +881,21 @@ async function mergeDepartmentItemsIntoParent(parentId) { ...@@ -828,21 +881,21 @@ async function mergeDepartmentItemsIntoParent(parentId) {
const businessPatch = {}; const businessPatch = {};
departmentBusinessFields(departmentSubject).forEach((field) => { departmentBusinessFields(departmentSubject).forEach((field) => {
if (field.name === 'business_unit') return; if (field.name === 'business_unit') return;
sortedItems.forEach((item) => { businessPatch[field.name] = mergeDepartmentBusinessValue(sortedItems, field);
if (Object.prototype.hasOwnProperty.call(item, field.name)) {
businessPatch[field.name] = normalizeSnapshotValue(item[field.name], field);
}
});
}); });
businessPatch.business_unit = uniqueValues(sortedItems.map((item) => item.business_unit)); businessPatch.business_unit = uniqueValues(sortedItems.map((item) => item.business_unit));
const mergedAt = dayjs().toISOString(); const mergedAt = dayjs().toISOString();
const mergedItems = items.map((item) => ({ ...item, merged_at: mergedAt })); const mergedItems = items.map((item) => (
scopedItemIds.size === 0 || scopedItemIds.has(normalizeId(item.item_id))
? { ...item, merged_at: mergedAt }
: item
));
await updateRecord(latestParent._id || latestParent.name, { await updateRecord(latestParent._id || latestParent.name, {
metadata: { metadata: {
...businessPatch, ...businessPatch,
[DEPARTMENT_CONTROL_ITEMS_FIELD]: mergedItems, [DEPARTMENT_CONTROL_ITEMS_FIELD]: mergedItems,
...departmentParentSummary(mergedItems), ...departmentParentSummary(scopedItems),
}, },
}); });
return true; return true;
...@@ -1211,7 +1264,7 @@ export function getDepartmentItemPermissions(item, user) { ...@@ -1211,7 +1264,7 @@ export function getDepartmentItemPermissions(item, user) {
&& item?.auditor_status !== FLOW_STATUS.approved, && item?.auditor_status !== FLOW_STATUS.approved,
canFinalAudit: isSender canFinalAudit: isSender
&& item?.auditor_status === FLOW_STATUS.approved && item?.auditor_status === FLOW_STATUS.approved
&& item?.sender_status !== FLOW_STATUS.approved, && (item?.sender_status !== FLOW_STATUS.approved || !item?.merged_at),
}; };
} }
...@@ -1379,7 +1432,7 @@ export async function finalApproveDepartmentControlItem({ ...@@ -1379,7 +1432,7 @@ export async function finalApproveDepartmentControlItem({
throw new Error('该部门明细尚未通过一级审批。'); throw new Error('该部门明细尚未通过一级审批。');
} }
if (item.sender_status === FLOW_STATUS.approved) { if (item.sender_status === FLOW_STATUS.approved) {
throw new Error('该部门明细已经完成最终审批。'); return item;
} }
await completeProgressTasks(item.progress_id_sender); await completeProgressTasks(item.progress_id_sender);
return { return {
...@@ -1388,7 +1441,7 @@ export async function finalApproveDepartmentControlItem({ ...@@ -1388,7 +1441,7 @@ export async function finalApproveDepartmentControlItem({
sender_decided_at: dayjs().toISOString(), sender_decided_at: dayjs().toISOString(),
}; };
}); });
const merged = await mergeDepartmentItemsIntoParent(parentId); const merged = await mergeDepartmentItemsIntoParent(parentId, messageRecord);
await updateDepartmentFlowSummary(messageRecord); await updateDepartmentFlowSummary(messageRecord);
return { ...result, merged }; return { ...result, merged };
} }
......
...@@ -170,9 +170,15 @@ ...@@ -170,9 +170,15 @@
### 6.3 最终审核 ### 6.3 最终审核
- 仅当前子项 `sender` 可操作,且该子项一级审批必须通过。 - 仅当前子项 `sender` 可操作,且该子项一级审批必须通过。
- 单个部门终审通过时只更新当前子项;全部子项 `sender_status=已审批` 后才合并主表。 - 单个部门终审通过时只更新当前子项;本次流程 `department_item_ids` 对应的全部子项
- 合并按 `submitted_at` 升序应用子项业务字段,同名字段由最后送审的子项覆盖; `sender_status=已审批` 后才合并主表,历史或未参与本次推送的子项不阻塞本次合并。
`business_unit` 按全部子项责任单位汇总,公式、人员、状态和待办字段不参与业务字段覆盖。 - 历史流程若已终审但未写入 `merged_at`,发起人可再次触发最终审批动作补偿合并;
已审批状态不会被重复改写。
- 合并按 `submitted_at` 升序应用子项业务字段:文本、长文本和富文本按子项值用换行符
`\n` 拼接;`reference` 字段拼接各子项 `_id[]` 并去重;多值普通字段拼接去重;
其他单值字段取最后一个有值子项。
- `business_unit` 按全部子项责任单位汇总,公式、人员、状态和待办字段不参与业务字段覆盖;
主表审批状态只由子项审批状态映射汇总,按最落后状态展示。
- 合并后为所有子项写入同一个 `merged_at`;主表后续修改不会回写子项。 - 合并后为所有子项写入同一个 `merged_at`;主表后续修改不会回写子项。
- 终审驳回只把当前子项退回填报人并重新经历一级审批。 - 终审驳回只把当前子项退回填报人并重新经历一级审批。
......
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