|
@@ -0,0 +1,437 @@
|
|
|
+package service
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "math"
|
|
|
+ "server/dao"
|
|
|
+ "server/dao/flow"
|
|
|
+ "strconv"
|
|
|
+ "sync"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/jinzhu/gorm"
|
|
|
+
|
|
|
+ "github.com/mumushuiding/util"
|
|
|
+)
|
|
|
+
|
|
|
+// TaskReceiver 任务
|
|
|
+type TaskReceiver struct {
|
|
|
+ TaskID int `json:"taskID"`
|
|
|
+ UserID string `json:"userID,omitempty"`
|
|
|
+ UserName string `json:"username,omitempty"`
|
|
|
+ Pass string `json:"pass,omitempty"`
|
|
|
+ Company string `json:"company,omitempty"`
|
|
|
+ ProcInstID int `json:"procInstID,omitempty"`
|
|
|
+ Comment string `json:"comment,omitempty"`
|
|
|
+ Candidate string `json:"candidate,omitempty"`
|
|
|
+}
|
|
|
+
|
|
|
+var completeLock sync.Mutex
|
|
|
+
|
|
|
+// NewTask 新任务
|
|
|
+func NewTask(t *dao.Task) (int, error) {
|
|
|
+ if len(t.NodeID) == 0 {
|
|
|
+ return 0, errors.New("request param nodeID can not be null / 任务当前所在节点nodeId不能为空!")
|
|
|
+ }
|
|
|
+ t.CreateTime = util.FormatDate(time.Now(), util.YYYY_MM_DD_HH_MM_SS)
|
|
|
+ return t.NewTask()
|
|
|
+}
|
|
|
+
|
|
|
+// NewTaskTx NewTaskTx
|
|
|
+// 开启事务
|
|
|
+func NewTaskTx(t *dao.Task, tx *gorm.DB) (int, error) {
|
|
|
+ if len(t.NodeID) == 0 {
|
|
|
+ return 0, errors.New("request param nodeID can not be null / 任务当前所在节点nodeId不能为空!")
|
|
|
+ }
|
|
|
+ t.CreateTime = util.FormatDate(time.Now(), util.YYYY_MM_DD_HH_MM_SS)
|
|
|
+ return t.NewTaskTx(tx)
|
|
|
+}
|
|
|
+
|
|
|
+// DeleteTask 删除任务
|
|
|
+func DeleteTask(id int) error {
|
|
|
+ return dao.DeleteTask(id)
|
|
|
+}
|
|
|
+
|
|
|
+// GetTaskByID GetTaskById
|
|
|
+func GetTaskByID(id int) (task *dao.Task, err error) {
|
|
|
+ return dao.GetTaskByID(id)
|
|
|
+}
|
|
|
+
|
|
|
+// GetTaskLastByProInstID GetTaskLastByProInstID
|
|
|
+func GetTaskLastByProInstID(procInstID int) (*dao.Task, error) {
|
|
|
+ return dao.GetTaskLastByProInstID(procInstID)
|
|
|
+}
|
|
|
+
|
|
|
+// CompleteByToken 通过token 审批任务
|
|
|
+func CompleteByToken(token string, receiver *TaskReceiver) error {
|
|
|
+ userinfo, err := GetUserinfoFromRedis(token)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ pass, err := strconv.ParseBool(receiver.Pass)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ err = Complete(receiver.TaskID, userinfo.ID, userinfo.Username, userinfo.Company, receiver.Comment, receiver.Candidate, pass)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// Complete Complete
|
|
|
+// 审批
|
|
|
+func Complete(taskID int, userID, username, company, comment, candidate string, pass bool) error {
|
|
|
+ tx := dao.GetTx()
|
|
|
+ err := CompleteTaskTx(taskID, userID, username, company, comment, candidate, pass, tx)
|
|
|
+ if err != nil {
|
|
|
+ tx.Rollback()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ tx.Commit()
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// UpdateTaskWhenComplete 完成后更新任务
|
|
|
+func UpdateTaskWhenComplete(taskID int, userID string, pass bool, tx *gorm.DB) (*model.Task, error) {
|
|
|
+ // 获取task
|
|
|
+ completeLock.Lock() // 关锁
|
|
|
+ defer completeLock.Unlock() //解锁
|
|
|
+ // 查询任务
|
|
|
+ task, err := GetTaskByID(taskID)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if task == nil {
|
|
|
+ return nil, errors.New("任务【" + fmt.Sprintf("%d", task.ID) + "】不存在")
|
|
|
+ }
|
|
|
+ // 判断是否已经结束
|
|
|
+ if task.IsFinished == true {
|
|
|
+ if task.NodeID == "结束" {
|
|
|
+ return nil, errors.New("流程已经结束")
|
|
|
+ }
|
|
|
+ return nil, errors.New("任务【" + fmt.Sprintf("%d", taskID) + "】已经被审批过了!!")
|
|
|
+ }
|
|
|
+ // 设置处理人和处理时间
|
|
|
+ task.Assignee = userID
|
|
|
+ task.ClaimTime = util.FormatDate(time.Now(), util.YYYY_MM_DD_HH_MM_SS)
|
|
|
+ // ----------------会签 (默认全部通过才结束),只要存在一个不通过,就结束,然后流转到上一步
|
|
|
+ //同意
|
|
|
+ if pass {
|
|
|
+ task.AgreeNum++
|
|
|
+ } else {
|
|
|
+ task.IsFinished = true
|
|
|
+ }
|
|
|
+ // 未审批人数减一
|
|
|
+ task.UnCompleteNum--
|
|
|
+ // 判断是否结束
|
|
|
+ if task.UnCompleteNum == 0 {
|
|
|
+ task.IsFinished = true
|
|
|
+ }
|
|
|
+ err = task.UpdateTx(tx)
|
|
|
+ // str, _ := util.ToJSONStr(task)
|
|
|
+ // log.Println(str)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return task, nil
|
|
|
+}
|
|
|
+
|
|
|
+// CompleteTaskTx CompleteTaskTx
|
|
|
+// 执行任务
|
|
|
+func CompleteTaskTx(taskID int, userID, username, company, comment, candidate string, pass bool, tx *gorm.DB) error {
|
|
|
+
|
|
|
+ //更新任务
|
|
|
+ task, err := UpdateTaskWhenComplete(taskID, userID, pass, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // 如果是会签
|
|
|
+ if task.ActType == "and" {
|
|
|
+ // fmt.Println("------------------是会签,判断用户是否已经审批过了,避免重复审批-------")
|
|
|
+ // 判断用户是否已经审批过了(存在会签的情况)
|
|
|
+ yes, err := IfParticipantByTaskID(userID, company, taskID)
|
|
|
+ if err != nil {
|
|
|
+ tx.Rollback()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if yes {
|
|
|
+ tx.Rollback()
|
|
|
+ return errors.New("您已经审批过了,请等待他人审批!)")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查看任务的未审批人数是否为0,不为0就不流转
|
|
|
+ if task.UnCompleteNum > 0 && pass == true { // 默认是全部通过
|
|
|
+ // 添加参与人
|
|
|
+ err := AddParticipantTx(userID, username, company, comment, task.ID, task.ProcInstID, task.Step, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ // 流转到下一流程
|
|
|
+ // nodeInfos, err := GetExecNodeInfosByProcInstID(task.ProcInstID)
|
|
|
+ // if err != nil {
|
|
|
+ // return err
|
|
|
+ // }
|
|
|
+ err = MoveStageByProcInstID(userID, username, company, comment, candidate, task.ID, task.ProcInstID, task.Step, pass, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// WithDrawTaskByToken 撤回任务
|
|
|
+func WithDrawTaskByToken(token string, receiver *TaskReceiver) error {
|
|
|
+ userinfo, err := GetUserinfoFromRedis(token)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if len(userinfo.ID) == 0 {
|
|
|
+ return errors.New("保存在redis中的【用户信息 userinfo】字段 ID 不能为空!!")
|
|
|
+ }
|
|
|
+ if len(userinfo.Username) == 0 {
|
|
|
+ return errors.New("保存在redis中的【用户信息 userinfo】字段 username 不能为空!!")
|
|
|
+ }
|
|
|
+ if len(userinfo.Company) == 0 {
|
|
|
+ return errors.New("保存在redis中的【用户信息 userinfo】字段 company 不能为空")
|
|
|
+ }
|
|
|
+ return WithDrawTask(receiver.TaskID, receiver.ProcInstID, userinfo.ID, userinfo.Username, userinfo.Company, receiver.Comment)
|
|
|
+}
|
|
|
+
|
|
|
+// WithDrawTask 撤回任务
|
|
|
+func WithDrawTask(taskID, procInstID int, userID, username, company, comment string) error {
|
|
|
+ var err1, err2 error
|
|
|
+ var currentTask, lastTask *model.Task
|
|
|
+ var timesx time.Time
|
|
|
+ var wg sync.WaitGroup
|
|
|
+ timesx = time.Now()
|
|
|
+ wg.Add(2)
|
|
|
+ go func() {
|
|
|
+ currentTask, err1 = GetTaskByID(taskID)
|
|
|
+ wg.Done()
|
|
|
+ }()
|
|
|
+ go func() {
|
|
|
+ lastTask, err2 = GetTaskLastByProInstID(procInstID)
|
|
|
+ wg.Done()
|
|
|
+ }()
|
|
|
+ wg.Wait()
|
|
|
+ if err1 != nil {
|
|
|
+ if err1 == gorm.ErrRecordNotFound {
|
|
|
+ return errors.New("任务不存在")
|
|
|
+ }
|
|
|
+ return err1
|
|
|
+ }
|
|
|
+ if err2 != nil {
|
|
|
+ if err2 == gorm.ErrRecordNotFound {
|
|
|
+ return errors.New("找不到流程实例id为【" + fmt.Sprintf("%d", procInstID) + "】的任务,无权撤回")
|
|
|
+ }
|
|
|
+ return err2
|
|
|
+ }
|
|
|
+ // str1,_:=util.ToJSONStr(currentTask)
|
|
|
+ // str2,_:=util.ToJSONStr(lastTask)
|
|
|
+ // fmt.Println(str1)
|
|
|
+ // fmt.Println(str2)
|
|
|
+ if currentTask.Step == 0 {
|
|
|
+ return errors.New("开始位置无法撤回")
|
|
|
+ }
|
|
|
+ if lastTask.Assignee != userID {
|
|
|
+ return errors.New("只能撤回本人审批过的任务!!")
|
|
|
+ }
|
|
|
+ if currentTask.IsFinished {
|
|
|
+ return errors.New("已经审批结束,无法撤回!")
|
|
|
+ }
|
|
|
+ if currentTask.UnCompleteNum != currentTask.MemberCount {
|
|
|
+ return errors.New("已经有人审批过了,无法撤回!")
|
|
|
+ }
|
|
|
+ sub := currentTask.Step - lastTask.Step
|
|
|
+ if math.Abs(float64(sub)) != 1 {
|
|
|
+ return errors.New("只能撤回相邻的任务!!")
|
|
|
+ }
|
|
|
+ var pass = false
|
|
|
+ if sub < 0 {
|
|
|
+ pass = true
|
|
|
+ }
|
|
|
+ fmt.Printf("判断是否可以撤回,耗时:%v\n", time.Since(timesx))
|
|
|
+ timesx = time.Now()
|
|
|
+ tx := model.GetTx()
|
|
|
+ // 更新当前的任务
|
|
|
+ currentTask.IsFinished = true
|
|
|
+ err := currentTask.UpdateTx(tx)
|
|
|
+ if err != nil {
|
|
|
+ tx.Rollback()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // 撤回
|
|
|
+ err = MoveStageByProcInstID(userID, username, company, comment, "", currentTask.ID, procInstID, currentTask.Step, pass, tx)
|
|
|
+ if err != nil {
|
|
|
+ tx.Rollback()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ tx.Commit()
|
|
|
+ fmt.Printf("撤回流程耗时:%v\n", time.Since(timesx))
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// MoveStageByProcInstID MoveStageByProcInstID
|
|
|
+func MoveStageByProcInstID(userID, username, company, comment, candidate string, taskID, procInstID, step int, pass bool, tx *gorm.DB) (err error) {
|
|
|
+ nodeInfos, err := GetExecNodeInfosByProcInstID(procInstID)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return MoveStage(nodeInfos, userID, username, company, comment, candidate, taskID, procInstID, step, pass, tx)
|
|
|
+}
|
|
|
+
|
|
|
+// MoveStage MoveStage
|
|
|
+// 流程流转
|
|
|
+func MoveStage(nodeInfos []*flow.NodeInfo, userID, username, company, comment, candidate string, taskID, procInstID, step int, pass bool, tx *gorm.DB) (err error) {
|
|
|
+ // 添加上一步的参与人
|
|
|
+ err = AddParticipantTx(userID, username, company, comment, taskID, procInstID, step, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if pass {
|
|
|
+ step++
|
|
|
+ if step-1 > len(nodeInfos) {
|
|
|
+ return errors.New("已经结束无法流转到下一个节点")
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ step--
|
|
|
+ if step < 0 {
|
|
|
+ return errors.New("处于开始位置,无法回退到上一个节点")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 指定下一步执行人
|
|
|
+ if len(candidate) > 0 {
|
|
|
+ nodeInfos[step].Aprover = candidate
|
|
|
+ }
|
|
|
+ // 判断下一流程: 如果是审批人是:抄送人
|
|
|
+ // fmt.Printf("下一审批人类型:%s\n", nodeInfos[step].AproverType)
|
|
|
+ // fmt.Println(nodeInfos[step].AproverType == flow.NodeTypes[flow.NOTIFIER])
|
|
|
+ if nodeInfos[step].AproverType == flow.NodeTypes[flow.NOTIFIER] {
|
|
|
+ // 生成新的任务
|
|
|
+ var task = model.Task{
|
|
|
+ NodeID: flow.NodeTypes[flow.NOTIFIER],
|
|
|
+ Step: step,
|
|
|
+ ProcInstID: procInstID,
|
|
|
+ IsFinished: true,
|
|
|
+ }
|
|
|
+ task.IsFinished = true
|
|
|
+ _, err := task.NewTaskTx(tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // 添加抄送人
|
|
|
+ err = AddNotifierTx(nodeInfos[step].Aprover, company, step, procInstID, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return MoveStage(nodeInfos, userID, username, company, comment, candidate, taskID, procInstID, step, pass, tx)
|
|
|
+ }
|
|
|
+ if pass {
|
|
|
+ return MoveToNextStage(nodeInfos, userID, company, taskID, procInstID, step, tx)
|
|
|
+ }
|
|
|
+ return MoveToPrevStage(nodeInfos, userID, company, taskID, procInstID, step, tx)
|
|
|
+}
|
|
|
+
|
|
|
+// MoveToNextStage MoveToNextStage
|
|
|
+// 通过
|
|
|
+func MoveToNextStage(nodeInfos []*flow.NodeInfo, userID, company string, currentTaskID, procInstID, step int, tx *gorm.DB) error {
|
|
|
+ var currentTime = util.FormatDate(time.Now(), util.YYYY_MM_DD_HH_MM_SS)
|
|
|
+ var task = getNewTask(nodeInfos, step, procInstID, currentTime) //新任务
|
|
|
+ var procInst = &model.ProcInst{ // 流程实例要更新的字段
|
|
|
+ NodeID: nodeInfos[step].NodeID,
|
|
|
+ Candidate: nodeInfos[step].Aprover,
|
|
|
+ }
|
|
|
+ procInst.ID = procInstID
|
|
|
+ if (step + 1) != len(nodeInfos) { // 下一步不是【结束】
|
|
|
+ // 生成新的任务
|
|
|
+ taksID, err := task.NewTaskTx(tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // 添加candidate group
|
|
|
+ err = AddCandidateGroupTx(nodeInfos[step].Aprover, company, step, taksID, procInstID, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // 更新流程实例
|
|
|
+ procInst.TaskID = taksID
|
|
|
+ err = UpdateProcInst(procInst, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ } else { // 最后一步直接结束
|
|
|
+ // 生成新的任务
|
|
|
+ task.IsFinished = true
|
|
|
+ task.ClaimTime = currentTime
|
|
|
+ taksID, err := task.NewTaskTx(tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // 删除候选用户组
|
|
|
+ err = DelCandidateByProcInstID(procInstID, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // 更新流程实例
|
|
|
+ procInst.TaskID = taksID
|
|
|
+ procInst.EndTime = currentTime
|
|
|
+ procInst.IsFinished = true
|
|
|
+ procInst.Candidate = "审批结束"
|
|
|
+ err = UpdateProcInst(procInst, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// MoveToPrevStage MoveToPrevStage
|
|
|
+// 驳回
|
|
|
+func MoveToPrevStage(nodeInfos []*flow.NodeInfo, userID, company string, currentTaskID, procInstID, step int, tx *gorm.DB) error {
|
|
|
+ // 生成新的任务
|
|
|
+ var task = getNewTask(nodeInfos, step, procInstID, util.FormatDate(time.Now(), util.YYYY_MM_DD_HH_MM_SS)) //新任务
|
|
|
+ taksID, err := task.NewTaskTx(tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ var procInst = &model.ProcInst{ // 流程实例要更新的字段
|
|
|
+ NodeID: nodeInfos[step].NodeID,
|
|
|
+ Candidate: nodeInfos[step].Aprover,
|
|
|
+ TaskID: taksID,
|
|
|
+ }
|
|
|
+ procInst.ID = procInstID
|
|
|
+ err = UpdateProcInst(procInst, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if step == 0 { // 流程回到起始位置,注意起始位置为0,
|
|
|
+ err = AddCandidateUserTx(nodeInfos[step].Aprover, company, step, taksID, procInstID, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ // 添加candidate group
|
|
|
+ err = AddCandidateGroupTx(nodeInfos[step].Aprover, company, step, taksID, procInstID, tx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+func getNewTask(nodeInfos []*flow.NodeInfo, step, procInstID int, currentTime string) *model.Task {
|
|
|
+ var task = &model.Task{ // 新任务
|
|
|
+ NodeID: nodeInfos[step].NodeID,
|
|
|
+ Step: step,
|
|
|
+ CreateTime: currentTime,
|
|
|
+ ProcInstID: procInstID,
|
|
|
+ MemberCount: nodeInfos[step].MemberCount,
|
|
|
+ UnCompleteNum: nodeInfos[step].MemberCount,
|
|
|
+ ActType: nodeInfos[step].ActType,
|
|
|
+ }
|
|
|
+ return task
|
|
|
+}
|