Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
M
model_analysis
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
model_analysis
Commits
c0151ca8
Commit
c0151ca8
authored
Sep 17, 2026
by
drigle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add forward OA and app notifications
parent
a2314f0c
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
143 additions
and
5 deletions
+143
-5
ForwardApprovals.jsx
src/pages/project_manager_xy/ForwardApprovals.jsx
+25
-1
ModelBatchData.jsx
src/pages/project_manager_xy/ModelBatchData.jsx
+5
-0
forwardNotificationService.js
src/pages/project_manager_xy/forwardNotificationService.js
+0
-0
model-monitor.business-flow.md
src/pages/project_manager_xy/model-monitor.business-flow.md
+7
-3
modelMonitorService.js
src/pages/project_manager_xy/modelMonitorService.js
+43
-0
record-api.md
src/services/record-api.md
+59
-0
subjectsDefinition.md
src/services/subjectsDefinition.md
+4
-1
No files found.
src/pages/project_manager_xy/ForwardApprovals.jsx
View file @
c0151ca8
...
...
@@ -27,6 +27,7 @@ import {
loadForwardEditableRecords
,
loadForwardRecords
,
saveForwardEditableRecords
,
dispatchForwardStateNotification
,
updateForwardRecordStatus
,
}
from
'./modelMonitorService'
;
import
{
useContext
,
useEffect
,
useMemo
,
useState
}
from
'react'
;
...
...
@@ -94,6 +95,7 @@ export default function ForwardApprovals() {
const
[
editSaving
,
setEditSaving
]
=
useState
(
false
);
const
[
error
,
setError
]
=
useState
(
''
);
const
[
notice
,
setNotice
]
=
useState
(
''
);
const
[
notificationWarning
,
setNotificationWarning
]
=
useState
(
''
);
const
[
activeRecordId
,
setActiveRecordId
]
=
useState
(
''
);
const
[
isDetailDialogOpen
,
setIsDetailDialogOpen
]
=
useState
(
false
);
const
[
isEditDialogOpen
,
setIsEditDialogOpen
]
=
useState
(
false
);
...
...
@@ -186,8 +188,22 @@ export default function ForwardApprovals() {
async
function
handleUpdateRecord
(
record
,
nextStatuses
)
{
setSaving
(
true
);
setNotificationWarning
(
''
);
try
{
await
updateForwardRecordStatus
(
record
.
recordName
,
nextStatuses
);
const
updatedRecord
=
await
updateForwardRecordStatus
(
record
.
recordName
,
nextStatuses
);
const
notificationEvent
=
nextStatuses
.
approval_status
===
'approved'
?
'first_approval_pending'
:
nextStatuses
.
final_approval_status
===
'approved'
?
'final_approval_pending'
:
nextStatuses
.
approval_status
===
'rejected'
?
'first_rejected'
:
nextStatuses
.
final_approval_status
===
'rejected'
?
'final_rejected'
:
''
;
const
notificationResult
=
notificationEvent
?
await
dispatchForwardStateNotification
(
notificationEvent
,
updatedRecord
,
userInfo
)
:
{
warning
:
''
};
setNotificationWarning
(
notificationResult
.
warning
);
await
refreshRecords
(
'审批状态已更新。'
);
setError
(
''
);
}
catch
(
updateError
)
{
...
...
@@ -287,6 +303,7 @@ export default function ForwardApprovals() {
}
setEditSaving
(
true
);
setNotificationWarning
(
''
);
try
{
if
(
editableRecords
.
length
>
0
)
{
const
saved
=
await
saveForwardEditableRecords
(
...
...
@@ -309,6 +326,12 @@ export default function ForwardApprovals() {
});
setEditingRecord
(
updatedForwardRecord
);
const
notificationResult
=
await
dispatchForwardStateNotification
(
'first_approval_pending'
,
updatedForwardRecord
,
userInfo
,
);
setNotificationWarning
(
notificationResult
.
warning
);
setIsEditDialogOpen
(
false
);
await
refreshRecords
(
'填写已完成,当前批次已进入待审批。'
);
setError
(
''
);
...
...
@@ -460,6 +483,7 @@ export default function ForwardApprovals() {
{
loading
?
<
NoticeBanner
>
正在加载转发审批记录...
</
NoticeBanner
>
:
null
}
{
!
loading
&&
error
?
<
NoticeBanner
tone=
"error"
>
{
error
}
</
NoticeBanner
>
:
null
}
{
!
error
&&
notice
?
<
NoticeBanner
tone=
"success"
>
{
notice
}
</
NoticeBanner
>
:
null
}
{
notificationWarning
?
<
NoticeBanner
>
{
notificationWarning
}
</
NoticeBanner
>
:
null
}
<
div
className=
"min-w-0 max-w-full"
>
<
Tabs
className=
"w-fit max-w-full min-w-0"
selectedKey=
{
activeFilter
}
variant=
"primary"
onSelectionChange=
{
(
key
)
=>
{
setActiveFilter
(
String
(
key
));
setPage
(
0
);
}
}
>
...
...
src/pages/project_manager_xy/ModelBatchData.jsx
View file @
c0151ca8
...
...
@@ -39,6 +39,7 @@ import {
loadModelBatch
,
resolveForwardRowStatus
,
saveForwardRecord
,
dispatchForwardStateNotification
,
updateBatchHiddenRecords
,
}
from
'./modelMonitorService'
;
...
...
@@ -60,6 +61,7 @@ export default function ModelBatchData() {
const
[
saving
,
setSaving
]
=
useState
(
false
);
const
[
error
,
setError
]
=
useState
(
''
);
const
[
notice
,
setNotice
]
=
useState
(
''
);
const
[
notificationWarning
,
setNotificationWarning
]
=
useState
(
''
);
const
[
isHiddenDialogOpen
,
setIsHiddenDialogOpen
]
=
useState
(
false
);
const
[
isForwardDialogOpen
,
setIsForwardDialogOpen
]
=
useState
(
false
);
const
[
forwardForm
,
setForwardForm
]
=
useState
(
createEmptyForwardDraft
);
...
...
@@ -209,6 +211,8 @@ export default function ModelBatchData() {
},
userInfo
);
const
deliverRecords
=
selectedRecords
.
filter
((
item
)
=>
deliverIds
.
includes
(
item
.
recordName
||
item
.
id
));
await
ensureForwardRecordItems
(
record
,
deliverRecords
,
userInfo
);
const
notificationResult
=
await
dispatchForwardStateNotification
(
'requested'
,
record
,
userInfo
);
setNotificationWarning
(
notificationResult
.
warning
);
await
refresh
();
setSelectedRecordIds
([]);
setIsForwardDialogOpen
(
false
);
...
...
@@ -227,6 +231,7 @@ export default function ModelBatchData() {
{
loading
?
<
NoticeBanner
>
正在加载批次数据和转发状态...
</
NoticeBanner
>
:
null
}
{
error
?
<
NoticeBanner
tone=
"error"
>
{
error
}
</
NoticeBanner
>
:
null
}
{
!
error
&&
notice
?
<
NoticeBanner
tone=
"success"
>
{
notice
}
</
NoticeBanner
>
:
null
}
{
notificationWarning
?
<
NoticeBanner
>
{
notificationWarning
}
</
NoticeBanner
>
:
null
}
{
batch
?.
syncStatus
===
'partial'
||
batch
?.
syncStatus
===
'failed'
?
<
NoticeBanner
tone=
"error"
>
最近一次同步未完整成功:
{
batch
.
errorMessage
||
'该批次不可重试,请联系管理员排查。'
}
</
NoticeBanner
>
:
null
}
{
!
loading
&&
batch
?
(
...
...
src/pages/project_manager_xy/forwardNotificationService.js
0 → 100644
View file @
c0151ca8
This diff is collapsed.
Click to expand it.
src/pages/project_manager_xy/model-monitor.business-flow.md
View file @
c0151ca8
...
...
@@ -132,7 +132,8 @@ MySQL 联调数据可通过 `scripts/mysql/model-monitor-source-fixtures.sql`
3.
填写接收人、转发说明、一级审批人和终审人。
4.
创建
`model_monitor_forward_record`
,并为实际下发记录创建
`model_monitor_forward_item`
;发起时不写入尚未填写的是否属实、整改方案和预计整改完成时间字段。
5.
初始化
`editor_status = editing`
、
`approval_status = pending`
、
`final_approval_status = pending`
。
6.
未结束流程中的
`excluded_record_ids`
在来源批次页暂时隐藏,终审通过后恢复展示。
6.
发起成功后为每位接收人创建 OA 流程待办,并给接收人发送站内通知;接收人 OA 任务 ID 写入
`oa_recipient_task_ids`
。
7.
未结束流程中的
`excluded_record_ids`
在来源批次页暂时隐藏,终审通过后恢复展示。
### 4.8 填写与两级审批
...
...
@@ -143,8 +144,11 @@ MySQL 联调数据可通过 `scripts/mysql/model-monitor-source-fixtures.sql`
5.
一级审批人通过或驳回;只有一级通过后终审人才可操作。
6.
任一审批驳回后,填写状态恢复为
`editing`
,清空上次完成人和完成时间,接收人可修改原回填内容。
7.
接收人重新完成填写时,
`approval_status`
和
`final_approval_status`
均重置为
`pending`
,流程从一级审批重新开始。
8.
终审通过后状态为已最终审批,流程结束。
9.
接收人填写、完成填写、一级审批和终审在前端同时执行按钮禁用与提交事件校验;用户身份或流程状态不满足时不发送更新请求,并提示状态已经变化或当前用户无权操作。
8.
终审通过后状态为已最终审批,流程结束;终审 OA 待办关闭,并给发起人、接收人和两级审批人发送结束通知。
9.
接收人完成填写后关闭接收人 OA 待办,给一级审批人发送站内通知并创建其 OA 待办;一级通过后关闭一级 OA 待办,给终审人发送站内通知并创建其 OA 待办。
10.
一级或终审驳回后关闭当前环节 OA 待办,重新为接收人创建 OA 待办,并向接收人和发起人发送驳回站内通知。
11.
接收人填写、完成填写、一级审批和终审在前端同时执行按钮禁用与提交事件校验;用户身份或流程状态不满足时不发送更新请求,并提示状态已经变化或当前用户无权操作。
12.
通知和 OA 调用发生在业务状态成功更新之后;单个通知失败不会回滚填写、审批或转发状态,页面会显示“业务状态已更新,但通知处理存在异常”的提示,便于补发或排查。
## 5. 页面状态与异常处理
...
...
src/pages/project_manager_xy/modelMonitorService.js
View file @
c0151ca8
...
...
@@ -17,6 +17,10 @@ import {
listSourceModels
,
loadSourceBatchData
,
}
from
'./ModelMonitorConnectorService'
;
import
{
dispatchForwardNotification
,
formatForwardNotificationIssues
,
}
from
'./forwardNotificationService'
;
export
const
MODEL_GROUP_SUBJECT
=
'model_monitor_group'
;
export
const
MODEL_SUBJECT
=
'model_monitor_model'
;
...
...
@@ -172,6 +176,9 @@ const FIXED_SUBJECTS = [
{
name
:
'final_approver_name'
,
label
:
'终审人名称'
,
type
:
'text'
},
{
name
:
'initiator_id'
,
label
:
'发起人 ID'
,
type
:
'text'
},
{
name
:
'initiator_name'
,
label
:
'发起人名称'
,
type
:
'text'
},
{
name
:
'oa_recipient_task_ids'
,
label
:
'接收人 OA 任务 ID'
,
type
:
'text'
,
multiple
:
true
},
{
name
:
'oa_first_approver_task_id'
,
label
:
'一级审批人 OA 任务 ID'
,
type
:
'text'
},
{
name
:
'oa_final_approver_task_id'
,
label
:
'终审人 OA 任务 ID'
,
type
:
'text'
},
],
},
{
...
...
@@ -429,6 +436,11 @@ function normalizeForwardRecord(record = {}) {
finalApproverName
:
metadata
.
final_approver_name
||
''
,
initiatorId
:
metadata
.
initiator_id
||
''
,
initiatorName
:
metadata
.
initiator_name
||
''
,
oaRecipientTaskIds
:
Array
.
isArray
(
metadata
.
oa_recipient_task_ids
)
?
metadata
.
oa_recipient_task_ids
:
[],
oaFirstApproverTaskId
:
metadata
.
oa_first_approver_task_id
||
''
,
oaFinalApproverTaskId
:
metadata
.
oa_final_approver_task_id
||
''
,
createdAt
:
record
.
created_at
||
''
,
updatedAt
:
record
.
updated_at
||
''
,
};
...
...
@@ -1141,6 +1153,9 @@ function createForwardPayload(values, user) {
final_approver_name
:
values
.
finalApproverName
||
''
,
initiator_id
:
values
.
initiatorId
||
actor
.
id
,
initiator_name
:
values
.
initiatorName
||
actor
.
name
,
oa_recipient_task_ids
:
values
.
oaRecipientTaskIds
||
[],
oa_first_approver_task_id
:
values
.
oaFirstApproverTaskId
||
''
,
oa_final_approver_task_id
:
values
.
oaFinalApproverTaskId
||
''
,
},
};
}
...
...
@@ -1157,6 +1172,29 @@ export async function saveForwardRecord(values, user) {
return
normalizeForwardRecord
(
unwrapRecord
(
await
createRecord
(
createForwardPayload
(
values
,
user
))));
}
async
function
saveForwardNotificationTasks
(
recordName
,
taskUpdates
=
{})
{
if
(
!
Object
.
keys
(
taskUpdates
).
length
)
return
normalizeForwardRecord
(
await
loadRecord
(
recordName
));
const
current
=
normalizeForwardRecord
(
await
loadRecord
(
recordName
));
return
normalizeForwardRecord
(
unwrapRecord
(
await
updateRecord
(
recordName
,
createForwardPayload
({
...
current
,
...
taskUpdates
,
}))));
}
async
function
dispatchForwardStateNotification
(
event
,
record
,
user
)
{
const
result
=
await
dispatchForwardNotification
(
event
,
record
,
user
);
let
updatedRecord
=
record
;
try
{
updatedRecord
=
await
saveForwardNotificationTasks
(
record
.
recordName
,
result
.
taskUpdates
);
}
catch
(
saveError
)
{
result
.
errors
.
push
(
saveError
?.
message
||
'OA 任务标识保存失败'
);
}
return
{
record
:
updatedRecord
,
warning
:
formatForwardNotificationIssues
(
result
),
};
}
function
createForwardItemPayload
(
values
,
user
)
{
const
actor
=
currentUserValues
(
user
);
const
hasFill
=
Boolean
(
values
.
truthStatus
||
values
.
rectificationPlan
||
values
.
expectedRectificationCompletedAt
);
...
...
@@ -1309,3 +1347,8 @@ export async function updateForwardRecordStatus(recordName, nextMetadata = {}) {
});
return
normalizeForwardRecord
(
unwrapRecord
(
await
updateRecord
(
recordName
,
payload
)));
}
export
{
dispatchForwardStateNotification
,
formatForwardNotificationIssues
,
};
src/services/record-api.md
View file @
c0151ca8
...
...
@@ -285,3 +285,62 @@ Navbar 读取真实未读数;0 不显示 Badge,1-99 显示原值,100 及
```
`:action`
与
`:methodName`
必须来自通知返回的动态
`actions`
配置,前端不得写死流程动作。
## 8. 模型监控流程通知
模型监控转发流程复用平台通知与已配置的
`oa_notice`
Action。业务 Record 状态更新成功后才调用本节接口;接口异常不回滚业务状态,但页面必须展示通知异常摘要。
### 8.1 创建站内通知
`POST /notice`
```
json
{
"title"
:
"模型监控数据填写"
,
"content"
:
"接收人已收到转发,请打开:https://example.com/model_analysis/models/forwards"
,
"type"
:
"internal"
,
"category"
:
"message"
,
"from"
:
"发起人 _id"
,
"to"
:
[
"接收人 _id"
],
"settings"
:
{
"url"
:
"/model_analysis/models/forwards"
}
}
```
### 8.2 发送站内通知
`PUT /notice/{name}/send`
请求路径中的
`name`
必须来自
`POST /notice`
返回值。
`internal`
通知不可重复发送。
### 8.3 创建 OA 待办
`POST /action/execute/oa_notice:createProgress`
```
json
{
"title"
:
"模型监控数据填写:模型 / 批次"
,
"classify"
:
"流程待办"
,
"contentType"
:
"流程待办"
,
"receiveUserId"
:
"接收人 _id"
,
"receiveUserName"
:
"接收人账号"
,
"receiveDisplayName"
:
"接收人显示名"
,
"sendUserId"
:
"发起人 _id"
,
"sendUserName"
:
"发起人账号"
,
"sendDisplayName"
:
"发起人显示名"
,
"returnUrl"
:
"https://example.com/model_analysis/models/forwards"
}
```
成功结果必须包含
`taskId`
,并按环节保存到
`model_monitor_forward_record.metadata.oa_recipient_task_ids`
、
`oa_first_approver_task_id`
或
`oa_final_approver_task_id`
。
### 8.4 关闭 OA 待办
`POST /action/execute/oa_notice:updateProgressStatus`
```
json
{
"taskId"
:
"待关闭的 OA 任务 ID"
}
```
src/services/subjectsDefinition.md
View file @
c0151ca8
...
...
@@ -150,7 +150,7 @@ type Record = {
### `model_monitor_forward_record`:模型转发审批记录
-
类型:
`normal`
-
字段数量:2
3
-
字段数量:2
6
-
说明:模型转发审批记录自动配置
| 字段标签 | 字段别名 | 字段类型 | 多值 | 必填 | 主键 | 关联/配置 |
...
...
@@ -178,6 +178,9 @@ type Record = {
| 终审人名称 |
`final_approver_name`
| text | 否 | 否 | 否 | |
| 发起人 ID |
`initiator_id`
| text | 否 | 否 | 否 | |
| 发起人名称 |
`initiator_name`
| text | 否 | 否 | 否 | |
| 接收人 OA 任务 ID |
`oa_recipient_task_ids`
| text | 是 | 否 | 否 | |
| 一级审批人 OA 任务 ID |
`oa_first_approver_task_id`
| text | 否 | 否 | 否 | |
| 终审人 OA 任务 ID |
`oa_final_approver_task_id`
| text | 否 | 否 | 否 | |
### `model_monitor_forward_item`:模型转发回填明细
...
...
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