优化审批流

This commit is contained in:
hero920103 committed 2026-10-07 22:56:35 +08:00
1 parent ffaadc26ae
commit 438145cae7
17 files changed
+1131 -187

No files matched your search

+137 -7
View File
@@ -568,6 +568,99 @@ func dedupeApprovers(in []Approver) []Approver {
return out
}
// ============================ 运行时:发起前的可用流查询 ============================
// AvailableFlow 业务侧选择用的轻量结构
type AvailableFlow struct {
FlowID uint64 `json:"flow_id"`
FlowCode string `json:"flow_code"`
FlowName string `json:"flow_name"`
BizType string `json:"biz_type"`
}
// ListAvailableFlows 按 bizType + 用户部门 过滤“当前可用”审批流。
// 直接查询 flow 表中 biz_type 匹配且 status=1 未删除的流程。
// 租户侧额外按 apply_dept_ids 部门过滤。
// 平台侧(scope=2)忽略部门过滤(因平台 flow 无 apply_dept_ids)。
func ListAvailableFlows(scope int8, tid uint64, bizType string, userOrgID uint64) ([]AvailableFlow, error) {
// 租户侧预计算一次当前用户组织的“祖先链”(含自身),选中上级部门或公司均命中
var path []uint64
if scope == models.ApprovalScopeTenant && userOrgID > 0 {
path = buildOrgPath(tid, userOrgID)
}
out := make([]AvailableFlow, 0)
if scope == models.ApprovalScopePlatform {
var rows []models.PlatformApprovalFlow
_, _ = models.Orm.QueryTable(new(models.PlatformApprovalFlow)).
Filter("biz_type", bizType).Filter("is_deleted", 0).Filter("status", 1).
OrderBy("id").All(&rows)
for _, f := range rows {
out = append(out, AvailableFlow{FlowID: f.ID, FlowCode: f.FlowCode, FlowName: f.FlowName, BizType: f.BizType})
}
return out, nil
}
var rows []models.TenantApprovalFlow
_, _ = models.Orm.QueryTable(new(models.TenantApprovalFlow)).
Filter("tid", tid).Filter("biz_type", bizType).Filter("is_deleted", 0).Filter("status", 1).
OrderBy("id").All(&rows)
for _, f := range rows {
if !deptMatch(f.ApplyDeptIDs, path) {
continue
}
out = append(out, AvailableFlow{FlowID: f.ID, FlowCode: f.FlowCode, FlowName: f.FlowName, BizType: f.BizType})
}
return out, nil
}
// deptMatch csv 空视为全公司通用;否则与 path (包含自身与祖先部门ID) 任一命中即命中。
func deptMatch(csv string, path []uint64) bool {
csv = strings.TrimSpace(csv)
if csv == "" {
return true
}
if len(path) == 0 {
return false
}
inPath := make(map[uint64]bool, len(path))
for _, id := range path {
inPath[id] = true
}
for _, part := range strings.Split(csv, ",") {
if v, err := strconv.ParseUint(strings.TrimSpace(part), 10, 64); err == nil && inPath[v] {
return true
}
}
return false
}
// buildOrgPath 从当前部门沿 parent_id 向上追到根,返回含自身的 ID 链(环保护)
func buildOrgPath(tid, orgID uint64) []uint64 {
if orgID == 0 {
return nil
}
var all []models.BackendOrganization
_, _ = models.Orm.QueryTable(new(models.BackendOrganization)).
Filter("tid", tid).Filter("delete_time__isnull", true).
All(&all, "id", "parent_id")
parent := make(map[uint64]uint64, len(all))
for _, o := range all {
parent[o.ID] = o.ParentID
}
out := []uint64{orgID}
seen := map[uint64]bool{orgID: true}
cur := orgID
for {
p, ok := parent[cur]
if !ok || p == 0 || seen[p] {
break
}
out = append(out, p)
seen[p] = true
cur = p
}
return out
}
// ============================ 运行时:提交 ============================
// Submit 提交单据进入审批,创建实例并按图展开首个可执行节点的任务。
@@ -614,7 +707,8 @@ func Submit(in SubmitInput) (uint64, error) {
start := flow.ByKey[flow.StartKey]
hasApproval, firstApprovalName, firstApprovalKey := anyApprovalNode(flow)
node, approvers := nextExecutable(flow, in.FormData, start, in)
notifyFn := func(n *Node) { fireNotifyTasks(uint64(instID), in, n) }
node, approvers := nextExecutable(flow, in.FormData, start, in, notifyFn)
if node == nil {
// 无可执行审批节点
if hasApproval {
@@ -647,8 +741,9 @@ func anyApprovalNode(flow *Flow) (bool, string, string) {
return false, "", ""
}
// advanceFrom 从 cur 沿出边前进,遇条件网关求值选路,返回首个审批节点(nil 表示到终点)
func advanceFrom(flow *Flow, formData map[string]interface{}, cur *Node) *Node {
// advanceFrom 从 cur 沿出边前进,遇条件网关求值选路,返回首个审批节点(nil 表示到终点)。
// onNotify 可选回调:当路径经过通知节点时触发,由调用方负责创建通知任务。
func advanceFrom(flow *Flow, formData map[string]interface{}, cur *Node, onNotify ...func(*Node)) *Node {
visited := make(map[string]bool)
node := pickNext(flow, formData, cur)
for node != nil {
@@ -661,6 +756,11 @@ func advanceFrom(flow *Flow, formData map[string]interface{}, cur *Node) *Node {
return node
case models.ApprovalNodeEnd:
return nil
case models.ApprovalNodeNotify:
if len(onNotify) > 0 && onNotify[0] != nil {
onNotify[0](node)
}
node = pickNext(flow, formData, node)
default: // start / condition:继续向后
node = pickNext(flow, formData, node)
}
@@ -700,8 +800,9 @@ func pickNext(flow *Flow, formData map[string]interface{}, node *Node) *Node {
}
// nextExecutable 沿图找到下一个「有有效审批人」的审批节点;无人审批节点自动跳过
func nextExecutable(flow *Flow, formData map[string]interface{}, cur *Node, in SubmitInput) (*Node, []Approver) {
node := advanceFrom(flow, formData, cur)
// nextExecutable 获取下一个可执行审批节点及其审批人(跳过通知节点并通过 onNotify 回调触发)
func nextExecutable(flow *Flow, formData map[string]interface{}, cur *Node, in SubmitInput, onNotify ...func(*Node)) (*Node, []Approver) {
node := advanceFrom(flow, formData, cur, onNotify...)
visited := make(map[string]bool)
for node != nil {
if visited[node.NodeKey] {
@@ -715,7 +816,7 @@ func nextExecutable(flow *Flow, formData map[string]interface{}, cur *Node, in S
return node, approvers
}
}
node = advanceFrom(flow, formData, node)
node = advanceFrom(flow, formData, node, onNotify...)
}
return nil, nil
}
@@ -792,6 +893,34 @@ func createTasks(instID uint64, in SubmitInput, node Node, approvers []Approver)
return err
}
// fireNotifyTasks 为通知节点创建已读通知任务(不阻塞流程)
func fireNotifyTasks(instID uint64, in SubmitInput, notifyNode *Node) {
recipients, _ := resolveApprovers(in.Scope, *notifyNode, in)
recipients = dedupeApprovers(recipients)
if len(recipients) == 0 {
return
}
now := time.Now()
tasks := make([]models.ApprovalTask, 0, len(recipients))
for _, r := range recipients {
tasks = append(tasks, models.ApprovalTask{
InstanceID: instID,
Scope: in.Scope,
Tid: in.Tid,
BizType: in.BizType,
BizID: in.BizID,
NodeKey: notifyNode.NodeKey,
NodeName: notifyNode.NodeName,
Sort: notifyNode.Sort,
ApproverID: r.ID,
ApproverName: r.Name,
Status: models.ApprovalTaskNotified,
FinishTime: &now,
})
}
_, _ = models.Orm.InsertMulti(len(tasks), tasks)
}
// finishInstance 结束实例
func finishInstance(instID uint64, status int8) {
now := time.Now()
@@ -947,7 +1076,8 @@ func advance(instID uint64, currentKey string, in ApproveInput) (*ApproveResult,
formData := instanceFormData(&inst)
si := submitFromInstance(&inst, formData)
cur := flow.ByKey[currentKey]
node, approvers := nextExecutable(flow, formData, cur, si)
notifyFn := func(n *Node) { fireNotifyTasks(instID, si, n) }
node, approvers := nextExecutable(flow, formData, cur, si, notifyFn)
if node == nil {
finishInstance(instID, models.ApprovalInstanceApproved)
return &ApproveResult{