做双端审批流

This commit is contained in:
hero920103 committed 2026-10-07 13:46:32 +08:00
1 parent 5b4dbb2b46
commit ffaadc26ae
69 files changed
+6662 -1071

No files matched your search

+171
View File
@@ -0,0 +1,171 @@
package controllers
import (
"fmt"
"time"
"server/models"
"server/services/approval"
"github.com/beego/beego/v2/client/orm"
)
// =============================================================
// 域名申请 × 审批流引擎 的接入层(严格模式,无单级兼容)
//
// 口径:
// - 域名申请提交必须命中已配置的默认流程,走引擎多级流转;
// - 未配置流程 / 节点无有效审批人 → 提交失败并回滚刚创建的域名行,绝不留下无法审批的脏数据;
// - 审核只能通过引擎:必须存在在途实例且命中当前审批人的待办,否则拒绝操作。
// =============================================================
// submitDomainApprovalChecked 将域名提交进平台侧审批流。
// 成功返回空串;失败则软删回滚刚创建的域名行,并返回给前端的错误文案。
func submitDomainApprovalChecked(domainRowID, bizTid, submitUID uint64, submitName, fullDomain string) string {
_, err := approval.Submit(approval.SubmitInput{
Scope: models.ApprovalScopePlatform,
BizTid: bizTid,
BizType: models.ApprovalBizDomain,
BizID: domainRowID,
BizTitle: fullDomain,
SubmitUID: submitUID,
SubmitName: submitName,
})
if err == nil {
return ""
}
// 提交失败:回滚刚创建的域名行(软删除),避免出现无法审批的待审僵尸数据
_, _ = models.Orm.QueryTable(new(models.SystemTenantDomain)).
Filter("id", domainRowID).Update(map[string]interface{}{"delete_time": time.Now()})
if _, ok := err.(*approval.ErrNoFlow); ok {
return "平台尚未配置域名审批流程,无法提交申请"
}
if _, ok := err.(*approval.ErrNoApprover); ok {
return "域名审批流程未配置有效审批人,无法提交申请"
}
return "提交审批失败:" + err.Error()
}
// findRunningDomainInstance 取某域名当前在途的平台侧审批实例
func findRunningDomainInstance(domainID uint64) (*models.ApprovalInstance, bool) {
var inst models.ApprovalInstance
err := models.Orm.QueryTable(new(models.ApprovalInstance)).
Filter("scope", models.ApprovalScopePlatform).
Filter("biz_type", models.ApprovalBizDomain).
Filter("biz_id", domainID).
Filter("status", models.ApprovalInstanceRunning).
Filter("is_deleted", 0).
OrderBy("-id").One(&inst)
if err == orm.ErrNoRows {
return nil, false
}
if err != nil {
return nil, false
}
return &inst, true
}
// myPendingDomainTask 取指定审批人在该实例上的当前待办任务
func myPendingDomainTask(instID, approverID uint64) (*models.ApprovalTask, bool) {
var task models.ApprovalTask
err := models.Orm.QueryTable(new(models.ApprovalTask)).
Filter("instance_id", instID).
Filter("approver_id", approverID).
Filter("status", models.ApprovalTaskPending).
Filter("is_deleted", 0).
OrderBy("sort", "id").One(&task)
if err != nil {
return nil, false
}
return &task, true
}
// applyDomainApprovalResult 依据引擎结果回写域名状态(仅实例终结时改动)
func applyDomainApprovalResult(domainID uint64, res *approval.ApproveResult) {
if res == nil || !res.Finished {
return
}
newStatus := 2 // 默认拒绝/驳回
if res.InstanceStat == models.ApprovalInstanceApproved {
newStatus = 1 // 通过生效
}
now := time.Now()
_, _ = models.Orm.QueryTable(new(models.SystemTenantDomain)).Filter("id", domainID).
Update(orm.Params{"status": newStatus, "update_time": now})
}
// domainApprovalProgress 组装域名审批进度(节点 + 任务),供平台端展示
func domainApprovalProgress(domainID uint64) map[string]interface{} {
inst, ok := findRunningDomainInstance(domainID)
if !ok {
// 无在途实例:可能未走引擎(单级审核)或已结束
return map[string]interface{}{"engine": false}
}
flow, err := approval.LoadFlow(models.ApprovalScopePlatform, 0, models.ApprovalBizDomain, inst.FlowID)
nodes := []map[string]interface{}{}
if err == nil && flow != nil {
for _, nd := range flow.Nodes {
nodes = append(nodes, map[string]interface{}{
"node_key": nd.NodeKey,
"node_name": nd.NodeName,
"node_type": nd.NodeType,
"sort": nd.Sort,
})
}
}
var tasks []models.ApprovalTask
_, _ = models.Orm.QueryTable(new(models.ApprovalTask)).
Filter("instance_id", inst.ID).Filter("is_deleted", 0).
OrderBy("sort", "id").All(&tasks)
taskList := make([]map[string]interface{}, 0, len(tasks))
for _, t := range tasks {
taskList = append(taskList, map[string]interface{}{
"id": t.ID,
"node_key": t.NodeKey,
"node_name": t.NodeName,
"approver_id": t.ApproverID,
"approver_name": t.ApproverName,
"status": t.Status,
"action": t.Action,
"comment": t.Comment,
})
}
return map[string]interface{}{
"engine": true,
"instance_id": inst.ID,
"current_node": inst.CurrentNode,
"status": inst.Status,
"nodes": nodes,
"tasks": taskList,
}
}
// engineDomainAudit 平台端审批入口(严格引擎驱动):
// 必须存在在途实例且命中当前审批人的待办;否则拒绝,不做任何单级回退。
func engineDomainAudit(domainID, adminID uint64, pass bool, comment string, bizTid uint64) (msg string, code int) {
inst, ok := findRunningDomainInstance(domainID)
if !ok {
return "该域名申请未进入审批流程(未配置流程或已处理)", 400
}
task, ok := myPendingDomainTask(inst.ID, adminID)
if !ok {
return "该单据在审批流程中,当前不在您的待办节点", 403
}
res, err := approval.Approve(task.ID, approval.ApproveInput{
ApproverID: adminID,
Pass: pass,
Comment: comment,
BizTid: bizTid,
})
if err != nil {
return err.Error(), 400
}
applyDomainApprovalResult(domainID, res)
if res.Finished {
if res.InstanceStat == models.ApprovalInstanceApproved {
return "审批通过,域名已生效", 200
}
return "已驳回", 200
}
return fmt.Sprintf("已%s,流程继续(%s)", map[bool]string{true: "通过", false: "驳回"}[pass], res.Message), 200
}