Commit 004e66ec authored by David Yang's avatar David Yang

feat: 支持部门级延期申请

parent 3917767f
......@@ -466,6 +466,7 @@ export default function RecordWorkbench({
states = {},
renderCell,
renderExpandedRow,
getExpandedRecordRows,
isRowExpandable,
getRecordId = defaultGetRecordId,
getRecordLabel = defaultGetRecordLabel,
......@@ -534,7 +535,8 @@ export default function RecordWorkbench({
);
const hasColumnGroups = columnGroupMeta.groups.length > 0;
const showColumnGroupHeader = columnGroupSegments.some((segment) => segment.group);
const hasExpandableRows = typeof renderExpandedRow === 'function';
const hasExpandedRecordRows = typeof getExpandedRecordRows === 'function';
const hasExpandableRows = hasExpandedRecordRows || typeof renderExpandedRow === 'function';
const hasExpandColumn = hasExpandableRows && showExpandColumn;
const tableColSpan =
stickyColumns.orderedColumns.length +
......@@ -997,6 +999,21 @@ export default function RecordWorkbench({
openDetail,
};
const visibleRowActions = rowActions.filter((action) => !isActionHidden(action, rowContext));
const expandedRecordRows = (() => {
if (!expanded || !expandable || !hasExpandedRecordRows) {
return EMPTY_ARRAY;
}
const nextExpandedRecordRows = getExpandedRecordRows({
record,
rowContext,
columns: stickyColumns.orderedColumns,
});
return Array.isArray(nextExpandedRecordRows)
? nextExpandedRecordRows
: EMPTY_ARRAY;
})();
return (
<Fragment key={recordId}>
......@@ -1101,7 +1118,83 @@ export default function RecordWorkbench({
</TableCell>
) : null}
</TableRow>
{expanded && expandable ? (
{expanded && expandable && hasExpandedRecordRows ? (
expandedRecordRows.map((expandedRecord, expandedIndex) => {
const expandedRecordId =
expandedRecord?.key ||
expandedRecord?.id ||
expandedRecord?._id ||
`${recordId}-expanded-${expandedIndex}`;
return (
<TableRow
key={expandedRecordId}
className="align-top bg-rose-50/35"
>
{selectable ? (
<TableCell
className="align-top sticky left-0 z-30 bg-rose-50/95 shadow-[10px_0_20px_rgba(15,23,42,0.05)]"
style={{ width: SELECTION_COLUMN_WIDTH, minWidth: SELECTION_COLUMN_WIDTH }}
/>
) : null}
{hasExpandColumn ? (
<TableCell className="w-12 align-top bg-rose-50/70" style={{ width: 48, minWidth: 48 }} />
) : null}
{stickyColumns.orderedColumns.map((column) => {
const stickyMeta = stickyColumns.stickyMeta[column.field];
return (
<TableCell
key={column.field}
style={{
width: column.width || 180,
minWidth: Math.min(Math.max(column.width || 180, 150), 360),
left: stickyMeta?.side === 'left' ? `${stickyMeta.offset}px` : undefined,
right: stickyMeta?.side === 'right' ? `${stickyMeta.offset}px` : undefined,
}}
className={clsx(
'align-top',
getStickyCellClass(stickyMeta),
)}
>
{(() => {
if (column.render) {
return column.render(expandedRecord, column);
}
if (renderCell) {
const customNode = renderCell({
record: expandedRecord,
column,
expandable: false,
expanded: false,
toggleExpanded: () => {},
parentRecord: record,
isExpandedRecord: true,
});
if (customNode !== undefined) {
return customNode;
}
}
return defaultRenderCell(expandedRecord, column);
})()}
</TableCell>
);
})}
{visibleRowActions.length > 0 ? (
<TableCell
className="align-top sticky right-0 z-30 bg-rose-50/95 shadow-[-10px_0_20px_rgba(15,23,42,0.05)]"
style={{ width: actionColumnWidth, minWidth: actionColumnWidth }}
/>
) : null}
</TableRow>
);
})
) : null}
{expanded && expandable && !hasExpandedRecordRows ? (
<TableRow key={`${recordId}-expanded`} className="bg-zinc-50/70">
<TableCell colSpan={tableColSpan} className="px-6 py-4">
{renderExpandedRow({ record, rowContext })}
......
......@@ -34,6 +34,7 @@ import {
AUDIT_STATUS_PENDING,
AUDIT_STATUS_REJECTED,
DELAY_REQUEST_FIELD,
DELAY_REQUEST_ITEMS_FIELD,
EDITOR_STATUS_COMPLETED,
EDITOR_STATUS_PENDING,
FLOW_MODE_BATCH,
......@@ -41,9 +42,11 @@ import {
FLOW_MODE_DELAY_DEPARTMENT,
FLOW_MODE_DEPARTMENT,
PROBLEM_RECTIFICATION_SUBJECT,
buildDepartmentParentMetadataPatch,
buildDepartmentParentBatchUpdateData,
getProblemRectificationFlowTableUrl,
normalizeDelayRequest,
normalizeDelayRequestItems,
normalizeDepartmentRectificationItems,
} from '../problemRectificationListService';
......@@ -54,6 +57,7 @@ const FLOW_MANAGED_PARENT_FIELDS = [
'final_audit_status',
'department_rectification_items',
DELAY_REQUEST_FIELD,
DELAY_REQUEST_ITEMS_FIELD,
];
const DEPARTMENT_MANAGED_PARENT_FIELDS = [
...FLOW_MANAGED_PARENT_FIELDS,
......@@ -248,12 +252,50 @@ function buildBatchRows(records = [], metadata = {}) {
});
}
function buildDelayRows(records = [], metadata = {}) {
function buildDelayRows(records = [], metadata = {}, flowMode = FLOW_MODE_DELAY) {
const editor = toId(toArray(metadata.editor)[0]);
const auditor = toId(toArray(metadata.auditor)[0]);
const scopedItemIds = new Set(toArray(metadata.department_item_ids).map(toId).filter(Boolean));
return records.flatMap((record) => {
const recordMetadata = record.metadata || {};
if (flowMode === FLOW_MODE_DELAY_DEPARTMENT) {
return normalizeDelayRequestItems(recordMetadata[DELAY_REQUEST_ITEMS_FIELD])
.filter(
(request) =>
scopedItemIds.size === 0 ||
scopedItemIds.has(request.item_id) ||
scopedItemIds.has(request.source_department_item_id),
)
.map((request) => ({
key: `${record._id || record.name}-delay-${request.item_id}`,
flowMode: FLOW_MODE_DELAY_DEPARTMENT,
record,
item: request,
itemId: request.item_id,
sourceDepartmentItemId: request.source_department_item_id || request.item_id,
recordId: record._id || record.name,
title: record.title || recordMetadata.project_name || '-',
projectName: recordMetadata.project_name || '-',
problemBrief: recordMetadata.problem_brief || '',
relatedIssueDomain: toArray(recordMetadata.related_issue_domain).join('、'),
department: request.responsible_department || '-',
editor: request.editor || editor,
auditor: request.auditor || auditor,
sender: request.sender || toId(metadata.sender),
originalDueDate: request.original_due_date || '',
requestedDueDate: request.requested_due_date || '',
delayReason: request.delay_reason || '',
editorStatus: request.editor_status || EDITOR_STATUS_PENDING,
auditStatus: request.auditor_status || AUDIT_STATUS_PENDING,
finalAuditStatus: request.sender_status || AUDIT_STATUS_PENDING,
progressIdEditor: request.progress_id_editor || '',
progressIdAuditor: request.progress_id_auditor || '',
progressIdSender: request.progress_id_sender || '',
isLocked: recordMetadata.is_locked === true,
}));
}
const request = normalizeDelayRequest(recordMetadata[DELAY_REQUEST_FIELD]);
if (!request) {
return [];
......@@ -308,7 +350,7 @@ export default function ProblemRectificationFlowTablePage() {
const rows = useMemo(
() => {
if (flowMode === FLOW_MODE_DELAY || flowMode === FLOW_MODE_DELAY_DEPARTMENT) {
return buildDelayRows(records, metadata);
return buildDelayRows(records, metadata, flowMode);
}
return flowMode === FLOW_MODE_DEPARTMENT
? buildDepartmentRows(records)
......@@ -453,6 +495,67 @@ export default function ProblemRectificationFlowTablePage() {
throw new Error('当前问题整改记录已锁定,不允许继续修改或审批。');
}
if (row.flowMode === FLOW_MODE_DELAY_DEPARTMENT) {
const currentRequests = normalizeDelayRequestItems(
latestRecord?.metadata?.[DELAY_REQUEST_ITEMS_FIELD],
);
const currentRequest = currentRequests.find(
(request) =>
request.item_id === row.itemId ||
request.source_department_item_id === row.sourceDepartmentItemId,
);
if (!currentRequest) {
throw new Error('当前部门延期申请不存在,请刷新后重试。');
}
const currentFlowIds = [messageRecord?.name, messageRecord?._id, messageId].filter(Boolean);
if (
currentRequest.flow_record_id &&
!currentFlowIds.includes(currentRequest.flow_record_id)
) {
throw new Error('当前部门延期申请已关联其他流程,请刷新后重试。');
}
const nextRequests = currentRequests.map((request) =>
request.item_id === currentRequest.item_id
? {
...request,
...requestPatch,
}
: request,
);
const metadataPatch = {
[DELAY_REQUEST_ITEMS_FIELD]: nextRequests,
};
if (recordPatch.the_time_of_rectification) {
const currentDepartmentItems = normalizeDepartmentRectificationItems(latestRecord);
const sourceDepartmentItemId = row.sourceDepartmentItemId || row.itemId;
if (currentDepartmentItems.length > 0) {
const nextDepartmentItems = currentDepartmentItems.map((item) =>
item.item_id === sourceDepartmentItemId
? {
...item,
the_time_of_rectification: recordPatch.the_time_of_rectification,
updated_by: currentUserId,
updated_at: dayjs().toISOString(),
}
: item,
);
Object.assign(metadataPatch, buildDepartmentParentMetadataPatch(nextDepartmentItems));
} else {
metadataPatch.the_time_of_rectification = recordPatch.the_time_of_rectification;
}
}
await updateRecord(row.recordId, {
metadata: metadataPatch,
});
return;
}
const currentRequest = normalizeDelayRequest(
latestRecord?.metadata?.[DELAY_REQUEST_FIELD],
);
......
......@@ -29,6 +29,7 @@ export const PROBLEM_RECTIFICATION_PAGE_SIZE_OPTIONS = [10, 20, 50, 100];
export const DEFAULT_PROBLEM_RECTIFICATION_PAGE_SIZE = 20;
export const DEPARTMENT_RECTIFICATION_FIELD = 'department_rectification_items';
export const DELAY_REQUEST_FIELD = 'problem_rectification_delay_request';
export const DELAY_REQUEST_ITEMS_FIELD = 'problem_rectification_delay_request_items';
export const FLOW_MODE_DEPARTMENT = 'department';
export const FLOW_MODE_BATCH = 'batch';
export const FLOW_MODE_DELAY = 'delay';
......@@ -40,6 +41,8 @@ export const AUDIT_STATUS_PENDING = '未审批';
export const AUDIT_STATUS_APPROVED = '已审批';
export const AUDIT_STATUS_REJECTED = '已驳回';
const FIXED_PROGRESS_VALUES = new Set(['已整改', '已优化']);
const RECTIFICATION_PROGRESS_PRIORITY = ['整改中', '部分整改', '已整改', '已优化', '已完成', '不适用'];
const AUDIT_STATUS_PRIORITY = [AUDIT_STATUS_REJECTED, AUDIT_STATUS_PENDING, AUDIT_STATUS_APPROVED];
const LEVEL_ORDER = ['高', '中', '低'];
const FILTER_FIELD_PATHS = {
progress: 'metadata.rectification_progress',
......@@ -132,18 +135,36 @@ function makeDepartmentItemId(record, department, index) {
return `dept_${recordId}_${index + 1}_${departmentSlug}_${Date.now()}_${randomPart}`;
}
function summarizeTextStatus(values = [], partialText = '部分审批', emptyText = '') {
const normalizedValues = uniqueTruthy(values);
function summarizeLaggingStatus(values = [], priority = [], emptyText = '', unknownRank = 1) {
const normalizedValues = values.map((value) => normalizeText(value));
if (normalizedValues.length === 0) {
return emptyText;
}
if (normalizedValues.length === 1) {
return normalizedValues[0];
}
const priorityRank = new Map(priority.map((status, index) => [status, index]));
const getStatusRank = (status) => {
if (!status) {
return -1;
}
return priorityRank.has(status) ? priorityRank.get(status) : unknownRank;
};
return partialText;
return normalizedValues.reduce((laggingStatus, status) => {
const nextStatus = status || emptyText;
return getStatusRank(nextStatus) < getStatusRank(laggingStatus)
? nextStatus
: laggingStatus;
}, normalizedValues[0] || emptyText);
}
function summarizeDepartmentProgress(values = []) {
return summarizeLaggingStatus(values, RECTIFICATION_PROGRESS_PRIORITY, '', 1);
}
function summarizeDepartmentAuditStatus(values = []) {
return summarizeLaggingStatus(values, AUDIT_STATUS_PRIORITY, AUDIT_STATUS_PENDING, 1);
}
function summarizeDate(values = []) {
......@@ -240,6 +261,9 @@ export function normalizeDelayRequest(value) {
return {
...parsedValue,
item_id: normalizeText(parsedValue.item_id),
source_department_item_id: normalizeText(parsedValue.source_department_item_id),
responsible_department: normalizeText(parsedValue.responsible_department),
original_due_date: parsedValue.original_due_date || null,
requested_due_date: parsedValue.requested_due_date || null,
delay_reason: normalizeText(parsedValue.delay_reason),
......@@ -259,8 +283,41 @@ export function normalizeDelayRequest(value) {
};
}
export function normalizeDelayRequestItems(value) {
let parsedValue = value;
if (typeof value === 'string') {
try {
parsedValue = JSON.parse(value);
} catch (error) {
return [];
}
}
return toArray(parsedValue)
.map((item) => normalizeDelayRequest(item))
.filter(Boolean)
.map((item, index) => ({
...item,
item_id: item.item_id || item.source_department_item_id || `delay_item_${index + 1}`,
responsible_department: item.responsible_department || '未指定责任部门',
}));
}
export function buildDepartmentAssignmentsFromRecord(record) {
const metadata = record?.metadata || record?.raw?.metadata || {};
const departmentItems = normalizeDepartmentRectificationItems(record);
if (departmentItems.length > 0) {
return departmentItems.map((item, index) => ({
item_id: item.item_id || makeDepartmentItemId(record?.raw || record, item.responsible_department, index),
source_department_item_id: item.item_id || '',
responsible_department: normalizeText(item.responsible_department) || '未指定责任部门',
editor: null,
auditor: null,
}));
}
const departments = uniqueTruthy(
toArray(metadata.responsible_department).length > 0
? toArray(metadata.responsible_department)
......@@ -301,6 +358,54 @@ export function buildDepartmentRectificationItems(record, assignments = [], curr
}));
}
export function buildDepartmentDelayRequestItems({
record,
assignments = [],
currentUser = null,
flowRecordId = '',
progressIdEditorByItemId = {},
} = {}) {
const metadata = record?.metadata || record?.raw?.metadata || {};
const departmentItemsById = new Map(
normalizeDepartmentRectificationItems(record).map((item) => [item.item_id, item]),
);
const currentUserId = normalizeUserId(currentUser);
return assignments.map((assignment, index) => {
const sourceDepartmentItemId = normalizeText(assignment.source_department_item_id || assignment.item_id);
const sourceDepartmentItem = departmentItemsById.get(sourceDepartmentItemId);
const itemId =
normalizeText(assignment.item_id) ||
sourceDepartmentItemId ||
makeDepartmentItemId(record?.raw || record, assignment.responsible_department, index);
return {
item_id: itemId,
source_department_item_id: sourceDepartmentItemId,
responsible_department: normalizeText(assignment.responsible_department) || '未指定责任部门',
original_due_date:
sourceDepartmentItem?.the_time_of_rectification ||
metadata.the_time_of_rectification ||
null,
requested_due_date: null,
delay_reason: '',
editor: normalizeUserId(assignment.editor),
auditor: normalizeUserId(assignment.auditor),
sender: currentUserId,
editor_status: EDITOR_STATUS_PENDING,
auditor_status: AUDIT_STATUS_PENDING,
sender_status: AUDIT_STATUS_PENDING,
progress_id_editor: progressIdEditorByItemId[itemId] || '',
progress_id_auditor: '',
progress_id_sender: '',
flow_record_id: flowRecordId,
submitted_at: null,
auditor_decided_at: null,
sender_decided_at: null,
};
});
}
export function buildDepartmentParentMetadataPatch(items = []) {
const normalizedItems = items.filter(Boolean);
const editorIds = uniqueTruthy(normalizedItems.map((item) => item.editor));
......@@ -310,20 +415,14 @@ export function buildDepartmentParentMetadataPatch(items = []) {
[DEPARTMENT_RECTIFICATION_FIELD]: normalizedItems,
fill_in_the_people: editorIds,
review_people: auditorIds,
rectification_progress: summarizeTextStatus(
rectification_progress: summarizeDepartmentProgress(
normalizedItems.map((item) => item.rectification_progress),
'部分整改',
'',
),
audit_status: summarizeTextStatus(
audit_status: summarizeDepartmentAuditStatus(
normalizedItems.map((item) => item.audit_status),
'部分审批',
AUDIT_STATUS_PENDING,
),
final_audit_status: summarizeTextStatus(
final_audit_status: summarizeDepartmentAuditStatus(
normalizedItems.map((item) => item.final_audit_status),
'部分审批',
AUDIT_STATUS_PENDING,
),
the_time_of_rectification: summarizeDate(
normalizedItems.map((item) => item.the_time_of_rectification),
......@@ -668,25 +767,13 @@ export function normalizeProblemRectificationRecord(record) {
const departmentItems = normalizeDepartmentRectificationItems(record);
const hasDepartmentItems = departmentItems.length > 0;
const summarizedProgress = hasDepartmentItems
? summarizeTextStatus(
departmentItems.map((item) => item.rectification_progress),
'部分整改',
'',
)
? summarizeDepartmentProgress(departmentItems.map((item) => item.rectification_progress))
: normalizeText(metadata.rectification_progress);
const summarizedAuditStatus = hasDepartmentItems
? summarizeTextStatus(
departmentItems.map((item) => item.audit_status),
'部分审批',
AUDIT_STATUS_PENDING,
)
? summarizeDepartmentAuditStatus(departmentItems.map((item) => item.audit_status))
: normalizeText(metadata.audit_status);
const summarizedFinalAuditStatus = hasDepartmentItems
? summarizeTextStatus(
departmentItems.map((item) => item.final_audit_status),
'部分审批',
AUDIT_STATUS_PENDING,
)
? summarizeDepartmentAuditStatus(departmentItems.map((item) => item.final_audit_status))
: normalizeText(metadata.final_audit_status);
const summarizedDueDate = hasDepartmentItems
? summarizeDate(departmentItems.map((item) => item.the_time_of_rectification))
......@@ -771,6 +858,7 @@ export function normalizeProblemRectificationRecord(record) {
departmentItems,
hasDepartmentItems,
delayRequest: normalizeDelayRequest(metadata[DELAY_REQUEST_FIELD]),
delayRequestItems: normalizeDelayRequestItems(metadata[DELAY_REQUEST_ITEMS_FIELD]),
isFixed,
isLocked: metadata.is_locked === true,
isOverdue,
......
......@@ -152,6 +152,7 @@ type Record = {
| 主责部门 | `primary_responsible_department` | text | `null` | 否 | 否 | 否 | 否 | 否 |
| 部门整改明细 | `department_rectification_items` | object | `[]` | 否 | 是 | 否 | 否 | 否 |
| 延期申请 | `problem_rectification_delay_request` | subject | `null` | 否 | 否 | 否 | 否 | 否 |
| 部门延期申请明细 | `problem_rectification_delay_request_items` | object | `[]` | 否 | 是 | 否 | 否 | 否 |
| 状态锁定 | `is_locked` | boolean | `false` | 否 | 否 | 否 | 否 | 否 |
### 3.1.1 select 字段选项
......@@ -176,8 +177,9 @@ type Record = {
- 新增驾驶舱扩展字段当前在线上 Subject 中大多仍为 `text` 类型,前端工作台按标准值引导录入,用于支持后续严重程度、问题分类、问题原因和整改质效分析
- 其余 `select` 类型的可选值必须以线上 Subject 配置中的 `settings.options` 为准,禁止在页面内硬编码
- `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` 以明细为事实来源,父字段仅做兼容汇总展示
- `problem_rectification_delay_request` 为问题主记录的单次延期申请嵌套对象,与 `department_rectification_items` 平级;发起延期不要求已有部门整改明细
- `department_rectification_items` 用于新转发流程的部门级整改明细。单条问题即使只有 1 个责任部门,也生成 1 条明细;转发后 `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_items` 为部门级延期申请明细数组;每个责任部门 1 条延期申请,独立保存填写人、一级审批人、最终审批人、申请完成时间、延期事由、状态和待办 ID
- `is_locked` 为问题整改主记录的状态锁定标记;仅整改进度汇总值为 `已整改` 的记录允许写入 `true`,锁定后所有业务字段和流程状态均不可修改,只有解锁操作可以将其写回 `false`
- 部门明细的填写人只能编辑自己负责明细的整改业务字段,送审后(`editor_status = "已完成"`)不再允许编辑;审批人审批通过后(`audit_status = "已审批"`)不再允许编辑,审批驳回后(`audit_status = "已驳回"`)审批人可以编辑该明细的整改业务字段,填写人也恢复编辑。审批人恢复的只是业务字段编辑权限,不获得填写人的送审权限。流程推送人可以编辑本次推送关联的父问题记录及全部部门明细整改业务字段。审批状态、待办 ID、明细 ID 等流程控制字段不作为普通表单字段开放
- 推送人修改父问题记录或部门明细时,不自动重置该明细已经形成的审批状态,也不自动重新生成流程待办
......@@ -204,14 +206,18 @@ type Record = {
| 更新人 | `updated_by` | user | 否 | 最近更新用户 `_id` |
| 更新时间 | `updated_at` | date | 否 | 最近更新时间 |
### 3.1.4 `problem_rectification_delay_request` 嵌套对象
### 3.1.4 `problem_rectification_delay_request` / `problem_rectification_delay_request_items` 嵌套对象
- **subject**: `problem_rectification_delay_request`
- **父级路径**: `metadata.problem_rectification_delay_request`
- **部门级父级路径**: `metadata.problem_rectification_delay_request_items[]`
| 字段标签 | 字段别名 | 字段类型 | 是否必填 | 说明 |
| --- | --- | --- | --- | --- |
| 原预计完成时间 | `original_due_date` | date | 否 | 发起申请时从问题主记录 `the_time_of_rectification` 复制,保存 ISO 8601 |
| 明细 ID | `item_id` | text | 部门级必填 | 部门延期申请唯一标识,用于行级保存、审批和待办回写 |
| 关联部门整改明细 ID | `source_department_item_id` | text | 否 | 有 `department_rectification_items` 时保存对应 `item_id`,最终审批通过后用于回写该部门预计完成时间 |
| 责任部门 | `responsible_department` | text | 部门级必填 | 当前延期申请对应的责任部门 |
| 原预计完成时间 | `original_due_date` | date | 否 | 发起申请时优先从对应部门明细 `the_time_of_rectification` 复制,无部门明细时从问题主记录复制,保存 ISO 8601 |
| 申请完成时间 | `requested_due_date` | date | 否 | 填写人填写;提交申请时必填并保存 ISO 8601 |
| 延期事由 | `delay_reason` | long_text | 否 | 填写人填写;提交申请时必填 |
| 填写人 | `editor` | user | 否 | 保存推送时选择的填写人 `_id` |
......@@ -230,11 +236,11 @@ type Record = {
写入约定:
- 延期申请发起时只初始化问题主记录的嵌套对象和填写人 OA 待办,不修改主记录 `the_time_of_rectification`
- 延期申请发起时初始化问题主记录的部门级延期申请数组和填写人 OA 待办,不修改主记录或部门明细的 `the_time_of_rectification`
- `requested_due_date`、`delay_reason` 在保存草稿时可以为空,在提交申请时由前端强制校验必填
- 一级审批或最终审批驳回后,`editor_status` 重置为 `未完成`,并重新创建填写人 OA 待办
- 只有 `sender_status = "已审批"` 时,才把 `requested_due_date` 写回问题主记录 `the_time_of_rectification`
- 同一问题主记录已有该嵌套对象时,不允许再次创建另一份延期申请
- 一级审批或最终审批驳回后,仅对应部门延期申请的 `editor_status` 重置为 `未完成`,并重新创建该部门填写人 OA 待办
- 只有部门延期申请 `sender_status = "已审批"` 时,才把该行 `requested_due_date` 写回对应 `department_rectification_items[].the_time_of_rectification`;如果不存在对应部门整改明细,则回退写回问题主记录 `the_time_of_rectification`
- 同一问题主记录已有 `problem_rectification_delay_request` 或 `problem_rectification_delay_request_items` 时,不允许再次创建另一份延期申请
### 3.1.5 metadata 示例
......@@ -363,8 +369,8 @@ type Record = {
- `restriction_value` 保存被推送整改记录的 `_id[]`
- `reference_record_id` 与 `restriction_value` 保持一致,作为显式的“关联记录 ID”冗余字段
- `subject_name` 保存被推送记录所属对象名,当前整改流程固定为 `problem_rectification`
- `flow_mode` 区分流程:`department` 表示单条问题按责任部门生成整改明细行,`batch` 表示多选问题的单填写人 / 单审批人简单推送,`delay` 表示问题级延期申请;历史 `delay_department` 值仅保留页面识别兼容
- `department_item_ids` 保存本次部门流程涉及的 `department_rectification_items[].item_id`
- `flow_mode` 区分流程:`department` 表示单条问题按责任部门生成整改明细行,`batch` 表示多选问题的单填写人 / 单审批人简单推送,`delay` 表示历史问题级延期申请,`delay_department` 表示按责任部门生成延期申请明细
- `department_item_ids` 保存本次部门流程涉及的部门级 `item_id`;整改流程对应 `department_rectification_items[].item_id`,延期流程对应 `problem_rectification_delay_request_items[].item_id`
- `progress_id_editor`、`progress_id_auditor`、`progress_id_sender` 用于记录每一步用户确认动作生成的流程 ID,用来区分当前流程推进到了哪一步
- `editor_status`:当前项目统一使用 `未完成`、`已完成`
- `auditor_status`:当前项目统一使用 `未完成`、`已完成`
......
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