approveflow/app/provider/flow_instance/model/approval_instance.go

426 lines
13 KiB
Go

package model
import (
"approveflow/app/base"
"approveflow/app/provider/abstract/connect"
"approveflow/app/provider/flow_definition"
"approveflow/app/utils"
"context"
"encoding/json"
"fmt"
"github.com/Superdanda/hade/framework/gin"
"github.com/pkg/errors"
"sync"
)
const (
StatusCreated = "Created" // 审批实例已创建
StatusInProgress = "InProgress" // 审批进行中
StatusCompleted = "Completed" // 审批完成
StatusCancelled = "Cancelled" // 审批取消
)
// ApprovalInstance 审批实例表
type ApprovalInstance struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID
FlowID int64 `gorm:"type:bigint;index;not null" json:"flow_id"` // 流程ID
ApplicantKey string `gorm:"type:bigint;index;not null" json:"approver_id"` //申请人ID
CreatorKey string `gorm:"type:bigint;not null" json:"creator_id"` // 创建者ID
Status string `gorm:"type:varchar(50);not null" json:"status"` // 审批状态
CurrentStepIDs []*CurrentStep `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE" json:"current_step_ids"` // 当前步骤ID
Steps []*InstanceStep `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE" json:"steps"` // 实例步骤
Data string `gorm:"type:json" json:"data"` // 保存审批数据的 JSON
sync.RWMutex
base.Model
}
func (instance *ApprovalInstance) GetNodes() []connect.AbstractNode {
return utils.ConvertToAbstractNodes(instance.Steps, func(step *InstanceStep) connect.AbstractNode {
return step
})
}
func (instance *ApprovalInstance) SetNodes(nodes []connect.AbstractNode) {
//TODO implement me
panic("implement me")
}
func (instance *ApprovalInstance) GetNodeMap() map[string]connect.AbstractNode {
//TODO implement me
panic("implement me")
}
func (instance *ApprovalInstance) NewNodePathConfig(fromNodeKey, toNodeKey string) connect.AbstractNodePathConfig {
//TODO implement me
panic("implement me")
}
// CurrentStep 目前审批中的节点
type CurrentStep struct {
CurrentStepId int64 `gorm:"type:bigint;index;not null" json:"current_step_id"`
InstanceID int64 `gorm:"type:bigint;index;not null" json:"instance_id"`
}
// NewApprovalInstance 初始化创建一个审批流实例
func NewApprovalInstance(
ctx *gin.Context,
approvalFlow *flow_definition.ApprovalFlow,
data map[string]interface{}) (*ApprovalInstance, error) {
bytes, err := json.Marshal(data)
if err != nil {
return nil, err
}
instance := &ApprovalInstance{
FlowID: approvalFlow.ID,
ApplicantKey: data[base.ApplicantKey].(string),
CreatorKey: data[base.CreatorKey].(string),
Steps: []*InstanceStep{},
Data: string(bytes),
}
// Get the first step of the approval flow
firstStep, err := approvalFlow.FirstStep()
if err != nil {
return nil, err
}
// Build the steps recursively
processedSteps := make(map[string]*InstanceStep)
err = buildInstanceSteps(ctx, instance, firstStep, nil, data, processedSteps)
if err != nil {
return nil, err
}
return instance, nil
}
// buildInstanceSteps recursively builds instance steps from approval steps
func buildInstanceSteps(ctx *gin.Context, instance *ApprovalInstance, currentApprovalStep *flow_definition.ApprovalStep,
previousInstanceStep *InstanceStep, data map[string]interface{}, processedSteps map[string]*InstanceStep) error {
// Check if this step has already been processed to avoid cycles
if existingInstanceStep, ok := processedSteps[currentApprovalStep.Key]; ok {
if previousInstanceStep != nil {
connect.InsertNodeAfter(instance, previousInstanceStep, existingInstanceStep)
}
return nil
}
// Create a new instance step
var approverKey string
approver, err := currentApprovalStep.DynamicConfig.GetApprover(ctx, data)
if err != nil {
approverKey = ""
} else {
approverKey = approver.Key
}
currentInstanceStep := NewInstanceStep(currentApprovalStep, approverKey, false)
// Insert the instance step into the instance
if previousInstanceStep == nil {
connect.InsertNodeFirst(instance, currentInstanceStep)
} else {
connect.InsertNodeAfter(instance, previousInstanceStep, currentInstanceStep)
}
// Mark this step as processed
processedSteps[currentApprovalStep.Key] = currentInstanceStep
// Get the next steps and recursively build them
nextSteps, err := connect.GetNextNodes(instance, currentApprovalStep)
if err != nil {
return err
}
for _, nextStep := range nextSteps {
nextApprovalStep := nextStep.(*flow_definition.ApprovalStep)
err := buildInstanceSteps(ctx, instance, nextApprovalStep, currentInstanceStep, data, processedSteps)
if err != nil {
return err
}
}
return nil
}
// SendApprovalNotification 发送审批通知给第一个审批人的方法
func (instance *ApprovalInstance) SendApprovalNotification(step *InstanceStep, ctx context.Context) error {
// 向消息队列发送审批开始事件
//userService := container.MustMake(userProvider.Key).(userProvider.Service)
//approver, err := userService.GetUserByID(step.ApproverID)
//if err != nil {
// return fmt.Errorf("获取审批人失败: %v", err)
//}
//
//// 构建通知内容
//notification := fmt.Sprintf("审批流程已启动,请您审批步骤: %s", step.Name)
//
//// 发送通知
//err = sendNotificationToUser(approver, notification)
//if err != nil {
// return fmt.Errorf("发送通知失败: %v", err)
//}
return nil
}
// Start 开始审批流程,设置第一个步骤为当前步骤
func (instance *ApprovalInstance) Start(ctx context.Context) error {
instance.RLock()
defer instance.RUnlock()
if instance.Status != StatusCreated {
return fmt.Errorf("审批流程已启动或已完成,无法再次启动")
}
// 获取第一个审批步骤
firstStep, err := instance.getFirstStep()
if err != nil {
return err
}
// 设置当前步骤为第一个步骤,并更改状态
instance.AddCurrentStepID(firstStep)
instance.Status = StatusInProgress
err = instance.SendApprovalNotification(firstStep, ctx)
if err != nil {
return err
}
return nil
}
// getFirstStep 获取第一个审批步骤
func (instance *ApprovalInstance) getFirstStep() (*InstanceStep, error) {
for _, step := range instance.Steps {
if step.StepCode == flow_definition.StepStart {
return step, nil
}
}
return nil, errors.New("未找到起点")
}
// MoveToNextStep 移动到下一个步骤
func (instance *ApprovalInstance) MoveToNextStep() error {
instance.Lock()
defer instance.Unlock()
currentStepSlice := instance.GetCurrentSteps()
if currentStepSlice == nil || len(currentStepSlice) == 0 {
return fmt.Errorf("当前步骤不存在")
}
dataMap, err := instance.DataToMap()
if err != nil {
return err
}
currentStep := currentStepSlice[0]
// 完成当前步骤
currentStep.Status = StepStatusCompleted
// 获取下一个步骤
var nextStep *InstanceStep
nextStepArr, err := instance.getNextStep(currentStep, dataMap)
if nextStepArr != nil && len(nextStepArr) == 0 {
nextStep = nextStepArr[0]
} else {
return errors.New("暂时不支持并行审批")
}
if err != nil {
instance.Status = StatusCompleted // 流程结束
return nil
}
// 设置新的当前步骤
instance.RemoveCurrentStepID(currentStep.ID)
instance.AddCurrentStepID(nextStep)
nextStep.Status = StepStatusPending
return nil
}
// getNextStep 获取下一个步骤
func (instance *ApprovalInstance) getNextStep(currentStep *InstanceStep, data map[string]interface{}) ([]*InstanceStep, error) {
pathConfigs := currentStep.InstancePathConfigs
var nextSteps []*InstanceStep
for _, pathConfig := range pathConfigs {
met, err := pathConfig.IsConditionMet(data)
if err != nil || !met {
continue
}
nextStep, err := instance.GetStepByKey(pathConfig.GetToNodeKey())
if err != nil {
return nil, err
}
nextSteps = append(nextSteps, nextStep)
}
return nextSteps, nil
}
func (instance *ApprovalInstance) findStepByID(id int64) (*InstanceStep, error) {
for _, step := range instance.Steps {
if step.ID == id {
return step, nil
}
}
return nil, nil
}
func (instance *ApprovalInstance) findStepByKey(key string) (*InstanceStep, error) {
for _, step := range instance.Steps {
if step.Key == key {
return step, nil
}
}
return nil, nil
}
func (instance *ApprovalInstance) getStepByFlowStepID(flowStepId int64) *InstanceStep {
for _, step := range instance.Steps {
if step.StepID == flowStepId {
return step
}
}
return nil
}
// DataToMap 将 Data 字段转为 map[string]interface{}
func (instance *ApprovalInstance) DataToMap() (map[string]interface{}, error) {
var dataMap map[string]interface{}
if err := json.Unmarshal([]byte(instance.Data), &dataMap); err != nil {
return nil, fmt.Errorf("解析 Data 字段失败: %v", err)
}
return dataMap, nil
}
func (instance *ApprovalInstance) GetStep(id int64) (*InstanceStep, error) {
for _, step := range instance.Steps {
if step.ID == id {
return step, nil
}
}
return nil, fmt.Errorf("step with ID %d not found in approval flow", id)
}
func (instance *ApprovalInstance) GetStepByKey(key string) (*InstanceStep, error) {
for _, step := range instance.Steps {
if step.Key == key {
return step, nil
}
}
return nil, fmt.Errorf("step with key %d not found in approval flow", key)
}
// AddCurrentStepID adds a new CurrentStep to the CurrentStepIDs slice if it does not already exist
func (instance *ApprovalInstance) AddCurrentStepID(step *InstanceStep) {
// Check if the step already exists
for _, s := range instance.CurrentStepIDs {
if s.CurrentStepId == step.ID {
return // Step already exists, do nothing
}
}
// Add the new step
instance.CurrentStepIDs = append(instance.CurrentStepIDs, &CurrentStep{CurrentStepId: step.ID, InstanceID: instance.ID})
}
// RemoveCurrentStepID removes a CurrentStep from the CurrentStepIDs slice based on its ID
func (instance *ApprovalInstance) RemoveCurrentStepID(stepID int64) {
// Find the index of the step to remove
for i, s := range instance.CurrentStepIDs {
if s.CurrentStepId == stepID {
// Remove the step by slicing
instance.CurrentStepIDs = append(instance.CurrentStepIDs[:i], instance.CurrentStepIDs[i+1:]...)
return
}
}
}
// HasCurrentStepID checks if a CurrentStep with a given ID exists in the CurrentStepIDs slice
func (instance *ApprovalInstance) HasCurrentStepID(stepID int64) bool {
for _, s := range instance.CurrentStepIDs {
if s.CurrentStepId == stepID {
return true
}
}
return false
}
// GetCurrentSteps returns a copy of the CurrentSteps slice
func (instance *ApprovalInstance) GetCurrentSteps() []*InstanceStep {
// Return a copy of the slice to avoid modifications
currentSteps := make([]*InstanceStep, len(instance.CurrentStepIDs))
for _, step := range instance.Steps {
if instance.HasCurrentStepID(step.ID) {
currentSteps = append(currentSteps, step)
}
}
return currentSteps
}
func (instance *ApprovalInstance) GetCurrentStepsById(id int64) *InstanceStep {
currentSteps := instance.GetCurrentSteps()
for _, step := range currentSteps {
if step.ID == id {
return step
}
}
return nil
}
// executeApprovalStep 执行审批步骤
func (instance *ApprovalInstance) executeApprovalStep(approvalFlow flow_definition.ApprovalFlow) error {
for _, instanceStep := range instance.GetCurrentSteps() {
flowStep, err := approvalFlow.GetStep(instanceStep.StepID)
if err != nil {
continue
}
for _, rule := range flowStep.Rules {
// 评估条件表达式是否满足
dataToMap, err := instance.DataToMap()
if err != nil {
continue
}
conditionMet, err := rule.EvaluateCondition(dataToMap)
if err != nil {
return err
}
if conditionMet {
switch rule.Action {
case flow_definition.ActionAutoApprove:
// 自动通过
err := instanceStep.Approve("自动通过")
if err != nil {
return err
}
return nil
//case ActionAutoReject:
// // 自动驳回
// err := instanceService.RejectStep(ctx, instance, step, "自动驳回")
// if err != nil {
// return err
// }
// return nil
//case ActionReassignApprover:
// // 重新指定审批人
// newApprover, err := getNewApprover(container, data)
// if err != nil {
// return err
// }
// step.ApproverID = newApprover.ID
// // 更新步骤信息
// err = updateApprovalStep(step)
// if err != nil {
// return err
// }
// return nil
// 可以添加更多的 Action 处理逻辑
default:
return fmt.Errorf("unsupported action: %s", rule.Action)
}
}
}
}
return nil // 如果没有规则满足,正常返回
}
func (instance *ApprovalInstance) GetPathByFromStepKey(fromStepKey string) (*InstancePathConfig, error) {
return nil, nil
}