Files
mayfly-go/server/internal/flow/application/procinst_task.go

233 lines
7.7 KiB
Go
Raw Normal View History

package application
import (
"context"
"mayfly-go/internal/flow/application/dto"
"mayfly-go/internal/flow/domain/entity"
"mayfly-go/internal/flow/domain/repository"
"mayfly-go/pkg/base"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/eventbus"
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils/stringx"
2025-06-27 12:17:45 +08:00
"github.com/spf13/cast"
)
type ProcinstTask interface {
base.App[*entity.ProcinstTask]
Init()
// 获取代办任务
GetTasks(ctx context.Context, condition *entity.ProcinstTaskQuery, orderBy ...string) (*model.PageResult[*entity.ProcinstTaskPO], error)
// 任务审批通过
PassTask(ctx context.Context, taskOp dto.UserTaskOp) error
// 拒绝任务
RejectTask(ctx context.Context, taskOp dto.UserTaskOp) error
// 驳回任务(允许重新提交)
BackTask(ctx context.Context, taskOp dto.UserTaskOp) error
}
type procinstTaskAppImpl struct {
base.AppImpl[*entity.ProcinstTask, repository.ProcinstTask]
procinstApp Procinst `inject:"T"`
executionApp Execution `inject:"T"`
procinstTaskCandidateRepo repository.ProcinstTaskCandidate `inject:"T"`
}
var _ (ProcinstTask) = (*procinstTaskAppImpl)(nil)
func (p *procinstTaskAppImpl) Init() {
const subId = "ProcinstTaskApp"
flowEventBus.Subscribe(EventTopicFlowProcinstCancel, subId, func(ctx context.Context, event *eventbus.Event[any]) error {
procinstId := event.Val.(uint64)
if err := p.UpdateByCond(ctx, &entity.ProcinstTask{Status: entity.ProcinstTaskStatusCanceled}, &entity.ProcinstTask{ProcinstId: procinstId, Status: entity.ProcinstTaskStatusProcess}); err != nil {
return err
}
return p.procinstTaskCandidateRepo.DeleteByCond(ctx, &entity.ProcinstTaskCandidate{ProcinstId: procinstId})
})
}
func (p *procinstTaskAppImpl) GetTasks(ctx context.Context, condition *entity.ProcinstTaskQuery, orderBy ...string) (*model.PageResult[*entity.ProcinstTaskPO], error) {
return p.Repo.GetPageList(condition, orderBy...)
}
func (p *procinstTaskAppImpl) PassTask(ctx context.Context, taskOp dto.UserTaskOp) error {
return p.CompleteTask(ctx, taskOp)
}
func (p *procinstTaskAppImpl) RejectTask(ctx context.Context, taskOp dto.UserTaskOp) error {
taskId := taskOp.TaskId
instTask, taskCandidates, procinst, execution, err := p.getAndValidInstTask(ctx, taskId, taskOp.Candidate)
if err != nil {
return err
}
// 赋值状态和备注
instTask.Status = entity.ProcinstTaskStatusReject
instTask.Remark = taskOp.Remark
instTask.SetEnd()
// 更新流程实例为终止状态,无法重新提交
procinst.Status = entity.ProcinstStatusTerminated
procinst.BizStatus = entity.ProcinstBizStatusNo
procinst.SetEnd()
procinstId := procinst.Id
return p.Tx(ctx, func(ctx context.Context) error {
executionCtx := NewExecutionCtx(ctx, procinst, execution)
executionCtx.OpExtra.Set("approvalResult", instTask.Status)
for _, taskCandidate := range taskCandidates {
taskCandidate.Status = entity.ProcinstTaskStatusReject
taskCandidate.SetEnd()
taskCandidate.Handler = &taskOp.Handler
if err := p.procinstTaskCandidateRepo.UpdateById(ctx, taskCandidate); err != nil {
return err
}
}
if err := p.procinstApp.Save(ctx, procinst); err != nil {
return err
}
if err := p.Save(ctx, instTask); err != nil {
return err
}
if err := p.UpdateByCond(ctx, &entity.ProcinstTask{Status: entity.ProcinstTaskStatusCanceled}, &entity.ProcinstTask{ProcinstId: procinstId, Status: entity.ProcinstTaskStatusProcess}); err != nil {
return err
}
// 跳转至结束节点
if err := p.executionApp.MoveTo(executionCtx, executionCtx.GetFlowDef().GetNodeByType(FlowNodeTypeEnd)[0]); err != nil {
return err
}
// 删除待处理的其他候选人任务
return p.procinstTaskCandidateRepo.DeleteByCond(ctx, &entity.ProcinstTaskCandidate{ProcinstId: procinstId, Status: entity.ProcinstTaskStatusProcess})
})
}
func (p *procinstTaskAppImpl) BackTask(ctx context.Context, taskOp dto.UserTaskOp) error {
// instTask, err := p.getAndValidInstTask(ctx, instTaskId)
// if err != nil {
// return err
// }
// // 赋值状态和备注
// instTask.Status = entity.ProcinstTaskStatusBack
// instTask.Remark = remark
// procinst, _ := p.GetById(instTask.ProcinstId)
// // 更新流程实例为挂起状态,等待重新提交
// procinst.Status = entity.ProcinstStatusSuspended
// return p.Tx(ctx, func(ctx context.Context) error {
// return p.UpdateById(ctx, procinst)
// }, func(ctx context.Context) error {
// return p.procinstTaskRepo.UpdateById(ctx, instTask)
// }, func(ctx context.Context) error {
// return p.triggerProcinstStatusChangeEvent(ctx, procinst)
// })
return nil
}
func (p *procinstTaskAppImpl) CompleteTask(ctx context.Context, taskOp dto.UserTaskOp) error {
taskId := taskOp.TaskId
instTask, taskCandidates, procinst, execution, err := p.getAndValidInstTask(ctx, taskId, taskOp.Candidate)
if err != nil {
return err
}
return p.Tx(ctx, func(ctx context.Context) error {
executionCtx := NewExecutionCtx(ctx, procinst, execution)
usertaskNode := ToUserTaskNode(executionCtx.GetFlowNode())
for _, taskCandidate := range taskCandidates {
taskCandidate.Status = entity.ProcinstTaskStatusCompleted
taskCandidate.SetEnd()
taskCandidate.Handler = &taskOp.Handler
if err := p.procinstTaskCandidateRepo.UpdateById(ctx, taskCandidate); err != nil {
return err
}
}
executionCtx.parent = ctx
vars := instTask.Vars
// map[string]any整数会被解析为float64故统一转为float64
nrOfCompleted := cast.ToFloat64(vars.GetInt(NrOfCompleted) + len(taskCandidates))
vars.Set(NrOfCompleted, nrOfCompleted)
// 完成比例
vars.Set("nrOfCompletedRate", float32(nrOfCompleted)/float32(vars.GetInt(NrOfAll)))
isCompleteRes, err := stringx.TemplateParse(usertaskNode.CompletionCondition, vars)
if err != nil {
return err
}
// 不满足通过条件则保存更新任务完成数等变量即可
if !cast.ToBool(isCompleteRes) {
return p.Save(ctx, instTask)
}
// 赋值状态和备注
instTask.Status = entity.ProcinstTaskStatusCompleted
instTask.Remark = taskOp.Remark
instTask.SetEnd()
if err := p.Save(ctx, instTask); err != nil {
return err
}
// 删除待处理的任务处理候选人
if err := p.procinstTaskCandidateRepo.DeleteByCond(ctx, &entity.ProcinstTaskCandidate{TaskId: taskId, Status: entity.ProcinstTaskStatusProcess}); err != nil {
return err
}
executionCtx.OpExtra.Set("approvalResult", instTask.Status)
// 继续推进执行流
return p.executionApp.ContinueExecution(executionCtx)
})
}
// getAndValidInstTask 获取并校验实例任务
func (p *procinstTaskAppImpl) getAndValidInstTask(ctx context.Context, instTaskId uint64, candidates []string) (*entity.ProcinstTask, []*entity.ProcinstTaskCandidate, *entity.Procinst, *entity.Execution, error) {
instTask, err := p.GetById(instTaskId)
if err != nil {
return nil, nil, nil, nil, errorx.NewBiz("procinst task not found")
}
taskCandidates, err := p.procinstTaskCandidateRepo.SelectByCond(model.NewCond().
In("candidate", candidates).
Eq("task_id", instTask.Id).
Eq("status", entity.ProcinstTaskStatusProcess))
if err != nil {
return nil, nil, nil, nil, err
}
if len(taskCandidates) == 0 {
return nil, nil, nil, nil, errorx.NewBiz("the current candidates is not a task handler and cannot complete the task")
}
procinst, err := p.procinstApp.GetById(instTask.ProcinstId)
if err != nil {
return nil, nil, nil, nil, err
}
execution, err := p.executionApp.GetById(instTask.ExecutionId)
if err != nil {
return nil, nil, nil, nil, err
}
if execution.NodeKey != instTask.NodeKey {
return nil, nil, nil, nil, errorx.NewBiz("the current process instance node does not match and cannot process the task")
}
return instTask, taskCandidates, procinst, execution, nil
}