备份代码

This commit is contained in:
lulz1 2024-11-19 17:03:12 +08:00
parent 903cf7f516
commit 1dfc2f9245
30 changed files with 1266 additions and 103 deletions

View File

@ -8,4 +8,7 @@ const (
CreatorKey = "CreatorKey" CreatorKey = "CreatorKey"
ApproverKey = "ApproverKey" ApproverKey = "ApproverKey"
ApplicantKey = "ApplicantKey" ApplicantKey = "ApplicantKey"
TenantKey = "TenantKey"
AssociateKey = "AssociateKey"
) )

View File

@ -17,9 +17,13 @@ func RegisterRoutes(r *gin.Engine) error {
{ {
instancesGroup.POST("/create", api.InstancesCreate) instancesGroup.POST("/create", api.InstancesCreate)
instancesGroup.POST("/start", api.InstancesStart) instancesGroup.POST("/start", api.InstancesStart)
instancesGroup.POST("/start/rule", api.InstancesStartRule)
stepsGroup := instancesGroup.Group("/steps") stepsGroup := instancesGroup.Group("/steps")
{ {
stepsGroup.POST("/add", api.InstancesStepsAdd) stepsGroup.POST("/add", api.InstancesStepsAdd)
stepsGroup.POST("/approve", api.InstancesStepsApprove)
stepsGroup.POST("/reject", api.InstancesStepsReject)
stepsGroup.POST("/cancel", api.InstancesStepsCancel)
stepsGroup.POST("/update", api.InstancesStepsUpdate) stepsGroup.POST("/update", api.InstancesStepsUpdate)
stepsGroup.POST("/revert", api.InstancesStepsRevert) stepsGroup.POST("/revert", api.InstancesStepsRevert)
} }
@ -30,6 +34,5 @@ func RegisterRoutes(r *gin.Engine) error {
} }
} }
} }
return nil return nil
} }

View File

