Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
P
problem_rectification
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
problem_rectification
Commits
5a67a51e
Commit
5a67a51e
authored
Aug 06, 2026
by
David Yang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 完善批量整改流程和严重程度计算
parent
1cf5afe5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
309 additions
and
60 deletions
+309
-60
pnpm-lock.yaml
pnpm-lock.yaml
+0
-0
pnpm-workspace.yaml
pnpm-workspace.yaml
+5
-0
manifest.json
public/manifest.json
+1
-1
useSubjectRecord.js
src/pages/RecordDetailDrawer/hooks/useSubjectRecord.js
+36
-4
index.jsx
src/pages/RecordDetailDrawer/index.jsx
+8
-1
index.jsx
src/pages/problem_rectification/dashboard/index.jsx
+55
-19
index.jsx
src/pages/problem_rectification/flow-table/index.jsx
+0
-0
index.jsx
src/pages/problem_rectification/list/index.jsx
+105
-23
problem-rectification.business-flow.md
...blem_rectification/problem-rectification.business-flow.md
+0
-0
problemRectificationDashboardService.js
...lem_rectification/problemRectificationDashboardService.js
+2
-1
problemRectificationListService.js
.../problem_rectification/problemRectificationListService.js
+56
-7
problemSeverity.js
src/pages/problem_rectification/problemSeverity.js
+27
-0
subjectsDefinition.md
src/services/subjectsDefinition.md
+14
-4
No files found.
pnpm-lock.yaml
View file @
5a67a51e
This diff is collapsed.
Click to expand it.
pnpm-workspace.yaml
0 → 100644
View file @
5a67a51e
allowBuilds
:
core-js
:
false
core-js-pure
:
false
es5-ext
:
false
esbuild
:
false
public/manifest.json
View file @
5a67a51e
{
{
"name"
:
"problem_rectification_new"
,
"name"
:
"problem_rectification_new"
,
"version"
:
"1.0.
6
"
,
"version"
:
"1.0.
7
"
,
"build"
:
"abc"
,
"build"
:
"abc"
,
"description"
:
"merge master"
,
"description"
:
"merge master"
,
"date"
:
"2025-07-14 19:27:20"
,
"date"
:
"2025-07-14 19:27:20"
,
...
...
src/pages/RecordDetailDrawer/hooks/useSubjectRecord.js
View file @
5a67a51e
...
@@ -40,7 +40,27 @@ function transformSaveValue(newValue, fieldDef) {
...
@@ -40,7 +40,27 @@ function transformSaveValue(newValue, fieldDef) {
return
newValue
;
return
newValue
;
}
}
export
function
useSubjectRecord
(
recordName
,
refreshKey
,
readonly
,
subjectName
)
{
function
buildNestedPayload
(
fieldPath
,
value
)
{
return
fieldPath
.
split
(
'.'
).
reduceRight
((
nestedValue
,
key
)
=>
({
[
key
]:
nestedValue
}),
value
);
}
function
mergePayload
(
target
,
source
)
{
return
Object
.
entries
(
source
||
{}).
reduce
((
result
,
[
key
,
value
])
=>
({
...
result
,
[
key
]:
value
&&
typeof
value
===
'object'
&&
!
Array
.
isArray
(
value
)
?
mergePayload
(
result
[
key
]
||
{},
value
)
:
value
,
}),
target
);
}
export
function
useSubjectRecord
(
recordName
,
refreshKey
,
readonly
,
subjectName
,
deriveFieldValues
,
)
{
const
[
remoteRecord
,
setRemoteRecord
]
=
useState
(
null
);
const
[
remoteRecord
,
setRemoteRecord
]
=
useState
(
null
);
const
[
editableRecord
,
setEditableRecord
]
=
useState
(
null
);
const
[
editableRecord
,
setEditableRecord
]
=
useState
(
null
);
const
[
subject
,
setSubject
]
=
useState
(
null
);
const
[
subject
,
setSubject
]
=
useState
(
null
);
...
@@ -107,12 +127,24 @@ export function useSubjectRecord(recordName, refreshKey, readonly, subjectName)
...
@@ -107,12 +127,24 @@ export function useSubjectRecord(recordName, refreshKey, readonly, subjectName)
}
}
const
previousRecord
=
editableRecord
;
const
previousRecord
=
editableRecord
;
const
nextRecord
=
setFieldValue
(
editableRecord
,
fieldPath
,
newValue
);
let
nextRecord
=
setFieldValue
(
editableRecord
,
fieldPath
,
newValue
);
setEditableRecord
(
nextRecord
);
try
{
try
{
const
saveValue
=
transformSaveValue
(
newValue
,
fieldDef
);
const
saveValue
=
transformSaveValue
(
newValue
,
fieldDef
);
const
payload
=
fieldPath
.
split
(
'.'
).
reduceRight
((
value
,
key
)
=>
({
[
key
]:
value
}),
saveValue
);
let
payload
=
buildNestedPayload
(
fieldPath
,
saveValue
);
const
derivedFieldValues
=
deriveFieldValues
?.({
fieldPath
,
fieldDef
,
newValue
:
saveValue
,
record
:
nextRecord
,
})
||
{};
Object
.
entries
(
derivedFieldValues
).
forEach
(([
derivedFieldPath
,
derivedValue
])
=>
{
nextRecord
=
setFieldValue
(
nextRecord
,
derivedFieldPath
,
derivedValue
);
payload
=
mergePayload
(
payload
,
buildNestedPayload
(
derivedFieldPath
,
derivedValue
));
});
setEditableRecord
(
nextRecord
);
await
updateRecord
(
recordName
,
payload
);
await
updateRecord
(
recordName
,
payload
);
setRemoteRecord
(
nextRecord
);
setRemoteRecord
(
nextRecord
);
}
catch
(
updateError
)
{
}
catch
(
updateError
)
{
...
...
src/pages/RecordDetailDrawer/index.jsx
View file @
5a67a51e
...
@@ -51,6 +51,7 @@ const RecordDetailDrawer = (props, ref) => {
...
@@ -51,6 +51,7 @@ const RecordDetailDrawer = (props, ref) => {
preferredViewName
,
preferredViewName
,
disabledFields
=
EMPTY_ARRAY
,
disabledFields
=
EMPTY_ARRAY
,
authorizationView
=
null
,
authorizationView
=
null
,
deriveFieldValues
,
}
=
props
;
}
=
props
;
const
[
refreshKey
,
setRefreshKey
]
=
useState
(()
=>
Date
.
now
());
const
[
refreshKey
,
setRefreshKey
]
=
useState
(()
=>
Date
.
now
());
...
@@ -66,7 +67,13 @@ const RecordDetailDrawer = (props, ref) => {
...
@@ -66,7 +67,13 @@ const RecordDetailDrawer = (props, ref) => {
handleFieldUpdate
,
handleFieldUpdate
,
isFieldDisabled
,
isFieldDisabled
,
loading
,
loading
,
}
=
useSubjectRecord
(
recordName
,
refreshKey
,
readonly
,
subjectName
);
}
=
useSubjectRecord
(
recordName
,
refreshKey
,
readonly
,
subjectName
,
deriveFieldValues
,
);
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
open
)
{
if
(
open
)
{
...
...
src/pages/problem_rectification/dashboard/index.jsx
View file @
5a67a51e
...
@@ -464,6 +464,32 @@ function buildFocusIssueBarOption(data = []) {
...
@@ -464,6 +464,32 @@ function buildFocusIssueBarOption(data = []) {
function buildBubbleMatrixOption(data = []) {
function buildBubbleMatrixOption(data = []) {
const maxTotal = Math.max(...data.map((item) => item.totalCount), 1);
const maxTotal = Math.max(...data.map((item) => item.totalCount), 1);
const maxOverdue = Math.max(...data.map((item) => item.overdueCount), 1);
const maxOverdue = Math.max(...data.map((item) => item.overdueCount), 1);
const xAxisMax = maxTotal + Math.max(1, Math.ceil(maxTotal * 0.08));
const coordinateOccurrences = new Map();
const chartData = data.map((item) => {
const coordinateKey = `
$
{
item
.
totalCount
}:
$
{
item
.
completionRate
}
`;
const labelStackIndex = coordinateOccurrences.get(coordinateKey) || 0;
coordinateOccurrences.set(coordinateKey, labelStackIndex + 1);
return {
...item,
value: [item.totalCount, item.completionRate],
symbolSize: 16 + (item.overdueCount / maxOverdue) * 34,
itemStyle: {
color:
item.completionRate >= 75
? '#22c55e'
: item.completionRate >= 45
? '#f59e0b'
: '#e11d48',
opacity: 0.85,
},
label: {
position: item.completionRate >= 75 ? 'bottom' : 'top',
distance: 8 + labelStackIndex * 18,
},
};
});
return {
return {
tooltip: {
tooltip: {
...
@@ -481,15 +507,17 @@ function buildBubbleMatrixOption(data = []) {
...
@@ -481,15 +507,17 @@ function buildBubbleMatrixOption(data = []) {
textStyle:
{
color
:
'#fff'
}
,
textStyle:
{
color
:
'#fff'
}
,
},
},
grid:
{
grid:
{
left
:
8
,
left
:
12
,
right
:
16
,
right
:
24
,
top
:
12
,
top
:
28
,
bottom
:
1
0
,
bottom
:
1
6
,
containLabel
:
true
,
containLabel
:
true
,
}
,
}
,
xAxis:
{
xAxis:
{
type
:
'value'
,
type
:
'value'
,
name
:
'问题总数'
,
name
:
'问题总数'
,
min
:
0
,
max
:
xAxisMax
,
nameTextStyle
:
{
color
:
'#64748b'
},
nameTextStyle
:
{
color
:
'#64748b'
},
splitLine
:
{
lineStyle
:
{
color
:
'rgba(148,163,184,0.18)'
}
},
splitLine
:
{
lineStyle
:
{
color
:
'rgba(148,163,184,0.18)'
}
},
axisLabel
:
{
color
:
'#64748b'
},
axisLabel
:
{
color
:
'#64748b'
},
...
@@ -499,33 +527,41 @@ function buildBubbleMatrixOption(data = []) {
...
@@ -499,33 +527,41 @@ function buildBubbleMatrixOption(data = []) {
name
:
'整改完成率(%)'
,
name
:
'整改完成率(%)'
,
nameTextStyle
:
{
color
:
'#64748b'
},
nameTextStyle
:
{
color
:
'#64748b'
},
min
:
0
,
min
:
0
,
max
:
10
0
,
max
:
10
5
,
splitLine
:
{
lineStyle
:
{
color
:
'rgba(148,163,184,0.18)'
}
},
splitLine
:
{
lineStyle
:
{
color
:
'rgba(148,163,184,0.18)'
}
},
axisLabel
:
{
color
:
'#64748b'
},
axisLabel
:
{
color
:
'#64748b'
},
}
,
}
,
series: [
series: [
{
{
type
:
'scatter'
,
type
:
'scatter'
,
data
:
data
.
map
((
item
)
=>
({
data
:
chartData
,
...
item
,
value
:
[
item
.
totalCount
,
item
.
completionRate
],
symbolSize
:
16
+
(
item
.
overdueCount
/
maxOverdue
)
*
34
,
itemStyle
:
{
color
:
item
.
completionRate
>=
75
?
'#22c55e'
:
item
.
completionRate
>=
45
?
'#f59e0b'
:
'#e11d48'
,
opacity
:
0.85
,
},
})),
label
:
{
label
:
{
show
:
true
,
show
:
true
,
formatter
:
(
params
)
=>
params
.
data
.
name
,
formatter
:
(
params
)
=>
params
.
data
.
name
,
position
:
'top'
,
position
:
'top'
,
distance
:
8
,
color
:
'#334155'
,
color
:
'#334155'
,
fontSize
:
12
,
fontSize
:
12
,
backgroundColor
:
'rgba(255,255,255,0.9)'
,
borderRadius
:
3
,
padding
:
[
2
,
4
],
},
labelLayout
:
{
moveOverlap
:
'shiftY'
,
hideOverlap
:
true
,
},
emphasis
:
{
focus
:
'self'
,
label
:
{
show
:
true
,
color
:
'#0f172a'
,
fontWeight
:
700
,
backgroundColor
:
'#fff'
,
borderColor
:
'#cbd5e1'
,
borderWidth
:
1
,
borderRadius
:
3
,
padding
:
[
3
,
5
],
},
},
},
}
,
}
,
],
],
...
...
src/pages/problem_rectification/flow-table/index.jsx
View file @
5a67a51e
This diff is collapsed.
Click to expand it.
src/pages/problem_rectification/list/index.jsx
View file @
5a67a51e
...
@@ -78,6 +78,10 @@ import {
...
@@ -78,6 +78,10 @@ import {
normalizeDelayRequestItems
,
normalizeDelayRequestItems
,
normalizeDepartmentRectificationItems
,
normalizeDepartmentRectificationItems
,
}
from
'../problemRectificationListService'
;
}
from
'../problemRectificationListService'
;
import
{
calculateProblemSeverityLevel
,
PROBLEM_SEVERITY_SOURCE_FIELDS
,
}
from
'../problemSeverity'
;
const
FIELD_HINTS
=
{
const
FIELD_HINTS
=
{
inspect_the_implementing_unit
:
'按对象配置的实施单位选项选择'
,
inspect_the_implementing_unit
:
'按对象配置的实施单位选项选择'
,
...
@@ -92,7 +96,7 @@ const FIELD_HINTS = {
...
@@ -92,7 +96,7 @@ const FIELD_HINTS = {
has_external_impact
:
'建议统一填写“是”或“否”'
,
has_external_impact
:
'建议统一填写“是”或“否”'
,
has_employee_accountability
:
'建议统一填写“是”或“否”'
,
has_employee_accountability
:
'建议统一填写“是”或“否”'
,
accountability_person_count
:
'建议填写数字人数'
,
accountability_person_count
:
'建议填写数字人数'
,
problem_severity_level
:
'
建议统一填写“高 / 中 / 低”
'
,
problem_severity_level
:
'
由经济损失、外部影响和员工问责情况自动计算
'
,
rectification_timeliness_level
:
'建议统一填写“高 / 中 / 低”'
,
rectification_timeliness_level
:
'建议统一填写“高 / 中 / 低”'
,
rectification_material_quality_level
:
'建议统一填写“高 / 中 / 低”'
,
rectification_material_quality_level
:
'建议统一填写“高 / 中 / 低”'
,
involves_policy_revision
:
'建议统一填写“是”或“否”'
,
involves_policy_revision
:
'建议统一填写“是”或“否”'
,
...
@@ -112,7 +116,6 @@ const CREATE_FIELD_OPTION_OVERRIDES = {
...
@@ -112,7 +116,6 @@ const CREATE_FIELD_OPTION_OVERRIDES = {
has_economic_loss
:
[
'是'
,
'否'
],
has_economic_loss
:
[
'是'
,
'否'
],
has_external_impact
:
[
'是'
,
'否'
],
has_external_impact
:
[
'是'
,
'否'
],
has_employee_accountability
:
[
'是'
,
'否'
],
has_employee_accountability
:
[
'是'
,
'否'
],
problem_severity_level
:
[
'高'
,
'中'
,
'低'
],
problem_category_level_1
:
[
'内控建设'
,
'内控运行'
,
'内控建设及运行'
,
'其他'
],
problem_category_level_1
:
[
'内控建设'
,
'内控运行'
,
'内控建设及运行'
,
'其他'
],
problem_category_level_2
:
[
'公司治理'
,
'投资管理'
,
'销售管理'
,
'员工行为管理'
,
'基金运营'
,
'财务管理'
,
'信息技术'
],
problem_category_level_2
:
[
'公司治理'
,
'投资管理'
,
'销售管理'
,
'员工行为管理'
,
'基金运营'
,
'财务管理'
,
'信息技术'
],
problem_root_cause
:
[
'内外规执行不到位'
,
'系统化程度低'
,
'规矩意识淡薄导致操作风险'
,
'现有流程设计缺陷'
,
'制度建设有待完善'
],
problem_root_cause
:
[
'内外规执行不到位'
,
'系统化程度低'
,
'规矩意识淡薄导致操作风险'
,
'现有流程设计缺陷'
,
'制度建设有待完善'
],
...
@@ -405,6 +408,10 @@ function CreateField({ fieldDef, value, onChange }) {
...
@@ -405,6 +408,10 @@ function CreateField({ fieldDef, value, onChange }) {
const
placeholder
=
FIELD_HINTS
[
fieldDef
?.
name
]
||
`请输入
${
fieldDef
?.
label
||
fieldDef
?.
name
}
`;
const
placeholder
=
FIELD_HINTS
[
fieldDef
?.
name
]
||
`请输入
${
fieldDef
?.
label
||
fieldDef
?.
name
}
`;
const overrideOptions = CREATE_FIELD_OPTION_OVERRIDES[fieldDef?.name] || [];
const overrideOptions = CREATE_FIELD_OPTION_OVERRIDES[fieldDef?.name] || [];
if (fieldDef?.name === 'problem_severity_level') {
return <Input value={value || '低'} disabled aria-label="问题综合严重程度(自动计算)" />;
}
if (fieldType === 'long_text') {
if (fieldType === 'long_text') {
return (
return (
<Textarea
<Textarea
...
@@ -1289,6 +1296,39 @@ export default function ProblemRectificationListPage() {
...
@@ -1289,6 +1296,39 @@ export default function ProblemRectificationListPage() {
const nextAuditorIds = isDepartmentFlow
const nextAuditorIds = isDepartmentFlow
? Array.from(new Set(departmentAssignments.map((item) => item.auditor?._id).filter(Boolean)))
? Array.from(new Set(departmentAssignments.map((item) => item.auditor?._id).filter(Boolean)))
: auditorIds;
: auditorIds;
const batchDepartmentItemsByRecordId = new Map();
if (!isDepartmentFlow) {
const now = dayjs().toISOString();
for (const selectedRecord of selectedRecords) {
const sourceRecord = await loadRecord(selectedRecord.id);
const existingItems = normalizeDepartmentRectificationItems(sourceRecord);
const nextItems = existingItems.length > 0
? existingItems.map((item) => ({
...item,
editor: nextFillerIds[0],
auditor: nextAuditorIds[0],
editor_status: EDITOR_STATUS_PENDING,
audit_status: AUDIT_STATUS_PENDING,
final_audit_status: AUDIT_STATUS_PENDING,
progress_id_editor: '',
progress_id_auditor: '',
progress_id_sender: '',
updated_by: currentUserInfo?._id || currentUserInfo?.id || '',
updated_at: now,
}))
: buildDepartmentRectificationItems(
sourceRecord,
buildDepartmentAssignmentsFromRecord(sourceRecord).map((assignment) => ({
...assignment,
editor: { _id: nextFillerIds[0] },
auditor: { _id: nextAuditorIds[0] },
})),
currentUserInfo,
);
batchDepartmentItemsByRecordId.set(selectedRecord.id, nextItems);
}
}
const messageRecord = await createRecord({
const messageRecord = await createRecord({
subject: 'message_reference_id',
subject: 'message_reference_id',
...
@@ -1303,7 +1343,11 @@ export default function ProblemRectificationListPage() {
...
@@ -1303,7 +1343,11 @@ export default function ProblemRectificationListPage() {
view_name: activeGridView?.name || '',
view_name: activeGridView?.name || '',
subject_name: PROBLEM_RECTIFICATION_SUBJECT,
subject_name: PROBLEM_RECTIFICATION_SUBJECT,
flow_mode: isDepartmentFlow ? FLOW_MODE_DEPARTMENT : FLOW_MODE_BATCH,
flow_mode: isDepartmentFlow ? FLOW_MODE_DEPARTMENT : FLOW_MODE_BATCH,
department_item_ids: isDepartmentFlow ? departmentAssignments.map((item) => item.item_id) : [],
department_item_ids: isDepartmentFlow
? departmentAssignments.map((item) => item.item_id)
: Array.from(batchDepartmentItemsByRecordId.values())
.flat()
.map((item) => item.item_id),
editor_status: EDITOR_STATUS_PENDING,
editor_status: EDITOR_STATUS_PENDING,
auditor_status: EDITOR_STATUS_PENDING,
auditor_status: EDITOR_STATUS_PENDING,
sender_status: EDITOR_STATUS_PENDING,
sender_status: EDITOR_STATUS_PENDING,
...
@@ -1356,7 +1400,7 @@ export default function ProblemRectificationListPage() {
...
@@ -1356,7 +1400,7 @@ export default function ProblemRectificationListPage() {
} else {
} else {
const fillerProfile = await loadUser(nextFillerIds[0]);
const fillerProfile = await loadUser(nextFillerIds[0]);
const oaProcessResult = await executeAction('oa_notice:createProgress', {
const oaProcessResult = await executeAction('oa_notice:createProgress', {
title: `
$
{
dayjs
().
format
(
'YYYY-MM-DD'
)}
填写问题整改表
`,
title: `
$
{
dayjs
().
format
(
'YYYY-MM-DD'
)}
填写问题整改表
(
$
{
selectedRecordIds
.
length
}
条)
`,
classify: '流程待办',
classify: '流程待办',
contentType: '流程待办',
contentType: '流程待办',
receiveUserId: fillerProfile._id,
receiveUserId: fillerProfile._id,
...
@@ -1368,20 +1412,32 @@ export default function ProblemRectificationListPage() {
...
@@ -1368,20 +1412,32 @@ export default function ProblemRectificationListPage() {
returnUrl,
returnUrl,
});
});
progressIdEditor.push(oaProcessResult?.data?.taskId);
progressIdEditor.push(oaProcessResult?.data?.taskId);
const batchProgressIdEditor = oaProcessResult?.data?.taskId || '';
for (const selectedRecord of selectedRecords) {
const departmentItems = batchDepartmentItemsByRecordId.get(selectedRecord.id) || [];
const nextItems = departmentItems.map((item) => ({
...item,
progress_id_editor: batchProgressIdEditor,
}));
await batchUpdateRecords({
subject: PROBLEM_RECTIFICATION_SUBJECT,
filter: { _id: selectedRecord.id },
data: buildDepartmentParentBatchUpdateData(nextItems),
});
await batchUpdateRecords({
const savedRecord = await loadRecord(selectedRecord.id);
subject: PROBLEM_RECTIFICATION_SUBJECT,
const savedItems = normalizeDepartmentRectificationItems(savedRecord);
filter: {
const expectedItemIds = new Set(nextItems.map((item) => item.item_id));
_id: { $in: selectedRecordIds },
if (
},
savedItems.length !== nextItems.length ||
data: {
savedItems.some((item) => !expectedItemIds.has(item.item_id))
'metadata.fill_in_the_people': nextFillerIds,
) {
'metadata.review_people': nextAuditorIds,
throw new Error(
'metadata.audit_status': AUDIT_STATUS_PENDING,
`
批量推送明细写入失败:问题“
$
{
selectedRecord
.
title
||
selectedRecord
.
id
}
”预期
$
{
nextItems
.
length
}
条,后台实际返回
$
{
savedItems
.
length
}
条。
`,
'metadata.final_audit_status': AUDIT_STATUS_PENDING,
);
[`
metadata
.
$
{
DEPARTMENT_RECTIFICATION_FIELD
}
`]: [],
}
},
}
});
}
}
await updateRecord(messageRecord._id || messageRecord.name, {
await updateRecord(messageRecord._id || messageRecord.name, {
...
@@ -1410,7 +1466,7 @@ export default function ProblemRectificationListPage() {
...
@@ -1410,7 +1466,7 @@ export default function ProblemRectificationListPage() {
setSuccessMessage(
setSuccessMessage(
isDepartmentFlow
isDepartmentFlow
? `
推送成功,已生成
$
{
departmentAssignments
.
length
}
条部门整改明细,可从记录标题右侧展开查看。
`
? `
推送成功,已生成
$
{
departmentAssignments
.
length
}
条部门整改明细,可从记录标题右侧展开查看。
`
: `
推送成功,已将
$
{
selectedRecords
.
length
}
条问题发送给填写人。
`,
: `
推送成功,已将
$
{
selectedRecords
.
length
}
条问题
的
$
{
Array
.
from
(
batchDepartmentItemsByRecordId
.
values
()).
flat
().
length
}
条部门整改明细
发送给填写人。
`,
);
);
} catch (requestError) {
} catch (requestError) {
setError(requestError?.message || '推送问题整改表失败,请稍后重试。');
setError(requestError?.message || '推送问题整改表失败,请稍后重试。');
...
@@ -1636,7 +1692,23 @@ export default function ProblemRectificationListPage() {
...
@@ -1636,7 +1692,23 @@ export default function ProblemRectificationListPage() {
recordName={recordName}
recordName={recordName}
onClose={onClose}
onClose={onClose}
onDataChange={onDataChange}
onDataChange={onDataChange}
disabledFields={[DELAY_REQUEST_FIELD, DELAY_REQUEST_ITEMS_FIELD]}
disabledFields={[
DELAY_REQUEST_FIELD,
DELAY_REQUEST_ITEMS_FIELD,
'problem_severity_level',
]}
deriveFieldValues={({ fieldPath, record: nextRecord }) => {
const fieldName = getFieldNameFromPath(fieldPath);
if (!PROBLEM_SEVERITY_SOURCE_FIELDS.includes(fieldName)) {
return {};
}
return {
'metadata.problem_severity_level': calculateProblemSeverityLevel(
nextRecord?.metadata,
),
};
}}
preferredViewName={activeGridView?.title || activeGridView?.name}
preferredViewName={activeGridView?.title || activeGridView?.name}
authorizationView={activeGridView}
authorizationView={activeGridView}
/>
/>
...
@@ -1733,10 +1805,20 @@ export default function ProblemRectificationListPage() {
...
@@ -1733,10 +1805,20 @@ export default function ProblemRectificationListPage() {
fieldDef={fieldDef}
fieldDef={fieldDef}
value={formValues[fieldDef.name]}
value={formValues[fieldDef.name]}
onChange={(nextValue) =>
onChange={(nextValue) =>
setFormValues((current) => ({
setFormValues((current) => {
...current,
const nextValues = {
[fieldDef.name]: nextValue,
...current,
}))
[fieldDef.name]: nextValue,
};
return PROBLEM_SEVERITY_SOURCE_FIELDS.includes(fieldDef.name)
? {
...nextValues,
problem_severity_level:
calculateProblemSeverityLevel(nextValues),
}
: nextValues;
})
}
}
/>
/>
{FIELD_HINTS[fieldDef.name] ? (
{FIELD_HINTS[fieldDef.name] ? (
...
...
src/pages/problem_rectification/problem-rectification.business-flow.md
View file @
5a67a51e
This diff is collapsed.
Click to expand it.
src/pages/problem_rectification/problemRectificationDashboardService.js
View file @
5a67a51e
import
dayjs
from
'dayjs'
;
import
dayjs
from
'dayjs'
;
import
{
calculateProblemSeverityLevel
}
from
'./problemSeverity'
;
import
{
queryRecords
}
from
'@/services/DataService'
;
import
{
queryRecords
}
from
'@/services/DataService'
;
export
const
PROBLEM_RECTIFICATION_SUBJECT
=
'problem_rectification'
;
export
const
PROBLEM_RECTIFICATION_SUBJECT
=
'problem_rectification'
;
...
@@ -455,7 +456,7 @@ function normalizeProblemRecord(record) {
...
@@ -455,7 +456,7 @@ function normalizeProblemRecord(record) {
problemType
:
normalizeText
(
metadata
.
problem_type
),
problemType
:
normalizeText
(
metadata
.
problem_type
),
relatedIssueDomain
:
normalizeText
(
metadata
.
related_issue_domain
)
||
'未分类'
,
relatedIssueDomain
:
normalizeText
(
metadata
.
related_issue_domain
)
||
'未分类'
,
brief
:
normalizeText
(
metadata
.
problem_brief
)
||
'暂无问题简述'
,
brief
:
normalizeText
(
metadata
.
problem_brief
)
||
'暂无问题简述'
,
severityLevel
:
normalizeText
(
metadata
.
problem_severity_level
),
severityLevel
:
calculateProblemSeverityLevel
(
metadata
),
categoryLevel1
:
normalizeText
(
metadata
.
problem_category_level_1
),
categoryLevel1
:
normalizeText
(
metadata
.
problem_category_level_1
),
categoryLevel2
:
normalizeText
(
metadata
.
problem_category_level_2
),
categoryLevel2
:
normalizeText
(
metadata
.
problem_category_level_2
),
categoryLevel3
:
normalizeText
(
metadata
.
problem_category_level_3
),
categoryLevel3
:
normalizeText
(
metadata
.
problem_category_level_3
),
...
...
src/pages/problem_rectification/problemRectificationListService.js
View file @
5a67a51e
...
@@ -9,6 +9,7 @@ import {
...
@@ -9,6 +9,7 @@ import {
calculateRectificationQualityRating
,
calculateRectificationQualityRating
,
DEPARTMENT_GROUPS
,
DEPARTMENT_GROUPS
,
}
from
'./problemRectificationDashboardService'
;
}
from
'./problemRectificationDashboardService'
;
import
{
calculateProblemSeverityLevel
}
from
'./problemSeverity'
;
import
{
import
{
buildViewColumns
,
buildViewColumns
,
checkViewCondition
,
checkViewCondition
,
...
@@ -48,7 +49,6 @@ const LEVEL_ORDER = ['高', '中', '低'];
...
@@ -48,7 +49,6 @@ const LEVEL_ORDER = ['高', '中', '低'];
const
FILTER_FIELD_PATHS
=
{
const
FILTER_FIELD_PATHS
=
{
progress
:
'metadata.rectification_progress'
,
progress
:
'metadata.rectification_progress'
,
auditStatus
:
'metadata.audit_status'
,
auditStatus
:
'metadata.audit_status'
,
severityLevel
:
'metadata.problem_severity_level'
,
primaryResponsibleDepartment
:
'metadata.primary_responsible_department'
,
primaryResponsibleDepartment
:
'metadata.primary_responsible_department'
,
problemFrom
:
'metadata.problem_from'
,
problemFrom
:
'metadata.problem_from'
,
rootCause
:
'metadata.problem_root_cause'
,
rootCause
:
'metadata.problem_root_cause'
,
...
@@ -189,7 +189,13 @@ export function getProblemRectificationFlowTableUrl(messageName) {
...
@@ -189,7 +189,13 @@ export function getProblemRectificationFlowTableUrl(messageName) {
const
query
=
messageName
?
`?id=
${
encodeURIComponent
(
messageName
)}
`
:
''
;
const
query
=
messageName
?
`?id=
${
encodeURIComponent
(
messageName
)}
`
:
''
;
const
basePath
=
String
(
PROBLEM_RECTIFICATION_APP_BASE_PATH
||
'/'
)
const
basePath
=
String
(
PROBLEM_RECTIFICATION_APP_BASE_PATH
||
'/'
)
.
replace
(
/
\/
+$/
,
''
);
.
replace
(
/
\/
+$/
,
''
);
return
`
${
basePath
}${
PROBLEM_RECTIFICATION_FLOW_TABLE_PATH
}${
query
}
`
;
const
appPath
=
`
${
basePath
}${
PROBLEM_RECTIFICATION_FLOW_TABLE_PATH
}${
query
}
`
;
if
(
typeof
window
===
'undefined'
)
{
return
appPath
;
}
return
`
${
window
.
location
.
origin
}${
appPath
}
`
;
}
}
export
function
getProblemRectificationFormUrl
(
messageName
)
{
export
function
getProblemRectificationFormUrl
(
messageName
)
{
...
@@ -694,6 +700,40 @@ function buildInspectionReportMonthFilter(month) {
...
@@ -694,6 +700,40 @@ function buildInspectionReportMonthFilter(month) {
};
};
}
}
function
buildProblemSeverityFilter
(
severityLevel
)
{
if
(
!
LEVEL_ORDER
.
includes
(
severityLevel
))
{
return
{};
}
const
economicLossFilter
=
{
'metadata.has_economic_loss'
:
{
$in
:
AFFIRMATIVE_FILTER_VALUES
},
};
const
externalImpactFilter
=
{
'metadata.has_external_impact'
:
{
$in
:
AFFIRMATIVE_FILTER_VALUES
},
};
if
(
severityLevel
===
'高'
)
{
return
{
$or
:
[
economicLossFilter
,
externalImpactFilter
]
};
}
const
noHighSeverityCause
=
{
'metadata.has_economic_loss'
:
{
$nin
:
AFFIRMATIVE_FILTER_VALUES
},
'metadata.has_external_impact'
:
{
$nin
:
AFFIRMATIVE_FILTER_VALUES
},
};
if
(
severityLevel
===
'中'
)
{
return
{
...
noHighSeverityCause
,
'metadata.has_employee_accountability'
:
{
$in
:
AFFIRMATIVE_FILTER_VALUES
},
};
}
return
{
...
noHighSeverityCause
,
'metadata.has_employee_accountability'
:
{
$nin
:
AFFIRMATIVE_FILTER_VALUES
},
};
}
function
buildFocusFlagFilter
(
focusFlag
)
{
function
buildFocusFlagFilter
(
focusFlag
)
{
if
(
focusFlag
===
'consequence'
)
{
if
(
focusFlag
===
'consequence'
)
{
return
{
return
{
...
@@ -706,7 +746,7 @@ function buildFocusFlagFilter(focusFlag) {
...
@@ -706,7 +746,7 @@ function buildFocusFlagFilter(focusFlag) {
}
}
if
(
focusFlag
===
'high_severity'
)
{
if
(
focusFlag
===
'high_severity'
)
{
return
{
'metadata.problem_severity_level'
:
'高'
}
;
return
buildProblemSeverityFilter
(
'高'
)
;
}
}
if
(
focusFlag
===
'low_rating'
)
{
if
(
focusFlag
===
'low_rating'
)
{
...
@@ -828,6 +868,7 @@ export function buildProblemRectificationQueryFilter(filters = {}, gridView, sub
...
@@ -828,6 +868,7 @@ export function buildProblemRectificationQueryFilter(filters = {}, gridView, sub
buildViewConditionsFilter
(
gridView
?.
settings
?.
filters
),
buildViewConditionsFilter
(
gridView
?.
settings
?.
filters
),
...
uiFieldClauses
,
...
uiFieldClauses
,
buildInspectionReportMonthFilter
(
filters
?.
inspectionReportMonth
),
buildInspectionReportMonthFilter
(
filters
?.
inspectionReportMonth
),
buildProblemSeverityFilter
(
filters
?.
severityLevel
),
buildCalculatedQualityRatingFilter
(
filters
?.
overallRating
),
buildCalculatedQualityRatingFilter
(
filters
?.
overallRating
),
buildFocusFlagFilter
(
filters
?.
focusFlag
),
buildFocusFlagFilter
(
filters
?.
focusFlag
),
buildKeywordFilter
(
filters
?.
keyword
,
columns
),
buildKeywordFilter
(
filters
?.
keyword
,
columns
),
...
@@ -893,7 +934,7 @@ export function normalizeProblemRectificationRecord(record) {
...
@@ -893,7 +934,7 @@ export function normalizeProblemRectificationRecord(record) {
problemFrom
:
normalizeText
(
metadata
.
problem_from
),
problemFrom
:
normalizeText
(
metadata
.
problem_from
),
relatedIssueDomain
:
normalizeText
(
metadata
.
related_issue_domain
),
relatedIssueDomain
:
normalizeText
(
metadata
.
related_issue_domain
),
brief
:
normalizeText
(
metadata
.
problem_brief
),
brief
:
normalizeText
(
metadata
.
problem_brief
),
severityLevel
:
normalizeText
(
metadata
.
problem_severity_level
),
severityLevel
:
calculateProblemSeverityLevel
(
metadata
),
categoryLevel1
:
normalizeText
(
metadata
.
problem_category_level_1
),
categoryLevel1
:
normalizeText
(
metadata
.
problem_category_level_1
),
categoryLevel2
:
normalizeText
(
metadata
.
problem_category_level_2
),
categoryLevel2
:
normalizeText
(
metadata
.
problem_category_level_2
),
categoryLevel3
:
normalizeText
(
metadata
.
problem_category_level_3
),
categoryLevel3
:
normalizeText
(
metadata
.
problem_category_level_3
),
...
@@ -1375,10 +1416,15 @@ export function buildProblemRectificationCreateFormFields(gridView, subject) {
...
@@ -1375,10 +1416,15 @@ export function buildProblemRectificationCreateFormFields(gridView, subject) {
}
}
export
function
buildInitialCreateValues
(
createFields
)
{
export
function
buildInitialCreateValues
(
createFields
)
{
return
createFields
.
reduce
((
v
alues
,
field
)
=>
({
const
values
=
createFields
.
reduce
((
nextV
alues
,
field
)
=>
({
...
v
alues
,
...
nextV
alues
,
[
field
.
fieldDef
.
name
]:
emptyValueForField
(
field
.
fieldDef
),
[
field
.
fieldDef
.
name
]:
emptyValueForField
(
field
.
fieldDef
),
}),
{});
}),
{});
return
{
...
values
,
problem_severity_level
:
calculateProblemSeverityLevel
(
values
),
};
}
}
export
function
buildProblemRectificationCreatePayload
(
formValues
,
createFields
=
[])
{
export
function
buildProblemRectificationCreatePayload
(
formValues
,
createFields
=
[])
{
...
@@ -1400,6 +1446,9 @@ export function buildProblemRectificationCreatePayload(formValues, createFields
...
@@ -1400,6 +1446,9 @@ export function buildProblemRectificationCreatePayload(formValues, createFields
return
{
return
{
subject
:
PROBLEM_RECTIFICATION_SUBJECT
,
subject
:
PROBLEM_RECTIFICATION_SUBJECT
,
title
:
projectName
||
problemBrief
||
'问题整改记录'
,
title
:
projectName
||
problemBrief
||
'问题整改记录'
,
metadata
,
metadata
:
{
...
metadata
,
problem_severity_level
:
calculateProblemSeverityLevel
(
metadata
),
},
};
};
}
}
src/pages/problem_rectification/problemSeverity.js
0 → 100644
View file @
5a67a51e
import
{
normalizeText
}
from
'@/utils/subjectView'
;
const
AFFIRMATIVE_VALUES
=
new
Set
([
'是'
,
'有'
,
'涉及'
,
'TRUE'
,
'1'
,
'Y'
,
'YES'
]);
export
const
PROBLEM_SEVERITY_SOURCE_FIELDS
=
[
'has_economic_loss'
,
'has_external_impact'
,
'has_employee_accountability'
,
];
export
function
calculateProblemSeverityLevel
(
metadata
=
{})
{
const
isAffirmative
=
(
value
)
=>
AFFIRMATIVE_VALUES
.
has
(
normalizeText
(
value
).
toUpperCase
());
if
(
isAffirmative
(
metadata
.
has_economic_loss
)
||
isAffirmative
(
metadata
.
has_external_impact
)
)
{
return
'高'
;
}
if
(
isAffirmative
(
metadata
.
has_employee_accountability
))
{
return
'中'
;
}
return
'低'
;
}
src/services/subjectsDefinition.md
View file @
5a67a51e
...
@@ -155,6 +155,16 @@ type Record = {
...
@@ -155,6 +155,16 @@ type Record = {
| 部门延期申请明细 |
`problem_rectification_delay_request_items`
| object |
`[]`
| 否 | 是 | 否 | 否 | 否 |
| 部门延期申请明细 |
`problem_rectification_delay_request_items`
| object |
`[]`
| 否 | 是 | 否 | 否 | 否 |
| 状态锁定 |
`is_locked`
| boolean |
`false`
| 否 | 否 | 否 | 否 | 否 |
| 状态锁定 |
`is_locked`
| boolean |
`false`
| 否 | 否 | 否 | 否 | 否 |
#### 问题综合严重程度计算规则
`problem_severity_level`
由系统根据三个问题后果字段自动计算,不由用户手工选择:
-
`has_economic_loss`
或
`has_external_impact`
任一项为“是”时,结果为“高”
-
上述两项均不为“是”,仅
`has_employee_accountability`
为“是”时,结果为“中”
-
三项均不为“是”或未填写时,结果为“低”
创建记录和编辑上述任一来源字段时,计算结果写入
`metadata.problem_severity_level`
。列表与驾驶舱读取历史记录时也按相同规则即时计算。
### 3.1.1 select 字段选项
### 3.1.1 select 字段选项
-
`inspect_the_implementing_unit`
(检查实施单位):
`法律合规部`
、
`源泰律师事务所(合规有效性评估)`
、
`普华永道会计师事务所`
、
`风险管理部`
、
`内部审计`
、
`内部合规检查`
、
`外部审计`
、
`外部检查`
、
`总行审计`
、
`工会经费审査`
、
`国家审计署`
、
`鑫沅资产`
、
`鑫元基金`
、
`其他`
-
`inspect_the_implementing_unit`
(检查实施单位):
`法律合规部`
、
`源泰律师事务所(合规有效性评估)`
、
`普华永道会计师事务所`
、
`风险管理部`
、
`内部审计`
、
`内部合规检查`
、
`外部审计`
、
`外部检查`
、
`总行审计`
、
`工会经费审査`
、
`国家审计署`
、
`鑫沅资产`
、
`鑫元基金`
、
`其他`
...
@@ -177,7 +187,7 @@ type Record = {
...
@@ -177,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 条明细。
转发后
`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`
...
@@ -370,13 +380,13 @@ type Record = {
...
@@ -370,13 +380,13 @@ type Record = {
-
`reference_record_id`
与
`restriction_value`
保持一致,作为显式的“关联记录 ID”冗余字段
-
`reference_record_id`
与
`restriction_value`
保持一致,作为显式的“关联记录 ID”冗余字段
-
`subject_name`
保存被推送记录所属对象名,当前整改流程固定为
`problem_rectification`
-
`subject_name`
保存被推送记录所属对象名,当前整改流程固定为
`problem_rectification`
-
`flow_mode`
区分流程:
`department`
表示单条问题按责任部门生成整改明细行,
`batch`
表示多选问题的单填写人 / 单审批人简单推送,
`delay`
表示历史问题级延期申请,
`delay_department`
表示按责任部门生成延期申请明细
-
`flow_mode`
区分流程:
`department`
表示单条问题按责任部门生成整改明细行,
`batch`
表示多选问题的单填写人 / 单审批人简单推送,
`delay`
表示历史问题级延期申请,
`delay_department`
表示按责任部门生成延期申请明细
-
`department_item_ids`
保存本次
部门流程涉及的部门级
`item_id`
;
整改流程对应
`department_rectification_items[].item_id`
,延期流程对应
`problem_rectification_delay_request_items[].item_id`
-
`department_item_ids`
保存本次
流程涉及的部门级
`item_id`
;单条和多条
整改流程对应
`department_rectification_items[].item_id`
,延期流程对应
`problem_rectification_delay_request_items[].item_id`
-
部分线上
`message_reference_id`
对象会过滤未配置的
`flow_mode`
或
`department_item_ids`
;延期流程页须回退按关联问题
`problem_rectification_delay_request_items[].flow_record_id._id`
匹配当前流程主单
`_id`
,或按共享的
`progress_id_editor`
匹配,不能将其当作普通整改流程
-
部分线上
`message_reference_id`
对象会过滤未配置的
`flow_mode`
或
`department_item_ids`
;延期流程页须回退按关联问题
`problem_rectification_delay_request_items[].flow_record_id._id`
匹配当前流程主单
`_id`
,或按共享的
`progress_id_editor`
匹配,不能将其当作普通整改流程
-
`progress_id_editor`
、
`progress_id_auditor`
、
`progress_id_sender`
用于记录每一步用户确认动作生成的流程 ID,用来区分当前流程推进到了哪一步
-
批量流程的填写、复核、最终审批状态及待办 ID 直接使用流程主单顶层的
`editor_status`
、
`auditor_status`
、
`sender_status`
和
`progress_id_*`
,同一批次的全部问题共享流程状态
-
`editor_status`
:当前项目统一使用
`未完成`
、
`已完成`
-
`editor_status`
:当前项目统一使用
`未完成`
、
`已完成`
-
`auditor_status`
:当前项目统一使用
`未完成`
、
`已完成`
-
`auditor_status`
:当前项目统一使用
`未完成`
、
`已完成`
-
`sender_status`
:当前项目统一使用
`未完成`
、
`已完成`
-
`sender_status`
:当前项目统一使用
`未完成`
、
`已完成`
-
当
复核或最终审核驳回时,流程主单会回写新的
`progress_id_editor`
,并将
`editor_status`
重置为
`未完成`
,从而
重新进入填写阶段
-
当
批量流程复核或最终审核驳回时,流程主单
`editor_status`
重置为
`未完成`
并回写新的
`progress_id_editor`
,同一批次的全部问题共同
重新进入填写阶段
## 3.5 项目管理
## 3.5 项目管理
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment