2024-02-29 22:12:50 +08:00
|
|
|
|
package application
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"fmt"
|
2025-04-15 21:42:31 +08:00
|
|
|
|
"mayfly-go/internal/event"
|
2024-05-09 21:29:34 +08:00
|
|
|
|
"mayfly-go/internal/flow/application/dto"
|
2024-02-29 22:12:50 +08:00
|
|
|
|
"mayfly-go/internal/flow/domain/entity"
|
|
|
|
|
|
"mayfly-go/internal/flow/domain/repository"
|
2024-11-20 22:43:53 +08:00
|
|
|
|
"mayfly-go/internal/flow/imsg"
|
2025-04-15 21:42:31 +08:00
|
|
|
|
msgdto "mayfly-go/internal/msg/application/dto"
|
2024-02-29 22:12:50 +08:00
|
|
|
|
"mayfly-go/pkg/base"
|
|
|
|
|
|
"mayfly-go/pkg/contextx"
|
|
|
|
|
|
"mayfly-go/pkg/errorx"
|
2025-04-15 21:42:31 +08:00
|
|
|
|
"mayfly-go/pkg/global"
|
2024-11-20 22:43:53 +08:00
|
|
|
|
"mayfly-go/pkg/i18n"
|
2024-10-16 17:24:50 +08:00
|
|
|
|
"mayfly-go/pkg/logx"
|
2024-02-29 22:12:50 +08:00
|
|
|
|
"mayfly-go/pkg/model"
|
2024-10-16 17:24:50 +08:00
|
|
|
|
"mayfly-go/pkg/utils/anyx"
|
|
|
|
|
|
"mayfly-go/pkg/utils/jsonx"
|
|
|
|
|
|
"mayfly-go/pkg/utils/stringx"
|
2025-04-15 21:42:31 +08:00
|
|
|
|
|
|
|
|
|
|
"github.com/may-fly/cast"
|
2024-02-29 22:12:50 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type Procinst interface {
|
|
|
|
|
|
base.App[*entity.Procinst]
|
|
|
|
|
|
|
|
|
|
|
|
GetPageList(condition *entity.ProcinstQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
|
|
|
|
|
|
|
|
|
|
|
// 获取流程实例审批节点任务
|
|
|
|
|
|
GetProcinstTasks(condition *entity.ProcinstTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
|
|
|
|
|
|
2024-05-08 21:04:25 +08:00
|
|
|
|
// StartProc 根据流程定义启动一个流程实例
|
2024-05-09 21:29:34 +08:00
|
|
|
|
StartProc(ctx context.Context, procdefId uint64, reqParam *dto.StarProc) (*entity.Procinst, error)
|
2024-02-29 22:12:50 +08:00
|
|
|
|
|
|
|
|
|
|
// 取消流程
|
|
|
|
|
|
CancelProc(ctx context.Context, procinstId uint64) error
|
|
|
|
|
|
|
|
|
|
|
|
// 完成任务
|
|
|
|
|
|
CompleteTask(ctx context.Context, taskId uint64, remark string) error
|
|
|
|
|
|
|
|
|
|
|
|
// 拒绝任务
|
|
|
|
|
|
RejectTask(ctx context.Context, taskId uint64, remark string) error
|
|
|
|
|
|
|
|
|
|
|
|
// 驳回任务(允许重新提交)
|
|
|
|
|
|
BackTask(ctx context.Context, taskId uint64, remark string) error
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type procinstAppImpl struct {
|
|
|
|
|
|
base.AppImpl[*entity.Procinst, repository.Procinst]
|
|
|
|
|
|
|
2024-12-16 23:29:18 +08:00
|
|
|
|
procinstTaskRepo repository.ProcinstTask `inject:"T"`
|
|
|
|
|
|
procdefApp Procdef `inject:"T"`
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-08 21:04:25 +08:00
|
|
|
|
var _ (Procinst) = (*procinstAppImpl)(nil)
|
|
|
|
|
|
|
2024-02-29 22:12:50 +08:00
|
|
|
|
func (p *procinstAppImpl) GetPageList(condition *entity.ProcinstQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
|
|
|
|
|
return p.Repo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *procinstAppImpl) GetProcinstTasks(condition *entity.ProcinstTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
|
|
|
|
|
return p.procinstTaskRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-09 21:29:34 +08:00
|
|
|
|
func (p *procinstAppImpl) StartProc(ctx context.Context, procdefId uint64, reqParam *dto.StarProc) (*entity.Procinst, error) {
|
2024-05-08 21:04:25 +08:00
|
|
|
|
procdef, err := p.procdefApp.GetById(procdefId)
|
|
|
|
|
|
if err != nil {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return nil, errorx.NewBiz("procdef not found")
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if procdef.Status != entity.ProcdefStatusEnable {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return nil, errorx.NewBizI(ctx, imsg.ErrProcdefNotEnable)
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-16 17:24:50 +08:00
|
|
|
|
bizKey := reqParam.BizKey
|
|
|
|
|
|
if bizKey == "" {
|
|
|
|
|
|
bizKey = stringx.RandUUID()
|
|
|
|
|
|
}
|
2024-02-29 22:12:50 +08:00
|
|
|
|
procinst := &entity.Procinst{
|
|
|
|
|
|
BizType: reqParam.BizType,
|
2024-10-16 17:24:50 +08:00
|
|
|
|
BizKey: bizKey,
|
2024-03-02 19:08:19 +08:00
|
|
|
|
BizForm: reqParam.BizForm,
|
2024-02-29 22:12:50 +08:00
|
|
|
|
BizStatus: entity.ProcinstBizStatusWait,
|
|
|
|
|
|
ProcdefId: procdef.Id,
|
|
|
|
|
|
ProcdefName: procdef.Name,
|
|
|
|
|
|
Remark: reqParam.Remark,
|
2024-03-02 19:08:19 +08:00
|
|
|
|
Status: entity.ProcinstStatusActive,
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
task := p.getNextTask(procdef, "")
|
|
|
|
|
|
procinst.TaskKey = task.TaskKey
|
|
|
|
|
|
|
|
|
|
|
|
if err := p.Save(ctx, procinst); err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
return procinst, p.createProcinstTask(ctx, procinst, task)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *procinstAppImpl) CancelProc(ctx context.Context, procinstId uint64) error {
|
2024-05-05 14:53:30 +08:00
|
|
|
|
procinst, err := p.GetById(procinstId)
|
2024-02-29 22:12:50 +08:00
|
|
|
|
if err != nil {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return errorx.NewBiz("procinst not found")
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
la := contextx.GetLoginAccount(ctx)
|
|
|
|
|
|
if la == nil {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return errorx.NewBiz("no login")
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
if procinst.CreatorId != la.Id {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return errorx.NewBizI(ctx, imsg.ErrProcinstCancelSelf)
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
2024-03-02 19:08:19 +08:00
|
|
|
|
procinst.Status = entity.ProcinstStatusCancelled
|
2024-02-29 22:12:50 +08:00
|
|
|
|
procinst.BizStatus = entity.ProcinstBizStatusNo
|
|
|
|
|
|
procinst.SetEnd()
|
|
|
|
|
|
|
|
|
|
|
|
return p.Tx(ctx, func(ctx context.Context) error {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return p.cancelInstTasks(ctx, procinstId, i18n.T(imsg.ErrProcinstCancelled))
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}, func(ctx context.Context) error {
|
|
|
|
|
|
return p.Save(ctx, procinst)
|
|
|
|
|
|
}, func(ctx context.Context) error {
|
|
|
|
|
|
return p.triggerProcinstStatusChangeEvent(ctx, procinst)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *procinstAppImpl) CompleteTask(ctx context.Context, instTaskId uint64, remark string) error {
|
|
|
|
|
|
instTask, err := p.getAndValidInstTask(ctx, instTaskId)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 赋值状态和备注
|
|
|
|
|
|
instTask.Status = entity.ProcinstTaskStatusPass
|
|
|
|
|
|
instTask.Remark = remark
|
|
|
|
|
|
instTask.SetEnd()
|
|
|
|
|
|
|
2024-05-05 14:53:30 +08:00
|
|
|
|
procinst, _ := p.GetById(instTask.ProcinstId)
|
|
|
|
|
|
procdef, _ := p.procdefApp.GetById(procinst.ProcdefId)
|
2024-02-29 22:12:50 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取下一实例审批任务
|
|
|
|
|
|
task := p.getNextTask(procdef, instTask.TaskKey)
|
|
|
|
|
|
if task == nil {
|
2024-03-02 19:08:19 +08:00
|
|
|
|
procinst.Status = entity.ProcinstStatusCompleted
|
2024-02-29 22:12:50 +08:00
|
|
|
|
procinst.SetEnd()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
procinst.TaskKey = task.TaskKey
|
2025-04-15 21:42:31 +08:00
|
|
|
|
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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.createProcinstTask(ctx, procinst, task)
|
|
|
|
|
|
}, func(ctx context.Context) error {
|
|
|
|
|
|
// 下一审批节点任务不存在,说明该流程已结束
|
|
|
|
|
|
if task == nil {
|
|
|
|
|
|
return p.triggerProcinstStatusChangeEvent(ctx, procinst)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *procinstAppImpl) RejectTask(ctx context.Context, instTaskId uint64, remark string) error {
|
|
|
|
|
|
instTask, err := p.getAndValidInstTask(ctx, instTaskId)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 赋值状态和备注
|
|
|
|
|
|
instTask.Status = entity.ProcinstTaskStatusReject
|
|
|
|
|
|
instTask.Remark = remark
|
|
|
|
|
|
instTask.SetEnd()
|
|
|
|
|
|
|
2024-05-05 14:53:30 +08:00
|
|
|
|
procinst, _ := p.GetById(instTask.ProcinstId)
|
2024-02-29 22:12:50 +08:00
|
|
|
|
// 更新流程实例为终止状态,无法重新提交
|
2024-03-02 19:08:19 +08:00
|
|
|
|
procinst.Status = entity.ProcinstStatusTerminated
|
2024-02-29 22:12:50 +08:00
|
|
|
|
procinst.BizStatus = entity.ProcinstBizStatusNo
|
|
|
|
|
|
procinst.SetEnd()
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *procinstAppImpl) BackTask(ctx context.Context, instTaskId uint64, remark string) error {
|
|
|
|
|
|
instTask, err := p.getAndValidInstTask(ctx, instTaskId)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 赋值状态和备注
|
|
|
|
|
|
instTask.Status = entity.ProcinstTaskStatusBack
|
|
|
|
|
|
instTask.Remark = remark
|
|
|
|
|
|
|
2024-05-05 14:53:30 +08:00
|
|
|
|
procinst, _ := p.GetById(instTask.ProcinstId)
|
2024-02-29 22:12:50 +08:00
|
|
|
|
|
|
|
|
|
|
// 更新流程实例为挂起状态,等待重新提交
|
2024-03-02 19:08:19 +08:00
|
|
|
|
procinst.Status = entity.ProcinstStatusSuspended
|
2024-02-29 22:12:50 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 取消处理中的流程实例任务
|
|
|
|
|
|
func (p *procinstAppImpl) cancelInstTasks(ctx context.Context, procinstId uint64, cancelReason string) error {
|
|
|
|
|
|
// 流程实例任务信息
|
2024-05-05 14:53:30 +08:00
|
|
|
|
instTasks, _ := p.procinstTaskRepo.SelectByCond(&entity.ProcinstTask{ProcinstId: procinstId, Status: entity.ProcinstTaskStatusProcess})
|
|
|
|
|
|
for _, instTask := range instTasks {
|
2024-02-29 22:12:50 +08:00
|
|
|
|
instTask.Status = entity.ProcinstTaskStatusCanceled
|
|
|
|
|
|
instTask.Remark = cancelReason
|
|
|
|
|
|
instTask.SetEnd()
|
|
|
|
|
|
p.procinstTaskRepo.UpdateById(ctx, instTask)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 触发流程实例状态改变事件
|
|
|
|
|
|
func (p *procinstAppImpl) triggerProcinstStatusChangeEvent(ctx context.Context, procinst *entity.Procinst) error {
|
2024-10-16 17:24:50 +08:00
|
|
|
|
|
|
|
|
|
|
handleRes, err := FlowBizHandle(ctx, &BizHandleParam{
|
|
|
|
|
|
Procinst: *procinst,
|
2024-03-02 19:08:19 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2024-10-16 17:24:50 +08:00
|
|
|
|
if !anyx.IsBlank(handleRes) {
|
|
|
|
|
|
procinst.BizHandleRes = jsonx.ToStr(handleRes)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-02-29 22:12:50 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
// 业务处理错误,非完成状态则终止流程
|
2024-03-02 19:08:19 +08:00
|
|
|
|
if procinst.Status != entity.ProcinstStatusCompleted {
|
|
|
|
|
|
procinst.Status = entity.ProcinstStatusTerminated
|
2024-02-29 22:12:50 +08:00
|
|
|
|
procinst.SetEnd()
|
2024-11-20 22:43:53 +08:00
|
|
|
|
p.cancelInstTasks(ctx, procinst.Id, i18n.T(imsg.ErrBizHandlerFail))
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
procinst.BizStatus = entity.ProcinstBizStatusFail
|
2024-10-16 17:24:50 +08:00
|
|
|
|
if procinst.BizHandleRes == "" {
|
|
|
|
|
|
procinst.BizHandleRes = err.Error()
|
|
|
|
|
|
} else {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
logx.Errorf("process business [%s] processing failed: %v", procinst.BizKey, err.Error())
|
2024-10-16 17:24:50 +08:00
|
|
|
|
}
|
2024-02-29 22:12:50 +08:00
|
|
|
|
return p.UpdateById(ctx, procinst)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理成功,并且状态为完成,则更新业务状态为成功
|
2024-03-02 19:08:19 +08:00
|
|
|
|
if procinst.Status == entity.ProcinstStatusCompleted {
|
2024-02-29 22:12:50 +08:00
|
|
|
|
procinst.BizStatus = entity.ProcinstBizStatusSuccess
|
2024-10-16 17:24:50 +08:00
|
|
|
|
if procinst.BizHandleRes == "" {
|
|
|
|
|
|
procinst.BizHandleRes = "success"
|
|
|
|
|
|
}
|
2024-02-29 22:12:50 +08:00
|
|
|
|
return p.UpdateById(ctx, procinst)
|
|
|
|
|
|
}
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取并校验实例任务
|
|
|
|
|
|
func (p *procinstAppImpl) getAndValidInstTask(ctx context.Context, instTaskId uint64) (*entity.ProcinstTask, error) {
|
2024-05-05 14:53:30 +08:00
|
|
|
|
instTask, err := p.procinstTaskRepo.GetById(instTaskId)
|
|
|
|
|
|
if err != nil {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return nil, errorx.NewBiz("procinst not found")
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
la := contextx.GetLoginAccount(ctx)
|
|
|
|
|
|
if instTask.Assignee != fmt.Sprintf("%d", la.Id) {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return nil, errorx.NewBiz("the current user is not a task handler and cannot complete the task")
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return instTask, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建流程实例节点任务
|
|
|
|
|
|
func (p *procinstAppImpl) createProcinstTask(ctx context.Context, procinst *entity.Procinst, task *entity.ProcdefTask) error {
|
|
|
|
|
|
if task == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
procinstTask := &entity.ProcinstTask{
|
|
|
|
|
|
ProcinstId: procinst.Id,
|
|
|
|
|
|
Status: entity.ProcinstTaskStatusProcess,
|
|
|
|
|
|
|
|
|
|
|
|
TaskKey: task.TaskKey,
|
|
|
|
|
|
TaskName: task.Name,
|
|
|
|
|
|
Assignee: task.UserId,
|
|
|
|
|
|
}
|
2025-04-15 21:42:31 +08:00
|
|
|
|
|
|
|
|
|
|
if err := p.procinstTaskRepo.Insert(ctx, procinstTask); err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送通知消息
|
|
|
|
|
|
global.EventBus.Publish(ctx, event.EventTopicBizMsgTmplSend, msgdto.BizMsgTmplSend{
|
|
|
|
|
|
BizType: FlowTaskNotifyBizKey,
|
|
|
|
|
|
BizId: procinst.ProcdefId,
|
|
|
|
|
|
Params: map[string]any{
|
|
|
|
|
|
"creator": procinst.Creator,
|
|
|
|
|
|
"procdefName": procinst.ProcdefName,
|
|
|
|
|
|
"bizKey": procinst.BizKey,
|
|
|
|
|
|
"taskName": task.Name,
|
|
|
|
|
|
"procinstRemark": procinst.Remark,
|
|
|
|
|
|
},
|
|
|
|
|
|
ReceiverIds: []uint64{cast.ToUint64(task.UserId)},
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
2024-02-29 22:12:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取下一审批节点任务
|
|
|
|
|
|
func (p *procinstAppImpl) getNextTask(procdef *entity.Procdef, nowTaskKey string) *entity.ProcdefTask {
|
|
|
|
|
|
tasks := procdef.GetTasks()
|
|
|
|
|
|
if len(tasks) == 0 {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if nowTaskKey == "" {
|
|
|
|
|
|
// nowTaskKey为空,则说明为刚启动该流程实例
|
|
|
|
|
|
return tasks[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for index, t := range tasks {
|
|
|
|
|
|
if (t.TaskKey == nowTaskKey) && (index < len(tasks)-1) {
|
|
|
|
|
|
return tasks[index+1]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|