This commit is contained in:
hero920103 committed 2026-10-19 01:13:17 +08:00
1 parent 5718a00e31
commit 1850ac640e
226 files changed
+37772 -12071

No files matched your search

+151
View File
@@ -0,0 +1,151 @@
package controllers
import (
"fmt"
"time"
"server/models"
"server/pkg/jwtutil"
"server/services"
approvalEngine "server/services/approval"
"github.com/beego/beego/v2/client/orm"
)
// reimburse_approval_core.go 报销单「审批人视角」操作共享逻辑。
//
// Web 端(/backend/reimbursements/approve|reject|my-approve-task)与
// 小程序端(/api/oa/expense/approve|reject|my-approve-task)共用本文件;
// 审批中心 TaskAct 也复用其中的 biz_type 判定与时间线回写。
// isReimburseBizType 判断审批任务/实例是否属于报销类单据。
func isReimburseBizType(bizType string) bool {
return approvalEngine.IsReimburseBizType(bizType)
}
// claimsReimburseUIDs 当前登录用户可能出现在 approver_id 上的两种口径:
// 认证中心 identity_id 与本租户 uid(历史上两种写法并存,这里都兼容)。
func claimsReimburseUIDs(claims *jwtutil.Claims) []interface{} {
raw := uint64(claims.UserID)
out := []interface{}{raw}
if uid := services.ResolveTenantUID(uint64(claims.TenantId), raw); uid != raw {
out = append(out, uid)
}
return out
}
// reimburseMyTaskCore 查当前用户在报销单 id 上的待办审批任务;无待办返回 (nil, nil)。
func reimburseMyTaskCore(claims *jwtutil.Claims, id uint64) (*models.ApprovalTask, error) {
tid := uint64(claims.TenantId)
var task models.ApprovalTask
err := models.Orm.QueryTable(new(models.ApprovalTask)).
Filter("scope", models.ApprovalScopeTenant).
Filter("tid", tid).
Filter("biz_type", models.ApprovalBizReimburse).
Filter("biz_id", id).
Filter("status", models.ApprovalTaskPending).
Filter("is_deleted", 0).
Filter("approver_id__in", claimsReimburseUIDs(claims)...).
OrderBy("-id").One(&task)
if err == orm.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
return &task, nil
}
// reimburseActCore 报销单审批操作共享逻辑(pass=true 通过 / false 驳回)。
//
// 返回 (httpCode, errMsg, hint):errMsg 非空表示失败;成功时 hint 为提示文案。
// 节点流转交由统一审批引擎,本函数负责定位当前用户的待办任务并补记业务时间线。
func reimburseActCore(claims *jwtutil.Claims, id uint64, pass bool, comment string) (int, string, string) {
if _, err := findOwnedReimbursement(claims, id); err != nil {
return 404, "报销单不存在", ""
}
task, err := reimburseMyTaskCore(claims, id)
if err != nil {
return 500, "查询审批任务失败", ""
}
if task == nil {
return 400, "当前没有待您审批的节点", ""
}
result, err := approvalEngine.Approve(task.ID, approvalEngine.ApproveInput{
ApproverID: task.ApproverID,
Pass: pass,
Comment: comment,
})
if err != nil {
return 400, err.Error(), ""
}
applyReimburseApprovalResult(uint64(claims.TenantId), id,
uint64(claims.UserID), claims.Username, task.Sort,
pass, comment, result.Finished, result.InstanceStat)
hint := result.Message
if hint == "" {
if pass {
hint = "审批通过"
} else {
hint = "已驳回"
}
}
return 200, "", hint
}
// applyReimburseApprovalResult 把一次审批动作回写到报销业务侧:
// 补记 yz_backend_approval_records 时间线;流程结束时同步单据终态(引擎的
// SyncBizResult 也会做同样的事,这里按终态幂等,避免重复插记录)。
func applyReimburseApprovalResult(tid, bizID, actorID uint64, actorName string, step int,
pass bool, comment string, finished bool, instStatus int8) {
if tid == 0 || bizID == 0 {
return
}
action := int8(models.ApprovalActionApprove)
if !pass {
action = models.ApprovalActionReject
}
var row models.BackendReimbursement
if err := models.Orm.QueryTable(new(models.BackendReimbursement)).
Filter("id", bizID).Filter("tid", tid).Filter("is_deleted", 0).One(&row); err != nil {
return
}
// 终态同步(审批中→通过/驳回),引擎已同步过则跳过
var bizStatus int8
if finished {
switch instStatus {
case models.ApprovalInstanceApproved:
bizStatus = 2
case models.ApprovalInstanceRejected:
bizStatus = 3
}
if bizStatus != 0 && row.Status != bizStatus {
_, _ = models.Orm.QueryTable(new(models.BackendReimbursement)).
Filter("id", bizID).Filter("tid", tid).
Update(orm.Params{"status": bizStatus, "current_approver_id": 0, "update_time": time.Now()})
}
}
// 时间线去重:同一步骤同一动作只记一次
dup := models.Orm.QueryTable(new(models.BackendApprovalRecord)).
Filter("tid", tid).Filter("reimbursement_id", bizID).
Filter("approver_id", actorID).Filter("action", action).
Filter("step", int8(step)).Filter("is_deleted", 0).Exist()
if dup {
return
}
var commentPtr *string
if comment != "" {
commentPtr = &comment
}
_, _ = models.Orm.Insert(&models.BackendApprovalRecord{
Tid: tid, UID: uint64Ptr(actorID), ReimbursementID: bizID,
ApproverID: actorID, ApproverName: fmt.Sprintf("%s", actorName), Action: action,
Comment: commentPtr, Step: int8(step), PrevStep: int8(step),
})
}