@ -13,6 +13,8 @@ type InstancesCreateParam struct {
FlowID int64 `json:"flow_id"` FlowID int64 `json:"flow_id"`
ApplicantKey string `json:"applicant_key"` //申请人ID ApplicantKey string `json:"applicant_key"` //申请人ID
CreatorKey string `json:"creator_key"` CreatorKey string `json:"creator_key"`
AssociateKey string `json:"associate_key"`
TenantKey string `json:"tenant_key"`
} }
// InstancesCreate handler // InstancesCreate handler
@ -33,6 +35,13 @@ func (api *FlowInstanceApi) InstancesCreate(c *gin.Context) {
data := make(map[string]interface{}) data := make(map[string]interface{})
data[base.ApplicantKey] = param.ApplicantKey data[base.ApplicantKey] = param.ApplicantKey
data[base.CreatorKey] = param.CreatorKey data[base.CreatorKey] = param.CreatorKey
if param.AssociateKey != "" {
data[base.AssociateKey] = param.AssociateKey
}
if param.TenantKey != "" {
data[base.TenantKey] = param.TenantKey
}
flow, err := definitionService.GetFlow(c, param.FlowID) flow, err := definitionService.GetFlow(c, param.FlowID)
if err != nil { if err != nil {
res.FailWithErr(c, err) res.FailWithErr(c, err)

View File

@ -0,0 +1,34 @@
package flow_instance
import (
"approveflow/app/http/base/res"
"approveflow/app/provider/flow_instance"
"approveflow/app/utils"
"github.com/Superdanda/hade/framework/gin"
)
type InstancesStartRuleParam struct {
InstanceID int64 `json:"instance_id"`
}
// InstancesStartRule handler
// @Summary 启动审批流自动规则
// @Description 启动审批流自动规则
// @ID instances-start-rule
// @Tags flow-instances
// @Accept json
// @Produce json
// @Param InstancesStartRuleParam body InstancesStartRuleParam true "启动实例ID"
// @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/start/rule [post]
func (api *FlowInstanceApi) InstancesStartRule(context *gin.Context) {
param := utils.QuickBind[InstancesStartParam](context)
instanceService := context.MustMake(flow_instance.FlowInstanceKey).(flow_instance.Service)
_, err := instanceService.StartInstanceRule(context, param.InstanceID)
if err != nil {
res.FailWithErr(context, err)
return
}
res.Success(context)
}

View File

@ -0,0 +1,36 @@
package flow_instance
import (
"approveflow/app/http/base/res"
"approveflow/app/provider/flow_instance"
"approveflow/app/utils"
"github.com/Superdanda/hade/framework/gin"
)
type InstancesStepsApproveParam struct {
InstanceID int64 `json:"instance_id"`
StepID int64 `json:"step_id"`
Comments string `json:"comments"`
}
// InstancesStepsApprove handler
// @Summary 审批通过
// @Description 根据实例号和节点号通过审批
// @ID instances-steps-approve
// @Tags flow-instances
// @Accept json
// @Produce json
// @Param InstancesStepsApproveParam body InstancesStepsApproveParam true "输入参数描述"
// @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/steps/approve [post]
func (api *FlowInstanceApi) InstancesStepsApprove(context *gin.Context) {
param := utils.QuickBind[InstancesStepsApproveParam](context)
instanceService := context.MustMake(flow_instance.FlowInstanceKey).(flow_instance.Service)
err := instanceService.ApproveStep(context, param.InstanceID, param.StepID, param.Comments)
if err != nil {
res.FailWithErr(context, err)
return
}
res.Success(context)
}

View File

@ -0,0 +1,34 @@
package flow_instance
import (
"approveflow/app/http/base/res"
"approveflow/app/provider/flow_instance"
"approveflow/app/utils"
"github.com/Superdanda/hade/framework/gin"
)
type InstancesStepsCancelParam struct {
InstanceID int64 `json:"instance_id"`
}
// InstancesStepsCancel handler
// @Summary 审批取消
// @Description 根据实例号取消审批
// @ID instances-steps-cancel
// @Tags flow-instances
// @Accept json
// @Produce json
// @Param InstancesStepsCancelParam body InstancesStepsCancelParam true "审批取消参数"
// @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/steps/cancel [post]
func (api *FlowInstanceApi) InstancesStepsCancel(context *gin.Context) {
param := utils.QuickBind[InstancesStepsCancelParam](context)
instanceService := context.MustMake(flow_instance.FlowInstanceKey).(flow_instance.Service)
err := instanceService.CancelInstance(context, param.InstanceID)
if err != nil {
res.FailWithErr(context, err)
return
}
res.Success(context)
}

View File

@ -0,0 +1,36 @@
package flow_instance
import (
"approveflow/app/http/base/res"
"approveflow/app/provider/flow_instance"
"approveflow/app/utils"
"github.com/Superdanda/hade/framework/gin"
)
type InstancesStepsRejectParam struct {
InstanceID int64 `json:"instance_id"`
StepID int64 `json:"step_id"`
Comments string `json:"comments"`
}
// InstancesStepsReject handler
// @Summary 审批驳回
// @Description 根据实例号和节点号驳回审批
// @ID instances-steps-reject
// @Tags flow-instances
// @Accept json
// @Produce json
// @Param InstancesStepsRejectParam body InstancesStepsRejectParam true "驳回参数"
// @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/steps/reject [post]
func (api *FlowInstanceApi) InstancesStepsReject(context *gin.Context) {
param := utils.QuickBind[InstancesStepsRejectParam](context)
instanceService := context.MustMake(flow_instance.FlowInstanceKey).(flow_instance.Service)
err := instanceService.RejectStep(context, param.InstanceID, param.StepID, param.Comments)
if err != nil {
res.FailWithErr(context, err)
return
}
res.Success(context)
}

View File

@ -9,6 +9,7 @@ type ApprovalInstanceDTO struct {
CurrentStepIDs []*CurrentStepDTO `json:"current_step_ids"` // 当前步骤ID CurrentStepIDs []*CurrentStepDTO `json:"current_step_ids"` // 当前步骤ID
Steps []*InstanceStepDTO `json:"steps"` // 实例步骤 Steps []*InstanceStepDTO `json:"steps"` // 实例步骤
DynamicPathConfigs []*DynamicPathConfigDTO `json:"dynamic_path_configs"` // 动态路径配置,自定义,不直接存储 DynamicPathConfigs []*DynamicPathConfigDTO `json:"dynamic_path_configs"` // 动态路径配置,自定义,不直接存储
Key string `json:"key"`
} }
type CurrentStepDTO struct { type CurrentStepDTO struct {
@ -25,6 +26,7 @@ type InstanceStepDTO struct {
ApproverComments string `json:"approver_comments"` // 审批意见 ApproverComments string `json:"approver_comments"` // 审批意见
IsDynamic bool `json:"is_dynamic"` // 是否为动态步骤 IsDynamic bool `json:"is_dynamic"` // 是否为动态步骤
Records []*ApprovalRecordDTO `json:"records"` // 审批记录 Records []*ApprovalRecordDTO `json:"records"` // 审批记录
Key string `json:"key"`
} }
type ApprovalRecordDTO struct { type ApprovalRecordDTO struct {
@ -34,6 +36,7 @@ type ApprovalRecordDTO struct {
Status string `json:"status"` // 审批状态 Status string `json:"status"` // 审批状态
Comments string `json:"comments"` // 审批意见 Comments string `json:"comments"` // 审批意见
IsTimeout bool `json:"is_timeout"` // 是否超时 IsTimeout bool `json:"is_timeout"` // 是否超时
Key string `json:"key"`
} }
type DynamicPathConfigDTO struct { type DynamicPathConfigDTO struct {
@ -43,4 +46,5 @@ type DynamicPathConfigDTO struct {
ToStepID int64 `json:"to_step_id"` // 目标步骤ID ToStepID int64 `json:"to_step_id"` // 目标步骤ID
IsParallel bool `json:"is_parallel"` // 是否并行 IsParallel bool `json:"is_parallel"` // 是否并行
Priority int `json:"priority"` // 路径优先级 Priority int `json:"priority"` // 路径优先级
Key string `json:"key"`
} }

View File

@ -8,6 +8,9 @@ import (
func MapApprovalInstanceToDTOBatch(approvalInstances []*instanceModel.ApprovalInstance) ([]*ApprovalInstanceDTO, error) { func MapApprovalInstanceToDTOBatch(approvalInstances []*instanceModel.ApprovalInstance) ([]*ApprovalInstanceDTO, error) {
res := make([]*ApprovalInstanceDTO, 0, len(approvalInstances)) res := make([]*ApprovalInstanceDTO, 0, len(approvalInstances))
for _, instance := range approvalInstances { for _, instance := range approvalInstances {
for _, step := range instance.Steps {
step.Status = step.GetStatus()
}
dto, err := MapApprovalInstanceToDTO(instance) dto, err := MapApprovalInstanceToDTO(instance)
if err != nil { if err != nil {
return nil, err return nil, err
@ -21,6 +24,10 @@ func MapApprovalInstanceToDTO(approvalInstance *instanceModel.ApprovalInstance)
// 创建目标 ApprovalInstanceDTO 对象 // 创建目标 ApprovalInstanceDTO 对象
approvalInstanceDTO := &ApprovalInstanceDTO{} approvalInstanceDTO := &ApprovalInstanceDTO{}
for _, step := range approvalInstance.Steps {
step.Status = step.GetStatus()
}
// 使用 Convert 方法将 实体对象 转换为 DTO // 使用 Convert 方法将 实体对象 转换为 DTO
if err := utils.Convert(approvalInstance, approvalInstanceDTO); err != nil { if err := utils.Convert(approvalInstance, approvalInstanceDTO); err != nil {
return nil, err return nil, err

View File

@ -408,6 +408,47 @@ const docTemplate = `{
} }
} }
}, },
"/instances/start/rule": {
"post": {
"description": "启动审批流自动规则",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "启动审批流自动规则",
"operationId": "instances-start-rule",
"parameters": [
{
"description": "启动实例ID",
"name": "InstancesStartRuleParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStartRuleParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/add": { "/instances/steps/add": {
"post": { "post": {
"description": "输入你的接口总结详情", "description": "输入你的接口总结详情",
@ -449,6 +490,129 @@ const docTemplate = `{
} }
} }
}, },
"/instances/steps/approve": {
"post": {
"description": "根据实例号和节点号通过审批",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "审批通过",
"operationId": "instances-steps-approve",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesStepsApproveParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsApproveParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/cancel": {
"post": {
"description": "根据实例号取消审批",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "审批取消",
"operationId": "instances-steps-cancel",
"parameters": [
{
"description": "审批取消参数",
"name": "InstancesStepsCancelParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsCancelParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/reject": {
"post": {
"description": "根据实例号和节点号驳回审批",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "审批驳回",
"operationId": "instances-steps-reject",
"parameters": [
{
"description": "驳回参数",
"name": "InstancesStepsRejectParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsRejectParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/revert": { "/instances/steps/revert": {
"post": { "post": {
"description": "输入你的接口总结详情", "description": "输入你的接口总结详情",
@ -673,6 +837,9 @@ const docTemplate = `{
"description": "主键ID", "description": "主键ID",
"type": "integer" "type": "integer"
}, },
"key": {
"type": "string"
},
"status": { "status": {
"description": "审批状态", "description": "审批状态",
"type": "string" "type": "string"
@ -709,6 +876,9 @@ const docTemplate = `{
"description": "是否超时", "description": "是否超时",
"type": "boolean" "type": "boolean"
}, },
"key": {
"type": "string"
},
"status": { "status": {
"description": "审批状态", "description": "审批状态",
"type": "string" "type": "string"
@ -745,6 +915,9 @@ const docTemplate = `{
"description": "是否并行", "description": "是否并行",
"type": "boolean" "type": "boolean"
}, },
"key": {
"type": "string"
},
"priority": { "priority": {
"description": "路径优先级", "description": "路径优先级",
"type": "integer" "type": "integer"
@ -778,6 +951,9 @@ const docTemplate = `{
"description": "是否为动态步骤", "description": "是否为动态步骤",
"type": "boolean" "type": "boolean"
}, },
"key": {
"type": "string"
},
"records": { "records": {
"description": "审批记录", "description": "审批记录",
"type": "array", "type": "array",
@ -839,9 +1015,53 @@ const docTemplate = `{
} }
} }
}, },
"flow_instance.InstancesStartRuleParam": {
"type": "object",
"properties": {
"instance_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsAddParam": { "flow_instance.InstancesStepsAddParam": {
"type": "object" "type": "object"
}, },
"flow_instance.InstancesStepsApproveParam": {
"type": "object",
"properties": {
"comments": {
"type": "string"
},
"instance_id": {
"type": "integer"
},
"step_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsCancelParam": {
"type": "object",
"properties": {
"instance_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsRejectParam": {
"type": "object",
"properties": {
"comments": {
"type": "string"
},
"instance_id": {
"type": "integer"
},
"step_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsRevertParam": { "flow_instance.InstancesStepsRevertParam": {
"type": "object" "type": "object"
}, },

View File

@ -401,6 +401,47 @@
} }
} }
}, },
"/instances/start/rule": {
"post": {
"description": "启动审批流自动规则",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "启动审批流自动规则",
"operationId": "instances-start-rule",
"parameters": [
{
"description": "启动实例ID",
"name": "InstancesStartRuleParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStartRuleParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/add": { "/instances/steps/add": {
"post": { "post": {
"description": "输入你的接口总结详情", "description": "输入你的接口总结详情",
@ -442,6 +483,129 @@
} }
} }
}, },
"/instances/steps/approve": {
"post": {
"description": "根据实例号和节点号通过审批",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "审批通过",
"operationId": "instances-steps-approve",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesStepsApproveParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsApproveParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/cancel": {
"post": {
"description": "根据实例号取消审批",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "审批取消",
"operationId": "instances-steps-cancel",
"parameters": [
{
"description": "审批取消参数",
"name": "InstancesStepsCancelParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsCancelParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/reject": {
"post": {
"description": "根据实例号和节点号驳回审批",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "审批驳回",
"operationId": "instances-steps-reject",
"parameters": [
{
"description": "驳回参数",
"name": "InstancesStepsRejectParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsRejectParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/revert": { "/instances/steps/revert": {
"post": { "post": {
"description": "输入你的接口总结详情", "description": "输入你的接口总结详情",
@ -666,6 +830,9 @@
"description": "主键ID", "description": "主键ID",
"type": "integer" "type": "integer"
}, },
"key": {
"type": "string"
},
"status": { "status": {
"description": "审批状态", "description": "审批状态",
"type": "string" "type": "string"
@ -702,6 +869,9 @@
"description": "是否超时", "description": "是否超时",
"type": "boolean" "type": "boolean"
}, },
"key": {
"type": "string"
},
"status": { "status": {
"description": "审批状态", "description": "审批状态",
"type": "string" "type": "string"
@ -738,6 +908,9 @@
"description": "是否并行", "description": "是否并行",
"type": "boolean" "type": "boolean"
}, },
"key": {
"type": "string"
},
"priority": { "priority": {
"description": "路径优先级", "description": "路径优先级",
"type": "integer" "type": "integer"
@ -771,6 +944,9 @@
"description": "是否为动态步骤", "description": "是否为动态步骤",
"type": "boolean" "type": "boolean"
}, },
"key": {
"type": "string"
},
"records": { "records": {
"description": "审批记录", "description": "审批记录",
"type": "array", "type": "array",
@ -832,9 +1008,53 @@
} }
} }
}, },
"flow_instance.InstancesStartRuleParam": {
"type": "object",
"properties": {
"instance_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsAddParam": { "flow_instance.InstancesStepsAddParam": {
"type": "object" "type": "object"
}, },
"flow_instance.InstancesStepsApproveParam": {
"type": "object",
"properties": {
"comments": {
"type": "string"
},
"instance_id": {
"type": "integer"
},
"step_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsCancelParam": {
"type": "object",
"properties": {
"instance_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsRejectParam": {
"type": "object",
"properties": {
"comments": {
"type": "string"
},
"instance_id": {
"type": "integer"
},
"step_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsRevertParam": { "flow_instance.InstancesStepsRevertParam": {
"type": "object" "type": "object"
}, },

View File

@ -97,6 +97,8 @@ definitions:
id: id:
description: 主键ID description: 主键ID
type: integer type: integer
key:
type: string
status: status:
description: 审批状态 description: 审批状态
type: string type: string
@ -123,6 +125,8 @@ definitions:
is_timeout: is_timeout:
description: 是否超时 description: 是否超时
type: boolean type: boolean
key:
type: string
status: status:
description: 审批状态 description: 审批状态
type: string type: string
@ -148,6 +152,8 @@ definitions:
is_parallel: is_parallel:
description: 是否并行 description: 是否并行
type: boolean type: boolean
key:
type: string
priority: priority:
description: 路径优先级 description: 路径优先级
type: integer type: integer
@ -172,6 +178,8 @@ definitions:
is_dynamic: is_dynamic:
description: 是否为动态步骤 description: 是否为动态步骤
type: boolean type: boolean
key:
type: string
records: records:
description: 审批记录 description: 审批记录
items: items:
@ -213,8 +221,36 @@ definitions:
instance_id: instance_id:
type: integer type: integer
type: object type: object
flow_instance.InstancesStartRuleParam:
properties:
instance_id:
type: integer
type: object
flow_instance.InstancesStepsAddParam: flow_instance.InstancesStepsAddParam:
type: object type: object
flow_instance.InstancesStepsApproveParam:
properties:
comments:
type: string
instance_id:
type: integer
step_id:
type: integer
type: object
flow_instance.InstancesStepsCancelParam:
properties:
instance_id:
type: integer
type: object
flow_instance.InstancesStepsRejectParam:
properties:
comments:
type: string
instance_id:
type: integer
step_id:
type: integer
type: object
flow_instance.InstancesStepsRevertParam: flow_instance.InstancesStepsRevertParam:
type: object type: object
flow_instance.InstancesStepsUpdateParam: flow_instance.InstancesStepsUpdateParam:
@ -473,6 +509,33 @@ paths:
summary: 启动审批流 summary: 启动审批流
tags: tags:
- flow-instances - flow-instances
/instances/start/rule:
post:
consumes:
- application/json
description: 启动审批流自动规则
operationId: instances-start-rule
parameters:
- description: 启动实例ID
in: body
name: InstancesStartRuleParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesStartRuleParam'
produces:
- application/json
responses:
"200":
description: 返回成功的流程定义数据
schema:
$ref: '#/definitions/base.Result'
"500":
description: 返回失败的流程定义数据
schema:
$ref: '#/definitions/base.Result'
summary: 启动审批流自动规则
tags:
- flow-instances
/instances/steps/add: /instances/steps/add:
post: post:
consumes: consumes:
@ -500,6 +563,87 @@ paths:
summary: 输入你的接口总结 summary: 输入你的接口总结
tags: tags:
- flow-instances - flow-instances
/instances/steps/approve:
post:
consumes:
- application/json
description: 根据实例号和节点号通过审批
operationId: instances-steps-approve
parameters:
- description: 输入参数描述
in: body
name: InstancesStepsApproveParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesStepsApproveParam'
produces:
- application/json
responses:
"200":
description: 返回成功的流程定义数据
schema:
$ref: '#/definitions/base.Result'
"500":
description: 返回失败的流程定义数据
schema:
$ref: '#/definitions/base.Result'
summary: 审批通过
tags:
- flow-instances
/instances/steps/cancel:
post:
consumes:
- application/json
description: 根据实例号取消审批
operationId: instances-steps-cancel
parameters:
- description: 审批取消参数
in: body
name: InstancesStepsCancelParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesStepsCancelParam'
produces:
- application/json
responses:
"200":
description: 返回成功的流程定义数据
schema:
$ref: '#/definitions/base.Result'
"500":
description: 返回失败的流程定义数据
schema:
$ref: '#/definitions/base.Result'
summary: 审批取消
tags:
- flow-instances
/instances/steps/reject:
post:
consumes:
- application/json
description: 根据实例号和节点号驳回审批
operationId: instances-steps-reject
parameters:
- description: 驳回参数
in: body
name: InstancesStepsRejectParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesStepsRejectParam'
produces:
- application/json
responses:
"200":
description: 返回成功的流程定义数据
schema:
$ref: '#/definitions/base.Result'
"500":
description: 返回失败的流程定义数据
schema:
$ref: '#/definitions/base.Result'
summary: 审批驳回
tags:
- flow-instances
/instances/steps/revert: /instances/steps/revert:
post: post:
consumes: consumes:

View File

@ -38,18 +38,23 @@ func NewOrmInstanceRepositoryImplAndRegister(container framework.Container) {
} }
func (u *InstanceRepositoryImpl) SaveToDB(entity *InstanceModel.ApprovalInstance) error { func (u *InstanceRepositoryImpl) SaveToDB(entity *InstanceModel.ApprovalInstance) error {
// 先保存 ApprovalFlow 本身
if entity.ID != 0 {
// 使用FullSaveAssociations 方法来确保模型的整体状态,包括其所有关联都反映在了数据库中,从在应用中保持数据的完整性和一致性
return u.db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(entity).Error
}
return u.db.Save(entity).Error return u.db.Save(entity).Error
} }
func (u *InstanceRepositoryImpl) FindByIDFromDB(id int64) (*InstanceModel.ApprovalInstance, error) { func (u *InstanceRepositoryImpl) FindByIDFromDB(id int64) (*InstanceModel.ApprovalInstance, error) {
entity := &InstanceModel.ApprovalInstance{} entity := &InstanceModel.ApprovalInstance{}
err := u.db.First(entity, id).Error err := u.db.Preload("CurrentStepIDs").Preload("StatusEvents").Preload("Steps").Preload("Steps.StatusEvents").Preload("Steps.Records").Preload("Steps.InstancePathConfigs").First(entity, id).Error
return entity, err return entity, err
} }
func (u *InstanceRepositoryImpl) FindByIDsFromDB(ids []int64) ([]*InstanceModel.ApprovalInstance, error) { func (u *InstanceRepositoryImpl) FindByIDsFromDB(ids []int64) ([]*InstanceModel.ApprovalInstance, error) {
var entities []*InstanceModel.ApprovalInstance var entities []*InstanceModel.ApprovalInstance
err := u.db.Where("id IN ?", ids).Find(&entities).Error err := u.db.Where("id IN ?", ids).Preload("StatusEvents").Preload("CurrentStepIDs").Preload("Steps").Preload("Steps.StatusEvents").Preload("Steps.Records").Preload("Steps.InstancePathConfigs").Find(&entities).Error
return entities, err return entities, err
} }

View File

@ -53,7 +53,7 @@ func (u *FlowRepositoryImpl) FindByIDFromDB(id int64) (*FlowDefinitionModule.App
func (u *FlowRepositoryImpl) FindByIDsFromDB(ids []int64) ([]*FlowDefinitionModule.ApprovalFlow, error) { func (u *FlowRepositoryImpl) FindByIDsFromDB(ids []int64) ([]*FlowDefinitionModule.ApprovalFlow, error) {
var entities []*FlowDefinitionModule.ApprovalFlow var entities []*FlowDefinitionModule.ApprovalFlow
err := u.db.Where("id IN ?", ids).Find(&entities).Error err := u.db.Where("id IN ?", ids).Preload("Steps").Preload("Steps.Rules").Preload("Steps.DynamicConfig").Preload("Steps.PathConfigs").Find(&entities).Error
return entities, err return entities, err
} }

View File

@ -16,6 +16,7 @@ type NodeManager interface {
SetNodes([]AbstractNode) SetNodes([]AbstractNode)
GetNodeMap() map[string]AbstractNode GetNodeMap() map[string]AbstractNode
NewNodePathConfig(fromNodeKey, toNodeKey string) AbstractNodePathConfig NewNodePathConfig(fromNodeKey, toNodeKey string) AbstractNodePathConfig
Delete()
} }
type NodeManagerS struct { type NodeManagerS struct {
@ -26,6 +27,7 @@ type AbstractNode interface {
GetPathConfigs() []AbstractNodePathConfig GetPathConfigs() []AbstractNodePathConfig
SetPathConfigs([]AbstractNodePathConfig) SetPathConfigs([]AbstractNodePathConfig)
GetKey() string GetKey() string
Delete()
} }
type AbstractNodePathConfig interface { type AbstractNodePathConfig interface {
@ -33,6 +35,7 @@ type AbstractNodePathConfig interface {
GetNodeID() int64 GetNodeID() int64
GetFromNodeKey() string GetFromNodeKey() string
GetToNodeKey() string GetToNodeKey() string
Delete()
} }
type Node struct { type Node struct {
@ -270,9 +273,11 @@ func addPath(nct NodeManager, fromNode, toNode AbstractNode) {
func removePath(nct NodeManager, fromNode, toNode AbstractNode) { func removePath(nct NodeManager, fromNode, toNode AbstractNode) {
var newPathConfigs []AbstractNodePathConfig var newPathConfigs []AbstractNodePathConfig
for _, pc := range fromNode.GetPathConfigs() { for _, pc := range fromNode.GetPathConfigs() {
if !(pc.GetFromNodeKey() == fromNode.GetKey() && pc.GetToNodeKey() == toNode.GetKey()) { if pc.GetFromNodeKey() == fromNode.GetKey() && pc.GetToNodeKey() == toNode.GetKey() {
newPathConfigs = append(newPathConfigs, pc) // 使用软删除的方式
pc.Delete()
} }
newPathConfigs = append(newPathConfigs, pc)
} }
fromNode.SetPathConfigs(newPathConfigs) fromNode.SetPathConfigs(newPathConfigs)
} }

View File

@ -11,7 +11,7 @@ type ApprovalCondition interface {
} }
type ApprovalConditionCommon struct { type ApprovalConditionCommon struct {
ApprovalCondition ApprovalCondition `gorm:"-"`
ConditionExpression string `gorm:"type:text" json:"condition_expression"` // 条件表达式 ConditionExpression string `gorm:"type:text" json:"condition_expression"` // 条件表达式
} }

View File

@ -3,6 +3,7 @@ package flow_definition
import ( import (
"approveflow/app/base" "approveflow/app/base"
userProvider "approveflow/app/provider/user" userProvider "approveflow/app/provider/user"
"encoding/json"
"github.com/Superdanda/hade/framework/gin" "github.com/Superdanda/hade/framework/gin"
) )
@ -22,6 +23,14 @@ type DynamicApprovalStepConfig struct {
base.Model // 通用字段,包括创建时间、更新时间等 base.Model // 通用字段,包括创建时间、更新时间等
} }
func (config *DynamicApprovalStepConfig) MarshalBinary() ([]byte, error) {
return json.Marshal(config)
}
func (config *DynamicApprovalStepConfig) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, config)
}
// NewDirectSupervisorRule 创建审批人为直属领导审批规则 // NewDirectSupervisorRule 创建审批人为直属领导审批规则
func NewDirectSupervisorRule() *DynamicApprovalStepConfig { func NewDirectSupervisorRule() *DynamicApprovalStepConfig {
directSupervisorApproval := &DynamicApprovalStepConfig{ directSupervisorApproval := &DynamicApprovalStepConfig{

View File

@ -15,6 +15,7 @@ type ApprovalFlow struct {
Description string `gorm:"type:text" json:"description"` // 流程描述 Description string `gorm:"type:text" json:"description"` // 流程描述
Steps []*ApprovalStep `gorm:"foreignKey:FlowID;constraint:OnDelete:CASCADE" json:"steps"` // 流程步骤 Steps []*ApprovalStep `gorm:"foreignKey:FlowID;constraint:OnDelete:CASCADE" json:"steps"` // 流程步骤
NodeMap map[string]connect.AbstractNode `gorm:"-" json:"node_map"` // 流程步骤 NodeMap map[string]connect.AbstractNode `gorm:"-" json:"node_map"` // 流程步骤
TenantKey string `gorm:"type:varchar(50);not null" json:"tenant_key"`
base.Model // 通用字段,包括创建时间、更新时间等 base.Model // 通用字段,包括创建时间、更新时间等
} }

View File

@ -38,6 +38,9 @@ func (step *ApprovalStep) SetPathConfigs(configs []connect.AbstractNodePathConfi
if err != nil { if err != nil {
panic(fmt.Sprintf("Error in SetPathConfigs: %v", err)) panic(fmt.Sprintf("Error in SetPathConfigs: %v", err))
} }
for _, path := range step.PathConfigs {
path.Delete()
}
step.PathConfigs = convertedConfigs step.PathConfigs = convertedConfigs
} }

View File

@ -1,6 +1,7 @@
package flow_definition package flow_definition
import ( import (
"approveflow/app/provider/abstract/connect"
"context" "context"
"fmt" "fmt"
"github.com/Superdanda/hade/framework" "github.com/Superdanda/hade/framework"
@ -40,18 +41,11 @@ func (f *FlowDefinitionService) DeleteFlow(ctx context.Context, flowID int64) er
func (f *FlowDefinitionService) AddStepWithPosition(ctx context.Context, flowID int64, step *ApprovalStep, fromStepId string, toStepKey string) error { func (f *FlowDefinitionService) AddStepWithPosition(ctx context.Context, flowID int64, step *ApprovalStep, fromStepId string, toStepKey string) error {
return f.handlerFlow(ctx, flowID, func(ctx context.Context, flow *ApprovalFlow) error { return f.handlerFlow(ctx, flowID, func(ctx context.Context, flow *ApprovalFlow) error {
fromStep, err := flow.GetStepByKey(fromStepId) fromStep, err := flow.GetStepByKey(fromStepId)
_, err = flow.GetStepByKey(toStepKey) toStep, err := flow.GetStepByKey(toStepKey)
if err != nil { if err != nil {
return err return err
} }
path, ok := fromStep.GetPathByToKey(toStepKey) err = connect.InsertNodeBetween(flow, fromStep, toStep, step)
if !ok {
return errors.New("这两个节点不相邻")
}
_, toPath := path.NewPathWithStep(step)
err = step.AddPathConfig(toPath)
err = flow.AddStep(step)
return err return err
}) })
} }

View File

@ -14,6 +14,8 @@ type Service interface {
CreateInstance(ctx *gin.Context, approvalFlow *flow_definition.ApprovalFlow, data map[string]interface{}) (*model.ApprovalInstance, error) CreateInstance(ctx *gin.Context, approvalFlow *flow_definition.ApprovalFlow, data map[string]interface{}) (*model.ApprovalInstance, error)
// StartInstance 功能:启动新的审批流程实例。 // StartInstance 功能:启动新的审批流程实例。
StartInstance(ctx context.Context, instanceID int64) (*model.ApprovalInstance, error) StartInstance(ctx context.Context, instanceID int64) (*model.ApprovalInstance, error)
// StartInstanceRule 功能:启动新的审批流程实例规则。
StartInstanceRule(ctx context.Context, instanceID int64) (*model.ApprovalInstance, error)
// GetInstance 功能:获取指定流程实例的详细信息。 // GetInstance 功能:获取指定流程实例的详细信息。
GetInstance(ctx context.Context, instanceID int64) (*model.ApprovalInstance, error) GetInstance(ctx context.Context, instanceID int64) (*model.ApprovalInstance, error)
// GetInstancePage 功能:分页查询流程实例的详细信息。 // GetInstancePage 功能:分页查询流程实例的详细信息。

View File

@ -15,6 +15,7 @@ import (
const ( const (
StatusCreated = "Created" // 审批实例已创建 StatusCreated = "Created" // 审批实例已创建
StatusReject = "Reject" // 审批实例已创建
StatusInProgress = "InProgress" // 审批进行中 StatusInProgress = "InProgress" // 审批进行中
StatusCompleted = "Completed" // 审批完成 StatusCompleted = "Completed" // 审批完成
StatusCancelled = "Cancelled" // 审批取消 StatusCancelled = "Cancelled" // 审批取消
@ -26,14 +27,47 @@ type ApprovalInstance struct {
FlowID int64 `gorm:"type:bigint;index;not null" json:"flow_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 ApplicantKey string `gorm:"type:bigint;index;not null" json:"approver_id"` //申请人ID
CreatorKey string `gorm:"type:bigint;not null" json:"creator_id"` // 创建者ID CreatorKey string `gorm:"type:bigint;not null" json:"creator_id"` // 创建者ID
Status string `gorm:"type:varchar(50);not null" json:"status"` // 审批状态 Status string `gorm:"-" json:"status"` // 审批状态
CurrentStepIDs []*CurrentStep `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE" json:"current_step_ids"` // 当前步骤ID CurrentStepIDs []*CurrentStep `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE" json:"current_step_ids"` // 当前步骤ID
Steps []*InstanceStep `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE" json:"steps"` // 实例步骤 Steps []*InstanceStep `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE" json:"steps"` // 实例步骤
Data string `gorm:"type:json" json:"data"` // 保存审批数据的 JSON StatusEvents []*InstanceStatusEvent `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE" json:"status_events"` // 实例步骤
Data string `gorm:"type:json" json:"data"`
NodeMap map[string]connect.AbstractNode `gorm:"-" json:"node_map"` // 保存审批数据的 JSON
TenantKey string `gorm:"type:varchar(50);not null" json:"tenant_key"`
AssociateKey string `gorm:"type:varchar(50);not null" json:"associate_key"`
sync.RWMutex sync.RWMutex
base.Model base.Model
} }
// InstanceStatusEvent 审批实例状态表
type InstanceStatusEvent struct {
InstanceID int64 `gorm:"type:bigint;not null" json:"instance_id"`
Status string `gorm:"type:varchar(50);not null" json:"status"`
Extension string `gorm:"type:varchar(50);not null" json:"extension"`
base.Model
}
func (instance *ApprovalInstance) CheckIfComplete() bool {
currentSteps := instance.GetCurrentSteps()
if instance.GetStatus() == StatusCompleted {
return true
}
if len(currentSteps) == 1 && currentSteps[0].StepCode == flow_definition.StepEnd {
currentSteps[0].AddCompletedEvent()
instance.AddCompletedEvent()
return true
}
return false
}
func (instance *ApprovalInstance) MarshalBinary() ([]byte, error) {
return json.Marshal(instance)
}
func (instance *ApprovalInstance) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, instance)
}
func (instance *ApprovalInstance) GetNodes() []connect.AbstractNode { func (instance *ApprovalInstance) GetNodes() []connect.AbstractNode {
return utils.ConvertToAbstractNodes(instance.Steps, func(step *InstanceStep) connect.AbstractNode { return utils.ConvertToAbstractNodes(instance.Steps, func(step *InstanceStep) connect.AbstractNode {
return step return step
@ -41,24 +75,41 @@ func (instance *ApprovalInstance) GetNodes() []connect.AbstractNode {
} }
func (instance *ApprovalInstance) SetNodes(nodes []connect.AbstractNode) { func (instance *ApprovalInstance) SetNodes(nodes []connect.AbstractNode) {
//TODO implement me specificType, _ := utils.ConvertToSpecificType[*InstanceStep, connect.AbstractNode](nodes, func(node connect.AbstractNode) (*InstanceStep, bool) {
panic("implement me") return node.(*InstanceStep), true
})
instance.Steps = specificType
} }
func (instance *ApprovalInstance) GetNodeMap() map[string]connect.AbstractNode { func (instance *ApprovalInstance) GetNodeMap() map[string]connect.AbstractNode {
//TODO implement me if instance.NodeMap == nil {
panic("implement me") instance.NodeMap = map[string]connect.AbstractNode{}
connect.InitializeNodeMap(instance)
}
return instance.NodeMap
} }
func (instance *ApprovalInstance) NewNodePathConfig(fromNodeKey, toNodeKey string) connect.AbstractNodePathConfig { func (instance *ApprovalInstance) NewNodePathConfig(fromNodeKey, toNodeKey string) connect.AbstractNodePathConfig {
//TODO implement me fromStep, err := instance.GetStepByKey(fromNodeKey)
panic("implement me") if err != nil {
return nil
}
return NewDynamicPathConfig(instance.ID, fromStep.ID, fromNodeKey, toNodeKey, false)
} }
// CurrentStep 目前审批中的节点 // CurrentStep 目前审批中的节点
type CurrentStep struct { type CurrentStep struct {
CurrentStepId int64 `gorm:"type:bigint;index;not null" json:"current_step_id"` CurrentStepId int64 `gorm:"type:bigint;index;not null" json:"current_step_id"`
InstanceID int64 `gorm:"type:bigint;index;not null" json:"instance_id"` InstanceID int64 `gorm:"type:bigint;index;not null" json:"instance_id"`
base.Model
}
func (current *CurrentStep) MarshalBinary() ([]byte, error) {
return json.Marshal(current)
}
func (current *CurrentStep) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, current)
} }
// NewApprovalInstance 初始化创建一个审批流实例 // NewApprovalInstance 初始化创建一个审批流实例
@ -78,6 +129,15 @@ func NewApprovalInstance(
Steps: []*InstanceStep{}, Steps: []*InstanceStep{},
Data: string(bytes), Data: string(bytes),
} }
// 保存租户键和关联键
if value, ok := data[base.TenantKey]; ok {
instance.TenantKey = value.(string)
}
if value, ok := data[base.AssociateKey]; ok {
instance.TenantKey = value.(string)
}
// Get the first step of the approval flow // Get the first step of the approval flow
firstStep, err := approvalFlow.FirstStep() firstStep, err := approvalFlow.FirstStep()
if err != nil { if err != nil {
@ -85,15 +145,19 @@ func NewApprovalInstance(
} }
// Build the steps recursively // Build the steps recursively
processedSteps := make(map[string]*InstanceStep) processedSteps := make(map[string]*InstanceStep)
err = buildInstanceSteps(ctx, instance, firstStep, nil, data, processedSteps) err = buildInstanceSteps(ctx, approvalFlow, instance, firstStep, nil, data, processedSteps)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 赋予创建状态
instance.AddCreatedEvent()
return instance, nil return instance, nil
} }
// buildInstanceSteps recursively builds instance steps from approval steps // buildInstanceSteps recursively builds instance steps from approval steps
func buildInstanceSteps(ctx *gin.Context, instance *ApprovalInstance, currentApprovalStep *flow_definition.ApprovalStep, func buildInstanceSteps(ctx *gin.Context, approvalFlow *flow_definition.ApprovalFlow,
instance *ApprovalInstance, currentApprovalStep *flow_definition.ApprovalStep,
previousInstanceStep *InstanceStep, data map[string]interface{}, processedSteps map[string]*InstanceStep) error { previousInstanceStep *InstanceStep, data map[string]interface{}, processedSteps map[string]*InstanceStep) error {
// Check if this step has already been processed to avoid cycles // Check if this step has already been processed to avoid cycles
if existingInstanceStep, ok := processedSteps[currentApprovalStep.Key]; ok { if existingInstanceStep, ok := processedSteps[currentApprovalStep.Key]; ok {
@ -102,15 +166,21 @@ func buildInstanceSteps(ctx *gin.Context, instance *ApprovalInstance, currentApp
} }
return nil return nil
} }
// Create a new instance step
var approverKey string var approverKey string
if currentApprovalStep.DynamicConfig != nil {
approver, err := currentApprovalStep.DynamicConfig.GetApprover(ctx, data) approver, err := currentApprovalStep.DynamicConfig.GetApprover(ctx, data)
if err != nil { if err != nil {
approverKey = "" approverKey = ""
} else { } else {
approverKey = approver.Key approverKey = approver.Key
} }
} else {
approverKey = ""
}
currentInstanceStep := NewInstanceStep(currentApprovalStep, approverKey, false) currentInstanceStep := NewInstanceStep(currentApprovalStep, approverKey, false)
// Insert the instance step into the instance // Insert the instance step into the instance
if previousInstanceStep == nil { if previousInstanceStep == nil {
connect.InsertNodeFirst(instance, currentInstanceStep) connect.InsertNodeFirst(instance, currentInstanceStep)
@ -119,14 +189,16 @@ func buildInstanceSteps(ctx *gin.Context, instance *ApprovalInstance, currentApp
} }
// Mark this step as processed // Mark this step as processed
processedSteps[currentApprovalStep.Key] = currentInstanceStep processedSteps[currentApprovalStep.Key] = currentInstanceStep
// Get the next steps and recursively build them // Get the next steps and recursively build them
nextSteps, err := connect.GetNextNodes(instance, currentApprovalStep) flowStep, err := approvalFlow.GetStep(currentInstanceStep.StepID)
nextFlowSteps, err := connect.GetNextNodes(approvalFlow, flowStep)
if err != nil { if err != nil {
return err return err
} }
for _, nextStep := range nextSteps { for _, nextStep := range nextFlowSteps {
nextApprovalStep := nextStep.(*flow_definition.ApprovalStep) nextApprovalStep := nextStep.(*flow_definition.ApprovalStep)
err := buildInstanceSteps(ctx, instance, nextApprovalStep, currentInstanceStep, data, processedSteps) err := buildInstanceSteps(ctx, approvalFlow, instance, nextApprovalStep, currentInstanceStep, data, processedSteps)
if err != nil { if err != nil {
return err return err
} }
@ -162,7 +234,7 @@ func (instance *ApprovalInstance) Start(ctx context.Context) error {
instance.RLock() instance.RLock()
defer instance.RUnlock() defer instance.RUnlock()
if instance.Status != StatusCreated { if instance.GetStatus() != StatusCreated && instance.GetStatus() != StatusReject {
return fmt.Errorf("审批流程已启动或已完成,无法再次启动") return fmt.Errorf("审批流程已启动或已完成,无法再次启动")
} }
@ -174,7 +246,7 @@ func (instance *ApprovalInstance) Start(ctx context.Context) error {
// 设置当前步骤为第一个步骤,并更改状态 // 设置当前步骤为第一个步骤,并更改状态
instance.AddCurrentStepID(firstStep) instance.AddCurrentStepID(firstStep)
instance.Status = StatusInProgress instance.AddInProgressEvent()
err = instance.SendApprovalNotification(firstStep, ctx) err = instance.SendApprovalNotification(firstStep, ctx)
if err != nil { if err != nil {
@ -210,19 +282,19 @@ func (instance *ApprovalInstance) MoveToNextStep() error {
} }
currentStep := currentStepSlice[0] currentStep := currentStepSlice[0]
// 完成当前步骤 // 完成当前步骤
currentStep.Status = StepStatusCompleted currentStep.AddCompletedEvent()
// 获取下一个步骤 // 获取下一个步骤
var nextStep *InstanceStep var nextStep *InstanceStep
nextStepArr, err := instance.getNextStep(currentStep, dataMap) nextStepArr, err := instance.getNextStep(currentStep, dataMap)
if nextStepArr != nil && len(nextStepArr) == 0 { if nextStepArr != nil && len(nextStepArr) == 1 {
nextStep = nextStepArr[0] nextStep = nextStepArr[0]
} else { } else {
return errors.New("暂时不支持并行审批") return errors.New("暂时不支持并行审批")
} }
if err != nil { if err != nil {
instance.Status = StatusCompleted // 流程结束 instance.AddCompletedEvent() // 流程结束
return nil return nil
} }
@ -230,7 +302,8 @@ func (instance *ApprovalInstance) MoveToNextStep() error {
instance.RemoveCurrentStepID(currentStep.ID) instance.RemoveCurrentStepID(currentStep.ID)
instance.AddCurrentStepID(nextStep) instance.AddCurrentStepID(nextStep)
nextStep.Status = StepStatusPending nextStep.AddPendingEvent()
return nil return nil
} }
@ -303,7 +376,7 @@ func (instance *ApprovalInstance) GetStepByKey(key string) (*InstanceStep, error
return step, nil return step, nil
} }
} }
return nil, fmt.Errorf("step with key %d not found in approval flow", key) return nil, fmt.Errorf("step with key %s not found in approval flow", key)
} }
// AddCurrentStepID adds a new CurrentStep to the CurrentStepIDs slice if it does not already exist // AddCurrentStepID adds a new CurrentStep to the CurrentStepIDs slice if it does not already exist
@ -324,6 +397,7 @@ func (instance *ApprovalInstance) RemoveCurrentStepID(stepID int64) {
for i, s := range instance.CurrentStepIDs { for i, s := range instance.CurrentStepIDs {
if s.CurrentStepId == stepID { if s.CurrentStepId == stepID {
// Remove the step by slicing // Remove the step by slicing
s.Delete()
instance.CurrentStepIDs = append(instance.CurrentStepIDs[:i], instance.CurrentStepIDs[i+1:]...) instance.CurrentStepIDs = append(instance.CurrentStepIDs[:i], instance.CurrentStepIDs[i+1:]...)
return return
} }
@ -343,7 +417,7 @@ func (instance *ApprovalInstance) HasCurrentStepID(stepID int64) bool {
// GetCurrentSteps returns a copy of the CurrentSteps slice // GetCurrentSteps returns a copy of the CurrentSteps slice
func (instance *ApprovalInstance) GetCurrentSteps() []*InstanceStep { func (instance *ApprovalInstance) GetCurrentSteps() []*InstanceStep {
// Return a copy of the slice to avoid modifications // Return a copy of the slice to avoid modifications
currentSteps := make([]*InstanceStep, len(instance.CurrentStepIDs)) var currentSteps []*InstanceStep
for _, step := range instance.Steps { for _, step := range instance.Steps {
if instance.HasCurrentStepID(step.ID) { if instance.HasCurrentStepID(step.ID) {
currentSteps = append(currentSteps, step) currentSteps = append(currentSteps, step)
@ -362,9 +436,60 @@ func (instance *ApprovalInstance) GetCurrentStepsById(id int64) *InstanceStep {
return nil return nil
} }
// executeApprovalStep 执行审批步骤 func (instance *ApprovalInstance) ExecuteApprovalReversal(reversal *ApprovalReversal) error {
func (instance *ApprovalInstance) executeApprovalStep(approvalFlow flow_definition.ApprovalFlow) error { step, err := instance.GetStepByKey(reversal.StepKey)
for _, instanceStep := range instance.GetCurrentSteps() { if err != nil {
return err
}
currentStep := instance.GetCurrentStepsById(step.ID)
if currentStep == nil {
return errors.New("当前反转节点不是正在审批的节点")
}
switch reversal.FixAction {
case FixActionReApproveAndUpdateData:
err := currentStep.Reject(reversal.Reason)
if err != nil {
return err
}
// 移除当前所有节点,替换为开始节点
for _, step := range instance.GetCurrentSteps() {
instance.RemoveCurrentStepID(step.ID)
}
reversalStep, err := instance.GetStepByKey(reversal.ReversedStepKey)
if err != nil {
return err
}
for _, step := range instance.Steps {
// 所有节点重新加入待审批事件
if step.GetStatus() == StepStatusApproved {
// 如果是通过的话要再审批
step.AddPendingEvent()
}
}
instance.AddCurrentStepID(reversalStep)
}
return nil
}
func (instance *ApprovalInstance) Reversal(step *InstanceStep, reversedStep *InstanceStep, reason string, fixAction string) error {
reversal := NewReversal(step, reversedStep, reason, fixAction)
step.addApprovalReversal(reversal)
instance.AddRejectEvent()
return instance.ExecuteApprovalReversal(reversal)
}
// ExecuteApprovalStep 执行审批步骤规则
func (instance *ApprovalInstance) ExecuteApprovalStep(approvalFlow *flow_definition.ApprovalFlow) error {
currentSteps := instance.GetCurrentSteps()
for _, instanceStep := range currentSteps {
// 只对待审批的节点进行处理
if instanceStep.GetStatus() != StepStatusPending {
continue
}
flowStep, err := approvalFlow.GetStep(instanceStep.StepID) flowStep, err := approvalFlow.GetStep(instanceStep.StepID)
if err != nil { if err != nil {
continue continue
@ -415,11 +540,88 @@ func (instance *ApprovalInstance) executeApprovalStep(approvalFlow flow_definiti
} }
} }
} }
return instance.CheckIfMoveToNextStep(currentSteps)
}
return nil // 如果没有规则满足,正常返回 func (instance *ApprovalInstance) CheckIfMoveToNextStep(currentSteps []*InstanceStep) error {
if instance.CheckIfComplete() {
return errors.New("当前审批流已结束")
}
// 如果当前节点所有的处理完毕,那么当前节点将向后移动
currentIsCompleted := true
for _, step := range currentSteps {
if step.GetStatus() != StepStatusCompleted && step.GetStatus() != StepStatusApproved {
currentIsCompleted = false
}
}
if currentIsCompleted {
err := instance.MoveToNextStep()
if err != nil {
return err
}
instance.CheckIfComplete()
return nil
}
return nil
} }
func (instance *ApprovalInstance) GetPathByFromStepKey(fromStepKey string) (*InstancePathConfig, error) { func (instance *ApprovalInstance) GetPathByFromStepKey(fromStepKey string) (*InstancePathConfig, error) {
return nil, nil return nil, nil
} }
func (instance *ApprovalInstance) GetStartStep() *InstanceStep {
for _, step := range instance.Steps {
if step.StepCode == flow_definition.StepStart {
return step
}
}
return nil
}
func (instance *ApprovalInstance) GetEndStep() *InstanceStep {
for _, step := range instance.Steps {
if step.StepCode == flow_definition.StepEnd {
return step
}
}
return nil
}
func (instance *ApprovalInstance) GetStatus() string {
return instance.StatusEvents[len(instance.StatusEvents)-1].Status
}
func (instance *ApprovalInstance) AddStatusEvent(status string) {
if instance.StatusEvents == nil {
instance.StatusEvents = make([]*InstanceStatusEvent, 0)
}
if len(instance.StatusEvents) > 0 && instance.GetStatus() == status {
return
}
instance.StatusEvents = append(instance.StatusEvents, &InstanceStatusEvent{
InstanceID: instance.ID, Status: status, Extension: "",
})
instance.Status = status
}
func (instance *ApprovalInstance) AddCreatedEvent() {
instance.AddStatusEvent(StatusCreated)
}
func (instance *ApprovalInstance) AddRejectEvent() {
instance.AddStatusEvent(StatusReject)
}
func (instance *ApprovalInstance) AddInProgressEvent() {
instance.AddStatusEvent(StatusInProgress)
}
func (instance *ApprovalInstance) AddCompletedEvent() {
instance.AddStatusEvent(StatusCompleted)
}
func (instance *ApprovalInstance) AddCancelledEvent() {
instance.AddStatusEvent(StatusCancelled)
}

View File

@ -2,6 +2,7 @@ package model
import ( import (
"approveflow/app/base" "approveflow/app/base"
"encoding/json"
) )
const ( const (
@ -21,6 +22,14 @@ type ApprovalRecord struct {
base.Model base.Model
} }
func (record *ApprovalRecord) MarshalBinary() ([]byte, error) {
return json.Marshal(record)
}
func (record *ApprovalRecord) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, record)
}
// CreateRecord 创建新的审批记录 // CreateRecord 创建新的审批记录
func (record *ApprovalRecord) CreateRecord(stepID int64, approverKey, status, comments string) { func (record *ApprovalRecord) CreateRecord(stepID int64, approverKey, status, comments string) {
record.InstanceStepID = stepID record.InstanceStepID = stepID

View File

@ -1,28 +1,48 @@
package model package model
import "approveflow/app/base" import (
"approveflow/app/base"
"encoding/json"
)
// ApprovalReversal 审批反转表 // ApprovalReversal 审批反转表
type ApprovalReversal struct { type ApprovalReversal struct {
InstanceID int64 `gorm:"index;not null" json:"instance_id"` // 关联的审批实例ID InstanceID int64 `gorm:"type:bigint;index;not null" json:"instance_id"` // 关联的审批实例ID
StepID int64 `gorm:"index;not null" json:"step_id"` // 关联的步骤ID StepID int64 `gorm:"type:bigint;index;not null" json:"step_id"` // 关联的步骤ID
ReversedStepID int64 `gorm:"index;not null" json:"reversed_step_id"` // 反转的步骤ID StepKey string `gorm:"type:varchar(50);index;not null" json:"step_key"` // 关联的步骤ID
ReversedStepKey string `gorm:"type:varchar(50);index;not null" json:"reversed_step_id"` // 反转的步骤ID
FixAction string `gorm:"type:varchar(100);not null" json:"fix_action"` // 修正动作 FixAction string `gorm:"type:varchar(100);not null" json:"fix_action"` // 修正动作
Reason string `gorm:"type:text" json:"reason"` // 反转原因 Reason string `gorm:"type:text" json:"reason"` // 反转原因
base.Model base.Model
} }
// ReverseStep 执行步骤的反转操作 // FixAction 定义常量表示审批反转的修正动作
func (reversal *ApprovalReversal) ReverseStep(step *InstanceStep, reason string) error { const (
reversal.InstanceID = step.InstanceID FixActionReApproveAndUpdateData = "ReApproveAndUpdateData"
reversal.StepID = step.StepID FixActionReApprove = "ReApprove" // 重新审批
reversal.ReversedStepID = step.ID FixActionReassignApprover = "ReassignApprover" // 重新指定审批人
reversal.Reason = reason FixActionRollback = "Rollback" // 回滚到某个步骤
reversal.FixAction = "Reversal" FixActionUpdateData = "UpdateData" // 更新审批数据
FixActionReopen = "Reopen" // 重新打开审批流程
)
// 更新步骤状态为反转 func (reversal *ApprovalReversal) MarshalBinary() ([]byte, error) {
step.Status = "Reversed" return json.Marshal(reversal)
return nil }
func (reversal *ApprovalReversal) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, reversal)
}
func NewReversal(step *InstanceStep, reversedStep *InstanceStep, reason string, fixAction string) *ApprovalReversal {
reversal := &ApprovalReversal{
StepID: step.ID,
StepKey: step.Key,
ReversedStepKey: reversedStep.Key,
FixAction: fixAction,
Reason: reason,
}
return reversal
} }
// FixIssue 执行修正操作 // FixIssue 执行修正操作

View File

@ -2,6 +2,7 @@ package model
import ( import (
"approveflow/app/base" "approveflow/app/base"
"encoding/json"
"time" "time"
) )
@ -15,3 +16,17 @@ type BatchApprovalTask struct {
ActionTime time.Time `json:"action_time"` // 审批时间 ActionTime time.Time `json:"action_time"` // 审批时间
base.Model base.Model
} }
type BatchApprovalTaskContent struct {
InstanceID int64
InstanceStepID int64
base.Model
}
func (task *BatchApprovalTask) MarshalBinary() ([]byte, error) {
return json.Marshal(task)
}
func (task *BatchApprovalTask) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, task)
}

View File

@ -3,6 +3,7 @@ package model
import ( import (
"approveflow/app/base" "approveflow/app/base"
"approveflow/app/provider/flow_definition" "approveflow/app/provider/flow_definition"
"encoding/json"
) )
// InstancePathConfig 动态路径配置表结构 // InstancePathConfig 动态路径配置表结构
@ -18,19 +19,27 @@ type InstancePathConfig struct {
base.Model base.Model
} }
func (d InstancePathConfig) GetKey() string { func (d *InstancePathConfig) MarshalBinary() ([]byte, error) {
return json.Marshal(d)
}
func (d *InstancePathConfig) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, d)
}
func (d *InstancePathConfig) GetKey() string {
return d.Key return d.Key
} }
func (d InstancePathConfig) GetNodeID() int64 { func (d *InstancePathConfig) GetNodeID() int64 {
return d.NodeID return d.NodeID
} }
func (d InstancePathConfig) GetFromNodeKey() string { func (d *InstancePathConfig) GetFromNodeKey() string {
return d.FromNodeKey return d.FromNodeKey
} }
func (d InstancePathConfig) GetToNodeKey() string { func (d *InstancePathConfig) GetToNodeKey() string {
return d.ToNodeKey return d.ToNodeKey
} }
@ -47,11 +56,11 @@ func NewDynamicPathConfig(instanceID, nodeId int64, FromStepKey, ToStepKey strin
} }
// IsConditionMet 判断路径条件是否满足 // IsConditionMet 判断路径条件是否满足
func (a *InstancePathConfig) IsConditionMet(data map[string]interface{}) (bool, error) { func (d *InstancePathConfig) IsConditionMet(data map[string]interface{}) (bool, error) {
if a.ConditionExpression == "" { if d.ConditionExpression == "" {
return true, nil // 无条件,默认满足 return true, nil // 无条件,默认满足
} }
result, err := a.EvaluateCondition(data) result, err := d.EvaluateCondition(data)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -5,6 +5,7 @@ import (
"approveflow/app/provider/abstract/connect" "approveflow/app/provider/abstract/connect"
"approveflow/app/provider/flow_definition" "approveflow/app/provider/flow_definition"
"approveflow/app/utils" "approveflow/app/utils"
"encoding/json"
"fmt" "fmt"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -25,14 +26,75 @@ type InstanceStep struct {
StepName string `gorm:"type:varchar(50);not null" json:"step_name"` StepName string `gorm:"type:varchar(50);not null" json:"step_name"`
StepCode string `gorm:"type:varchar(100)" json:"step_code"` // 步骤编号用于标识特殊的审批节点 StepCode string `gorm:"type:varchar(100)" json:"step_code"` // 步骤编号用于标识特殊的审批节点
ApproverKey string `gorm:"type:varchar(50);not null" json:"approver_id"` // 审批人ID ApproverKey string `gorm:"type:varchar(50);not null" json:"approver_id"` // 审批人ID
Status string `gorm:"type:varchar(50);not null" json:"status"` // 审批状态 Status string `gorm:"-" json:"status"` // 审批状态
ApproverComments string `gorm:"type:text" json:"approver_comments"` // 审批意见 ApproverComments string `gorm:"type:text" json:"approver_comments"` // 审批意见
IsDynamic bool `gorm:"not null;default:false" json:"is_dynamic"` // 是否为动态步骤 IsDynamic bool `gorm:"not null;default:false" json:"is_dynamic"` // 是否为动态步骤
StatusEvents []*InstanceStepStatusEvent `gorm:"foreignKey:StepID;constraint:OnDelete:CASCADE" json:"status_events"` // 状态事件表
InstancePathConfigs []*InstancePathConfig `gorm:"foreignKey:NodeID;constraint:OnDelete:CASCADE" json:"instance_path_configs"` // 动态路径配置,自定义,不直接存储 InstancePathConfigs []*InstancePathConfig `gorm:"foreignKey:NodeID;constraint:OnDelete:CASCADE" json:"instance_path_configs"` // 动态路径配置,自定义,不直接存储
Records []*ApprovalRecord `gorm:"foreignKey:InstanceStepID;constraint:OnDelete:CASCADE" json:"records"` // 审批记录 Records []*ApprovalRecord `gorm:"foreignKey:InstanceStepID;constraint:OnDelete:CASCADE" json:"records"` // 审批记录
Reversals []*ApprovalReversal `gorm:"foreignKey:StepID;constraint:OnDelete:CASCADE" json:"reversals"` // 反转记录
base.Model base.Model
} }
// InstanceStepStatusEvent 审批步骤状态表
type InstanceStepStatusEvent struct {
StepID int64 `gorm:"type:bigint;not null" json:"step_id"`
Status string `gorm:"type:varchar(50);not null" json:"status"`
Extension string `gorm:"type:varchar(50);not null" json:"extension"`
base.Model
}
type ByCreatedAt []*InstanceStepStatusEvent
func (a ByCreatedAt) Len() int { return len(a) }
func (a ByCreatedAt) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByCreatedAt) Less(i, j int) bool { return a[i].CreatedAt.After(a[j].CreatedAt) }
func (step *InstanceStep) GetStatus() string {
return step.StatusEvents[len(step.StatusEvents)-1].Status
}
func (step *InstanceStep) AddStatusEvent(status string) {
if step.StatusEvents == nil {
step.StatusEvents = make([]*InstanceStepStatusEvent, 0)
}
if len(step.StatusEvents) > 0 && step.GetStatus() == status {
return
}
step.StatusEvents = append(step.StatusEvents, &InstanceStepStatusEvent{
StepID: step.ID, Status: status, Extension: "",
})
step.Status = status
}
func (step *InstanceStep) AddPendingEvent() {
step.AddStatusEvent(StepStatusPending)
}
func (step *InstanceStep) AddApprovedEvent() {
step.AddStatusEvent(StepStatusApproved)
}
func (step *InstanceStep) AddRejectedEvent() {
step.AddStatusEvent(StepStatusRejected)
}
func (step *InstanceStep) AddReversedEvent() {
step.AddStatusEvent(StepStatusReversed)
}
func (step *InstanceStep) AddCompletedEvent() {
step.AddStatusEvent(StepStatusCompleted)
}
func (step *InstanceStep) MarshalBinary() ([]byte, error) {
return json.Marshal(step)
}
func (step *InstanceStep) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, step)
}
func (step *InstanceStep) GetPathConfigs() []connect.AbstractNodePathConfig { func (step *InstanceStep) GetPathConfigs() []connect.AbstractNodePathConfig {
return utils.ConvertToAbstractNodes(step.InstancePathConfigs, func(t *InstancePathConfig) connect.AbstractNodePathConfig { return utils.ConvertToAbstractNodes(step.InstancePathConfigs, func(t *InstancePathConfig) connect.AbstractNodePathConfig {
return t return t
@ -60,19 +122,19 @@ func NewInstanceStep(flowStep *flow_definition.ApprovalStep, approverKey string,
StepName: flowStep.Name, StepName: flowStep.Name,
StepCode: flowStep.StepCode, StepCode: flowStep.StepCode,
ApproverKey: approverKey, ApproverKey: approverKey,
Status: StepStatusPending,
IsDynamic: isDynamic, IsDynamic: isDynamic,
} }
instanceStep.AddPendingEvent()
instanceStep.Key = uuid.New().String() instanceStep.Key = uuid.New().String()
return instanceStep return instanceStep
} }
// Approve 审批通过 // Approve 审批通过
func (step *InstanceStep) Approve(comments string) error { func (step *InstanceStep) Approve(comments string) error {
if step.Status != StepStatusPending { if step.GetStatus() != StepStatusPending {
return fmt.Errorf("当前步骤不是待审批状态") return fmt.Errorf("当前步骤不是待审批状态")
} }
step.Status = StepStatusApproved step.AddApprovedEvent()
step.ApproverComments = comments step.ApproverComments = comments
step.addApprovalRecord(StepStatusApproved, comments) step.addApprovalRecord(StepStatusApproved, comments)
return nil return nil
@ -80,10 +142,10 @@ func (step *InstanceStep) Approve(comments string) error {
// Reject 审批驳回 // Reject 审批驳回
func (step *InstanceStep) Reject(comments string) error { func (step *InstanceStep) Reject(comments string) error {
if step.Status != StepStatusPending { //if step.GetStatus() != StepStatusPending {
return fmt.Errorf("当前步骤不是待审批状态") // return fmt.Errorf("当前步骤不是待审批状态")
} //}
step.Status = StepStatusRejected step.AddRejectedEvent()
step.ApproverComments = comments step.ApproverComments = comments
step.addApprovalRecord(StepStatusRejected, comments) step.addApprovalRecord(StepStatusRejected, comments)
return nil return nil
@ -99,3 +161,9 @@ func (step *InstanceStep) addApprovalRecord(status string, comments string) {
} }
step.Records = append(step.Records, record) step.Records = append(step.Records, record)
} }
func (step *InstanceStep) addApprovalReversal(reversal *ApprovalReversal) {
var reversals []*ApprovalReversal
reversals = append(reversals, reversal)
step.Reversals = reversals
}

View File

@ -0,0 +1,24 @@
package model
import (
"approveflow/app/provider/database_connect"
tests "approveflow/test"
"testing"
)
func TestModel(t *testing.T) {
container := tests.InitBaseContainer()
databaseConnectService := container.MustMake(database_connect.DatabaseConnectKey).(database_connect.Service)
db := databaseConnectService.DefaultDatabaseConnect()
db.AutoMigrate(ApprovalInstance{},
InstanceStatusEvent{},
ApprovalRecord{},
ApprovalReversal{},
BatchApprovalTask{},
CurrentStep{},
InstancePathConfig{},
InstanceStep{},
InstanceStepStatusEvent{})
}

View File

@ -29,6 +29,19 @@ func (f *FlowInstanceService) CreateInstance(ctx *gin.Context, approvalFlow *flo
func (f *FlowInstanceService) StartInstance(ctx context.Context, instanceID int64) (*model.ApprovalInstance, error) { func (f *FlowInstanceService) StartInstance(ctx context.Context, instanceID int64) (*model.ApprovalInstance, error) {
return f.handlerInstance(ctx, instanceID, func(ctx context.Context, instance *model.ApprovalInstance) error { return f.handlerInstance(ctx, instanceID, func(ctx context.Context, instance *model.ApprovalInstance) error {
err := instance.Start(ctx) err := instance.Start(ctx)
flowService := f.container.MustMake(flow_definition.FlowDefinitionKey).(flow_definition.Service)
flow, err := flowService.GetFlow(ctx, instance.FlowID)
err = instance.ExecuteApprovalStep(flow)
err = instance.CheckIfMoveToNextStep(instance.GetCurrentSteps())
return err
})
}
func (f *FlowInstanceService) StartInstanceRule(ctx context.Context, instanceID int64) (*model.ApprovalInstance, error) {
return f.handlerInstance(ctx, instanceID, func(ctx context.Context, instance *model.ApprovalInstance) error {
flowService := f.container.MustMake(flow_definition.FlowDefinitionKey).(flow_definition.Service)
flow, err := flowService.GetFlow(ctx, instance.FlowID)
err = instance.ExecuteApprovalStep(flow)
return err return err
}) })
} }
@ -42,13 +55,21 @@ func (f *FlowInstanceService) GetInstancePage(ctx context.Context, pageNum, page
} }
func (f *FlowInstanceService) CancelInstance(ctx context.Context, instanceID int64) error { func (f *FlowInstanceService) CancelInstance(ctx context.Context, instanceID int64) error {
//TODO implement me _, err := f.handlerInstance(ctx, instanceID, func(ctx context.Context, instance *model.ApprovalInstance) error {
panic("implement me") if instance.GetStatus() == model.StatusInProgress || instance.GetStatus() == model.StatusCreated {
instance.AddCancelledEvent()
return nil
}
return errors.New("instance is cancelled or Completed")
})
return err
} }
func (f *FlowInstanceService) ApproveStep(ctx context.Context, instanceID int64, stepID int64, comments string) error { func (f *FlowInstanceService) ApproveStep(ctx context.Context, instanceID int64, stepID int64, comments string) error {
return f.handlerStep(ctx, instanceID, stepID, func(ctx context.Context, instance *model.ApprovalInstance, step *model.InstanceStep) error { return f.handlerStep(ctx, instanceID, stepID, func(ctx context.Context, instance *model.ApprovalInstance, step *model.InstanceStep) error {
err := step.Approve(comments) err := instance.CheckIfMoveToNextStep(instance.GetCurrentSteps())
err = step.Approve(comments)
err = instance.CheckIfMoveToNextStep(instance.GetCurrentSteps())
if err != nil { if err != nil {
return err return err
} }
@ -58,7 +79,8 @@ func (f *FlowInstanceService) ApproveStep(ctx context.Context, instanceID int64,
func (f *FlowInstanceService) RejectStep(ctx context.Context, instanceID int64, stepID int64, comments string) error { func (f *FlowInstanceService) RejectStep(ctx context.Context, instanceID int64, stepID int64, comments string) error {
return f.handlerStep(ctx, instanceID, stepID, func(ctx context.Context, instance *model.ApprovalInstance, step *model.InstanceStep) error { return f.handlerStep(ctx, instanceID, stepID, func(ctx context.Context, instance *model.ApprovalInstance, step *model.InstanceStep) error {
err := step.Reject(comments) err := instance.CheckIfMoveToNextStep(instance.GetCurrentSteps())
err = instance.Reversal(step, instance.GetStartStep(), comments, model.FixActionReApproveAndUpdateData)
if err != nil { if err != nil {
return err return err
} }
@ -150,6 +172,11 @@ func (f *FlowInstanceService) handlerInstance(ctx context.Context, instanceID in
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = f.GetFlowInstanceRepository().SaveInstance(ctx, instance)
if err != nil {
return nil, err
}
return instance, nil return instance, nil
} }

View File

@ -6,6 +6,26 @@ type UserService struct {
container framework.Container container framework.Container
} }
func (s *UserService) GetDirectSupervisorByUserId(userId int64) (*User, error) {
return &User{
ID: 1,
UserName: "dandan",
Name: "卢麟哲",
EmployeeNo: "230615020",
}, nil
}
func (s *UserService) GetDirectSupervisorByUserKey(userKey string) (*User, error) {
user := &User{
ID: 1,
UserName: "dandan",
Name: "卢麟哲",
EmployeeNo: "230615020",
}
user.Key = "230615020"
return user, nil
}
func NewUserService(params ...interface{}) (interface{}, error) { func NewUserService(params ...interface{}) (interface{}, error) {
container := params[0].(framework.Container) container := params[0].(framework.Container)
return &UserService{container: container}, nil return &UserService{container: container}, nil