备份代码

This commit is contained in:
lulz1 2024-11-15 16:53:35 +08:00
parent 0a3da4a609
commit 903cf7f516
24 changed files with 2102 additions and 486 deletions

View File

@ -18,13 +18,13 @@ type InstancesCreateParam struct {
// InstancesCreate handler // InstancesCreate handler
// @Summary 创建流程实例 // @Summary 创建流程实例
// @Description 通过流程模板创建流程实例 // @Description 通过流程模板创建流程实例
// @ID Instances-Create // @ID instances-create
// @Tags Instances-Create // @Tags flow-instances
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param InstancesCreateParam body InstancesCreateParam true "创建参数" // @Param InstancesCreateParam body InstancesCreateParam true "创建参数"
// @Success 200 {object} result.Result "返回成功的流程定义数据" // @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} result.Result "返回失败的流程定义数据" // @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/create [post] // @Router /instances/create [post]
func (api *FlowInstanceApi) InstancesCreate(c *gin.Context) { func (api *FlowInstanceApi) InstancesCreate(c *gin.Context) {
param := utils.QuickBind[InstancesCreateParam](c) param := utils.QuickBind[InstancesCreateParam](c)

View File

@ -15,12 +15,12 @@ type InstancesQueryParam struct {
// @Summary 实例查询 // @Summary 实例查询
// @Description 通过实例的ID查询流程实例详情 // @Description 通过实例的ID查询流程实例详情
// @ID instances-query // @ID instances-query
// @Tags instances-query // @Tags flow-instances
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param InstancesQueryParam body InstancesQueryParam true "查询参数" // @Param InstancesQueryParam body InstancesQueryParam true "查询参数"
// @Success 200 {object} result.Result{data = ApprovalInstanceDTO} "返回成功的流程定义数据" // @Success 200 {object} base.Result{data=ApprovalInstanceDTO} "返回成功的流程定义数据"
// @Failure 500 {object} result.Result "返回失败的流程定义数据" // @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/query [post] // @Router /instances/query [post]
func (api *FlowInstanceApi) InstancesQuery(c *gin.Context) { func (api *FlowInstanceApi) InstancesQuery(c *gin.Context) {
param := utils.QuickBind[InstancesQueryParam](c) param := utils.QuickBind[InstancesQueryParam](c)

View File

@ -15,13 +15,13 @@ type InstancesQueryListParam struct {
// InstancesQueryList handler // InstancesQueryList handler
// @Summary 输入你的接口总结 // @Summary 输入你的接口总结
// @Description 输入你的接口总结详情 // @Description 输入你的接口总结详情
// @ID 接口Id // @ID instances-query-list
// @Tags 接口tag // @Tags flow-instances
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param InstancesQueryListParam body InstancesQueryListParam true "输入参数描述" // @Param InstancesQueryListParam body InstancesQueryListParam true "输入参数描述"
// @Success 200 {object} result.Result "返回成功的流程定义数据" // @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} result.Result "返回失败的流程定义数据" // @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/query/list [post] // @Router /instances/query/list [post]
func (api *FlowInstanceApi) InstancesQueryList(c *gin.Context) { func (api *FlowInstanceApi) InstancesQueryList(c *gin.Context) {
param := utils.QuickBind[InstancesQueryListParam](c) param := utils.QuickBind[InstancesQueryListParam](c)

View File

@ -14,13 +14,13 @@ type InstancesStartParam struct {
// InstancesStart handler // InstancesStart handler
// @Summary 启动审批流 // @Summary 启动审批流
// @Description 启动审批流 // @Description 启动审批流
// @ID Instances-Start // @ID instances-start
// @Tags Instances-Start // @Tags flow-instances
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param InstancesStartParam body InstancesStartParam true "启动实例ID" // @Param InstancesStartParam body InstancesStartParam true "启动实例ID"
// @Success 200 {object} result.Result "返回成功的流程定义数据" // @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} result.Result "返回失败的流程定义数据" // @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/start [post] // @Router /instances/start [post]
func (api *FlowInstanceApi) InstancesStart(c *gin.Context) { func (api *FlowInstanceApi) InstancesStart(c *gin.Context) {
param := utils.QuickBind[InstancesStartParam](c) param := utils.QuickBind[InstancesStartParam](c)

View File

@ -10,13 +10,13 @@ type InstancesStepsAddParam struct {
// InstancesStepsAdd handler // InstancesStepsAdd handler
// @Summary 输入你的接口总结 // @Summary 输入你的接口总结
// @Description 输入你的接口总结详情 // @Description 输入你的接口总结详情
// @ID 接口Id // @ID instances-steps-add
// @Tags 接口tag // @Tags flow-instances
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param InstancesStepsAddParam body InstancesStepsAddParam true "输入参数描述" // @Param InstancesStepsAddParam body InstancesStepsAddParam true "输入参数描述"
// @Success 200 {object} result.Result "返回成功的流程定义数据" // @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} result.Result "返回失败的流程定义数据" // @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/steps/add [post] // @Router /instances/steps/add [post]
func (api *FlowInstanceApi) InstancesStepsAdd(c *gin.Context) { func (api *FlowInstanceApi) InstancesStepsAdd(c *gin.Context) {
// TODO: Implement InstancesStepsAdd // TODO: Implement InstancesStepsAdd

View File

@ -9,13 +9,13 @@ type InstancesStepsRevertParam struct{}
// InstancesStepsRevert handler // InstancesStepsRevert handler
// @Summary 输入你的接口总结 // @Summary 输入你的接口总结
// @Description 输入你的接口总结详情 // @Description 输入你的接口总结详情
// @ID 接口Id // @ID instances-steps-revert
// @Tags 接口tag // @Tags flow-instances
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param InstancesStepsRevertParam body InstancesStepsRevertParam true "输入参数描述" // @Param InstancesStepsRevertParam body InstancesStepsRevertParam true "输入参数描述"
// @Success 200 {object} result.Result "返回成功的流程定义数据" // @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} result.Result "返回失败的流程定义数据" // @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/steps/revert [post] // @Router /instances/steps/revert [post]
func (api *FlowInstanceApi) InstancesStepsRevert(c *gin.Context) { func (api *FlowInstanceApi) InstancesStepsRevert(c *gin.Context) {
// TODO: Implement InstancesStepsRevert // TODO: Implement InstancesStepsRevert

View File

@ -9,13 +9,13 @@ type InstancesStepsUpdateParam struct{}
// InstancesStepsUpdate handler // InstancesStepsUpdate handler
// @Summary 输入你的接口总结 // @Summary 输入你的接口总结
// @Description 输入你的接口总结详情 // @Description 输入你的接口总结详情
// @ID 接口Id // @ID instances-steps-update
// @Tags 接口tag // @Tags flow-instances
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param InstancesStepsUpdateParam body InstancesStepsUpdateParam true "输入参数描述" // @Param InstancesStepsUpdateParam body InstancesStepsUpdateParam true "输入参数描述"
// @Success 200 {object} result.Result "返回成功的流程定义数据" // @Success 200 {object} base.Result "返回成功的流程定义数据"
// @Failure 500 {object} result.Result "返回失败的流程定义数据" // @Failure 500 {object} base.Result "返回失败的流程定义数据"
// @Router /instances/steps/update [post] // @Router /instances/steps/update [post]
func (api *FlowInstanceApi) InstancesStepsUpdate(c *gin.Context) { func (api *FlowInstanceApi) InstancesStepsUpdate(c *gin.Context) {
// TODO: Implement InstancesStepsUpdate // TODO: Implement InstancesStepsUpdate

View File

@ -231,6 +231,305 @@ const docTemplate = `{
} }
} }
} }
},
"/instances/create": {
"post": {
"description": "通过流程模板创建流程实例",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "创建流程实例",
"operationId": "instances-create",
"parameters": [
{
"description": "创建参数",
"name": "InstancesCreateParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesCreateParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/query": {
"post": {
"description": "通过实例的ID查询流程实例详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "实例查询",
"operationId": "instances-query",
"parameters": [
{
"description": "查询参数",
"name": "InstancesQueryParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesQueryParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"allOf": [
{
"$ref": "#/definitions/base.Result"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/flow_instance.ApprovalInstanceDTO"
}
}
}
]
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/query/list": {
"post": {
"description": "输入你的接口总结详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "输入你的接口总结",
"operationId": "instances-query-list",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesQueryListParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesQueryListParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/start": {
"post": {
"description": "启动审批流",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "启动审批流",
"operationId": "instances-start",
"parameters": [
{
"description": "启动实例ID",
"name": "InstancesStartParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStartParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/add": {
"post": {
"description": "输入你的接口总结详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "输入你的接口总结",
"operationId": "instances-steps-add",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesStepsAddParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsAddParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/revert": {
"post": {
"description": "输入你的接口总结详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "输入你的接口总结",
"operationId": "instances-steps-revert",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesStepsRevertParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsRevertParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/update": {
"post": {
"description": "输入你的接口总结详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "输入你的接口总结",
"operationId": "instances-steps-update",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesStepsUpdateParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsUpdateParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
} }
}, },
"definitions": { "definitions": {
@ -340,6 +639,214 @@ const docTemplate = `{
"type": "string" "type": "string"
} }
} }
},
"flow_instance.ApprovalInstanceDTO": {
"type": "object",
"properties": {
"approver_id": {
"description": "申请人ID",
"type": "string"
},
"creator_id": {
"description": "创建者ID",
"type": "string"
},
"current_step_ids": {
"description": "当前步骤ID",
"type": "array",
"items": {
"$ref": "#/definitions/flow_instance.CurrentStepDTO"
}
},
"dynamic_path_configs": {
"description": "动态路径配置,自定义,不直接存储",
"type": "array",
"items": {
"$ref": "#/definitions/flow_instance.DynamicPathConfigDTO"
}
},
"flow_id": {
"description": "流程ID",
"type": "integer"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"status": {
"description": "审批状态",
"type": "string"
},
"steps": {
"description": "实例步骤",
"type": "array",
"items": {
"$ref": "#/definitions/flow_instance.InstanceStepDTO"
}
}
}
},
"flow_instance.ApprovalRecordDTO": {
"type": "object",
"properties": {
"approver_id": {
"description": "审批人ID",
"type": "string"
},
"comments": {
"description": "审批意见",
"type": "string"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"instance_step_id": {
"description": "关联的步骤实例ID",
"type": "integer"
},
"is_timeout": {
"description": "是否超时",
"type": "boolean"
},
"status": {
"description": "审批状态",
"type": "string"
}
}
},
"flow_instance.CurrentStepDTO": {
"type": "object",
"properties": {
"current_step_id": {
"type": "integer"
},
"instance_id": {
"type": "integer"
}
}
},
"flow_instance.DynamicPathConfigDTO": {
"type": "object",
"properties": {
"from_step_id": {
"description": "来源步骤ID",
"type": "integer"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"instance_id": {
"description": "关联的审批实例ID",
"type": "integer"
},
"is_parallel": {
"description": "是否并行",
"type": "boolean"
},
"priority": {
"description": "路径优先级",
"type": "integer"
},
"to_step_id": {
"description": "目标步骤ID",
"type": "integer"
}
}
},
"flow_instance.InstanceStepDTO": {
"type": "object",
"properties": {
"approver_comments": {
"description": "审批意见",
"type": "string"
},
"approver_id": {
"description": "审批人ID",
"type": "string"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"instance_id": {
"description": "所属审批实例ID",
"type": "integer"
},
"is_dynamic": {
"description": "是否为动态步骤",
"type": "boolean"
},
"records": {
"description": "审批记录",
"type": "array",
"items": {
"$ref": "#/definitions/flow_instance.ApprovalRecordDTO"
}
},
"status": {
"description": "审批状态",
"type": "string"
},
"step_id": {
"description": "关联的流程步骤ID",
"type": "integer"
}
}
},
"flow_instance.InstancesCreateParam": {
"type": "object",
"properties": {
"applicant_key": {
"description": "申请人ID",
"type": "string"
},
"creator_key": {
"type": "string"
},
"flow_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesQueryListParam": {
"type": "object",
"properties": {
"page_number": {
"description": "当前页码,默认是第一页",
"type": "integer"
},
"page_size": {
"description": "每页记录数默认是10条",
"type": "integer"
}
}
},
"flow_instance.InstancesQueryParam": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStartParam": {
"type": "object",
"properties": {
"instance_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsAddParam": {
"type": "object"
},
"flow_instance.InstancesStepsRevertParam": {
"type": "object"
},
"flow_instance.InstancesStepsUpdateParam": {
"type": "object"
} }
}, },
"securityDefinitions": { "securityDefinitions": {

View File

@ -224,6 +224,305 @@
} }
} }
} }
},
"/instances/create": {
"post": {
"description": "通过流程模板创建流程实例",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "创建流程实例",
"operationId": "instances-create",
"parameters": [
{
"description": "创建参数",
"name": "InstancesCreateParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesCreateParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/query": {
"post": {
"description": "通过实例的ID查询流程实例详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "实例查询",
"operationId": "instances-query",
"parameters": [
{
"description": "查询参数",
"name": "InstancesQueryParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesQueryParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"allOf": [
{
"$ref": "#/definitions/base.Result"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/flow_instance.ApprovalInstanceDTO"
}
}
}
]
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/query/list": {
"post": {
"description": "输入你的接口总结详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "输入你的接口总结",
"operationId": "instances-query-list",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesQueryListParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesQueryListParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/start": {
"post": {
"description": "启动审批流",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "启动审批流",
"operationId": "instances-start",
"parameters": [
{
"description": "启动实例ID",
"name": "InstancesStartParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStartParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/add": {
"post": {
"description": "输入你的接口总结详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "输入你的接口总结",
"operationId": "instances-steps-add",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesStepsAddParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsAddParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/revert": {
"post": {
"description": "输入你的接口总结详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "输入你的接口总结",
"operationId": "instances-steps-revert",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesStepsRevertParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsRevertParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
},
"/instances/steps/update": {
"post": {
"description": "输入你的接口总结详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"flow-instances"
],
"summary": "输入你的接口总结",
"operationId": "instances-steps-update",
"parameters": [
{
"description": "输入参数描述",
"name": "InstancesStepsUpdateParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/flow_instance.InstancesStepsUpdateParam"
}
}
],
"responses": {
"200": {
"description": "返回成功的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
},
"500": {
"description": "返回失败的流程定义数据",
"schema": {
"$ref": "#/definitions/base.Result"
}
}
}
}
} }
}, },
"definitions": { "definitions": {
@ -333,6 +632,214 @@
"type": "string" "type": "string"
} }
} }
},
"flow_instance.ApprovalInstanceDTO": {
"type": "object",
"properties": {
"approver_id": {
"description": "申请人ID",
"type": "string"
},
"creator_id": {
"description": "创建者ID",
"type": "string"
},
"current_step_ids": {
"description": "当前步骤ID",
"type": "array",
"items": {
"$ref": "#/definitions/flow_instance.CurrentStepDTO"
}
},
"dynamic_path_configs": {
"description": "动态路径配置,自定义,不直接存储",
"type": "array",
"items": {
"$ref": "#/definitions/flow_instance.DynamicPathConfigDTO"
}
},
"flow_id": {
"description": "流程ID",
"type": "integer"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"status": {
"description": "审批状态",
"type": "string"
},
"steps": {
"description": "实例步骤",
"type": "array",
"items": {
"$ref": "#/definitions/flow_instance.InstanceStepDTO"
}
}
}
},
"flow_instance.ApprovalRecordDTO": {
"type": "object",
"properties": {
"approver_id": {
"description": "审批人ID",
"type": "string"
},
"comments": {
"description": "审批意见",
"type": "string"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"instance_step_id": {
"description": "关联的步骤实例ID",
"type": "integer"
},
"is_timeout": {
"description": "是否超时",
"type": "boolean"
},
"status": {
"description": "审批状态",
"type": "string"
}
}
},
"flow_instance.CurrentStepDTO": {
"type": "object",
"properties": {
"current_step_id": {
"type": "integer"
},
"instance_id": {
"type": "integer"
}
}
},
"flow_instance.DynamicPathConfigDTO": {
"type": "object",
"properties": {
"from_step_id": {
"description": "来源步骤ID",
"type": "integer"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"instance_id": {
"description": "关联的审批实例ID",
"type": "integer"
},
"is_parallel": {
"description": "是否并行",
"type": "boolean"
},
"priority": {
"description": "路径优先级",
"type": "integer"
},
"to_step_id": {
"description": "目标步骤ID",
"type": "integer"
}
}
},
"flow_instance.InstanceStepDTO": {
"type": "object",
"properties": {
"approver_comments": {
"description": "审批意见",
"type": "string"
},
"approver_id": {
"description": "审批人ID",
"type": "string"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"instance_id": {
"description": "所属审批实例ID",
"type": "integer"
},
"is_dynamic": {
"description": "是否为动态步骤",
"type": "boolean"
},
"records": {
"description": "审批记录",
"type": "array",
"items": {
"$ref": "#/definitions/flow_instance.ApprovalRecordDTO"
}
},
"status": {
"description": "审批状态",
"type": "string"
},
"step_id": {
"description": "关联的流程步骤ID",
"type": "integer"
}
}
},
"flow_instance.InstancesCreateParam": {
"type": "object",
"properties": {
"applicant_key": {
"description": "申请人ID",
"type": "string"
},
"creator_key": {
"type": "string"
},
"flow_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesQueryListParam": {
"type": "object",
"properties": {
"page_number": {
"description": "当前页码,默认是第一页",
"type": "integer"
},
"page_size": {
"description": "每页记录数默认是10条",
"type": "integer"
}
}
},
"flow_instance.InstancesQueryParam": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStartParam": {
"type": "object",
"properties": {
"instance_id": {
"type": "integer"
}
}
},
"flow_instance.InstancesStepsAddParam": {
"type": "object"
},
"flow_instance.InstancesStepsRevertParam": {
"type": "object"
},
"flow_instance.InstancesStepsUpdateParam": {
"type": "object"
} }
}, },
"securityDefinitions": { "securityDefinitions": {

View File

@ -73,6 +73,152 @@ definitions:
description: 流程名称 description: 流程名称
type: string type: string
type: object type: object
flow_instance.ApprovalInstanceDTO:
properties:
approver_id:
description: 申请人ID
type: string
creator_id:
description: 创建者ID
type: string
current_step_ids:
description: 当前步骤ID
items:
$ref: '#/definitions/flow_instance.CurrentStepDTO'
type: array
dynamic_path_configs:
description: 动态路径配置,自定义,不直接存储
items:
$ref: '#/definitions/flow_instance.DynamicPathConfigDTO'
type: array
flow_id:
description: 流程ID
type: integer
id:
description: 主键ID
type: integer
status:
description: 审批状态
type: string
steps:
description: 实例步骤
items:
$ref: '#/definitions/flow_instance.InstanceStepDTO'
type: array
type: object
flow_instance.ApprovalRecordDTO:
properties:
approver_id:
description: 审批人ID
type: string
comments:
description: 审批意见
type: string
id:
description: 主键ID
type: integer
instance_step_id:
description: 关联的步骤实例ID
type: integer
is_timeout:
description: 是否超时
type: boolean
status:
description: 审批状态
type: string
type: object
flow_instance.CurrentStepDTO:
properties:
current_step_id:
type: integer
instance_id:
type: integer
type: object
flow_instance.DynamicPathConfigDTO:
properties:
from_step_id:
description: 来源步骤ID
type: integer
id:
description: 主键ID
type: integer
instance_id:
description: 关联的审批实例ID
type: integer
is_parallel:
description: 是否并行
type: boolean
priority:
description: 路径优先级
type: integer
to_step_id:
description: 目标步骤ID
type: integer
type: object
flow_instance.InstanceStepDTO:
properties:
approver_comments:
description: 审批意见
type: string
approver_id:
description: 审批人ID
type: string
id:
description: 主键ID
type: integer
instance_id:
description: 所属审批实例ID
type: integer
is_dynamic:
description: 是否为动态步骤
type: boolean
records:
description: 审批记录
items:
$ref: '#/definitions/flow_instance.ApprovalRecordDTO'
type: array
status:
description: 审批状态
type: string
step_id:
description: 关联的流程步骤ID
type: integer
type: object
flow_instance.InstancesCreateParam:
properties:
applicant_key:
description: 申请人ID
type: string
creator_key:
type: string
flow_id:
type: integer
type: object
flow_instance.InstancesQueryListParam:
properties:
page_number:
description: 当前页码,默认是第一页
type: integer
page_size:
description: 每页记录数默认是10条
type: integer
type: object
flow_instance.InstancesQueryParam:
properties:
id:
type: integer
type: object
flow_instance.InstancesStartParam:
properties:
instance_id:
type: integer
type: object
flow_instance.InstancesStepsAddParam:
type: object
flow_instance.InstancesStepsRevertParam:
type: object
flow_instance.InstancesStepsUpdateParam:
type: object
info: info:
contact: contact:
email: yejianfeng email: yejianfeng
@ -214,6 +360,200 @@ paths:
summary: 添加步骤到流程 summary: 添加步骤到流程
tags: tags:
- flow-definition - flow-definition
/instances/create:
post:
consumes:
- application/json
description: 通过流程模板创建流程实例
operationId: instances-create
parameters:
- description: 创建参数
in: body
name: InstancesCreateParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesCreateParam'
produces:
- application/json
responses:
"200":
description: 返回成功的流程定义数据
schema:
$ref: '#/definitions/base.Result'
"500":
description: 返回失败的流程定义数据
schema:
$ref: '#/definitions/base.Result'
summary: 创建流程实例
tags:
- flow-instances
/instances/query:
post:
consumes:
- application/json
description: 通过实例的ID查询流程实例详情
operationId: instances-query
parameters:
- description: 查询参数
in: body
name: InstancesQueryParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesQueryParam'
produces:
- application/json
responses:
"200":
description: 返回成功的流程定义数据
schema:
allOf:
- $ref: '#/definitions/base.Result'
- properties:
data:
$ref: '#/definitions/flow_instance.ApprovalInstanceDTO'
type: object
"500":
description: 返回失败的流程定义数据
schema:
$ref: '#/definitions/base.Result'
summary: 实例查询
tags:
- flow-instances
/instances/query/list:
post:
consumes:
- application/json
description: 输入你的接口总结详情
operationId: instances-query-list
parameters:
- description: 输入参数描述
in: body
name: InstancesQueryListParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesQueryListParam'
produces:
- application/json
responses:
"200":
description: 返回成功的流程定义数据
schema:
$ref: '#/definitions/base.Result'
"500":
description: 返回失败的流程定义数据
schema:
$ref: '#/definitions/base.Result'
summary: 输入你的接口总结
tags:
- flow-instances
/instances/start:
post:
consumes:
- application/json
description: 启动审批流
operationId: instances-start
parameters:
- description: 启动实例ID
in: body
name: InstancesStartParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesStartParam'
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:
post:
consumes:
- application/json
description: 输入你的接口总结详情
operationId: instances-steps-add
parameters:
- description: 输入参数描述
in: body
name: InstancesStepsAddParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesStepsAddParam'
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:
post:
consumes:
- application/json
description: 输入你的接口总结详情
operationId: instances-steps-revert
parameters:
- description: 输入参数描述
in: body
name: InstancesStepsRevertParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesStepsRevertParam'
produces:
- application/json
responses:
"200":
description: 返回成功的流程定义数据
schema:
$ref: '#/definitions/base.Result'
"500":
description: 返回失败的流程定义数据
schema:
$ref: '#/definitions/base.Result'
summary: 输入你的接口总结
tags:
- flow-instances
/instances/steps/update:
post:
consumes:
- application/json
description: 输入你的接口总结详情
operationId: instances-steps-update
parameters:
- description: 输入参数描述
in: body
name: InstancesStepsUpdateParam
required: true
schema:
$ref: '#/definitions/flow_instance.InstancesStepsUpdateParam'
produces:
- application/json
responses:
"200":
description: 返回成功的流程定义数据
schema:
$ref: '#/definitions/base.Result'
"500":
description: 返回失败的流程定义数据
schema:
$ref: '#/definitions/base.Result'
summary: 输入你的接口总结
tags:
- flow-instances
securityDefinitions: securityDefinitions:
ApiKeyAuth: ApiKeyAuth:
in: header in: header

View File

@ -0,0 +1,302 @@
package connect
import (
"approveflow/app/base"
"encoding/json"
"fmt"
)
type NodeConnectTest struct {
Nodes []*Node
NodeMap map[string]*Node // 用于快速查找节点
}
type NodeManager interface {
GetNodes() []AbstractNode
SetNodes([]AbstractNode)
GetNodeMap() map[string]AbstractNode
NewNodePathConfig(fromNodeKey, toNodeKey string) AbstractNodePathConfig
}
type NodeManagerS struct {
NodeManager
}
type AbstractNode interface {
GetPathConfigs() []AbstractNodePathConfig
SetPathConfigs([]AbstractNodePathConfig)
GetKey() string
}
type AbstractNodePathConfig interface {
GetKey() string
GetNodeID() int64
GetFromNodeKey() string
GetToNodeKey() string
}
type Node struct {
PathConfigs []AbstractNodePathConfig `gorm:"-" json:"path_configs"`
base.Model
}
func (n *Node) MarshalBinary() ([]byte, error) {
return json.Marshal(n)
}
func (n *Node) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, n)
}
func (n *Node) GetPathConfigs() []AbstractNodePathConfig {
return n.PathConfigs
}
func (n *Node) SetPathConfigs(configs []AbstractNodePathConfig) {
n.PathConfigs = configs
}
func (n *Node) GetKey() string {
return n.Key
}
type NodePathConfig struct {
NodeID int64 `gorm:"type:bigint;index;not null" json:"node_id"`
FromNodeKey string `gorm:"type:varchar(50);index;not null" json:"from_node_key"`
ToNodeKey string ` gorm:"type:varchar(50);index;not null" json:"to_node_key"`
base.Model
}
func (n *NodePathConfig) MarshalBinary() ([]byte, error) {
return json.Marshal(n)
}
func (n *NodePathConfig) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, n)
}
func (n NodePathConfig) GetKey() string {
return n.Key
}
func (n NodePathConfig) GetNodeID() int64 {
return n.NodeID
}
func (n NodePathConfig) GetFromNodeKey() string {
return n.FromNodeKey
}
func (n NodePathConfig) GetToNodeKey() string {
return n.ToNodeKey
}
func (s *NodeManagerS) InitializeNodeMap() {
InitializeNodeMap(s)
}
func InitializeNodeMap(nct NodeManager) {
for _, node := range nct.GetNodes() {
nct.GetNodeMap()[node.GetKey()] = node
}
}
func GetNextNodes(nct NodeManager, node AbstractNode) ([]AbstractNode, error) {
// 获取所有节点和路径配置
nodeMap := nct.GetNodeMap()
pathConfigs := node.GetPathConfigs()
// 存储下一个节点
var nextNodes []AbstractNode
// 遍历当前节点的路径配置
for _, pathConfig := range pathConfigs {
if pathConfig.GetFromNodeKey() == node.GetKey() {
// 根据路径配置的 ToNodeKey 找到目标节点
nextNodeKey := pathConfig.GetToNodeKey()
if nextNode, exists := nodeMap[nextNodeKey]; exists {
nextNodes = append(nextNodes, nextNode)
} else {
return nil, fmt.Errorf("next node with key %s not found", nextNodeKey)
}
}
}
return nextNodes, nil
}
func (s *NodeManagerS) InsertNodeFirst(newNode AbstractNode) *NodeManagerS {
if s.GetNodes() == nil {
s.SetNodes([]AbstractNode{newNode})
}
s.SetNodes(append(s.GetNodes(), newNode))
return s
}
func InsertNodeFirst(nct NodeManager, newNode AbstractNode) {
if nct.GetNodes() == nil {
nct.SetNodes([]AbstractNode{newNode})
}
nct.SetNodes(append(nct.GetNodes(), newNode))
}
func (s *NodeManagerS) InsertNodeBetween(fromNode, toNode, newNode AbstractNode) error {
return InsertNodeBetween(s, fromNode, toNode, newNode)
}
// InsertNodeBetween 在两个节点之间插入新节点
func InsertNodeBetween(nct NodeManager, fromNode, toNode, newNode AbstractNode) error {
if fromNode == nil || toNode == nil || newNode == nil {
return fmt.Errorf("节点不能为空")
}
// 移除从 fromNode 到 toNode 的路径
removePath(nct, fromNode, toNode)
// 添加从 fromNode 到 newNode 的路径
addPath(nct, fromNode, newNode)
// 添加从 newNode 到 toNode 的路径
addPath(nct, newNode, toNode)
// 如果新节点不在节点列表中,添加进去
if _, exists := nct.GetNodeMap()[newNode.GetKey()]; !exists {
nct.SetNodes(append(nct.GetNodes(), newNode))
nct.GetNodeMap()[newNode.GetKey()] = newNode
}
return nil
}
func (s *NodeManagerS) InsertNodeAfter(node, newNode AbstractNode) error {
return InsertNodeAfter(s, node, newNode)
}
// InsertNodeAfter 在指定节点之后插入节点
func InsertNodeAfter(nct NodeManager, node, newNode AbstractNode) error {
if node == nil || newNode == nil {
return fmt.Errorf("节点不能为空")
}
// 获取从当前节点出去的所有节点
outgoingNodes := getOutgoingNodes(nct, node)
// 移除这些路径并重新连接
for _, toNode := range outgoingNodes {
removePath(nct, node, toNode)
addPath(nct, newNode, toNode)
}
// 添加从当前节点到新节点的路径
addPath(nct, node, newNode)
// 如果新节点不在节点列表中,添加进去
if _, exists := nct.GetNodeMap()[newNode.GetKey()]; !exists {
nct.SetNodes(append(nct.GetNodes(), newNode))
nct.GetNodeMap()[newNode.GetKey()] = newNode
}
return nil
}
func (s *NodeManagerS) InsertNodeBefore(node, newNode AbstractNode) error {
return InsertNodeBefore(s, node, newNode)
}
// InsertNodeBefore 在指定节点之前插入节点
func InsertNodeBefore(nct NodeManager, node, newNode AbstractNode) error {
if node == nil || newNode == nil {
return fmt.Errorf("节点不能为空")
}
// 获取指向当前节点的所有节点
incomingNodes := getIncomingNodes(nct, node)
// 移除这些路径并重新连接
for _, fromNode := range incomingNodes {
removePath(nct, fromNode, node)
addPath(nct, fromNode, newNode)
}
// 添加从新节点到当前节点的路径
addPath(nct, newNode, node)
// 如果新节点不在节点列表中,添加进去
if _, exists := nct.GetNodeMap()[newNode.GetKey()]; !exists {
nct.SetNodes(append(nct.GetNodes(), newNode))
nct.GetNodeMap()[newNode.GetKey()] = newNode
}
return nil
}
func (s *NodeManagerS) VerifyConnections() error {
return VerifyConnections(s)
}
// VerifyConnections 验证节点连接
func VerifyConnections(nct NodeManager) error {
for _, node := range nct.GetNodes() {
for _, pc := range node.GetPathConfigs() {
if _, exists := nct.GetNodeMap()[pc.GetFromNodeKey()]; !exists {
return fmt.Errorf("FromNodeKey %s 不存在", pc.GetFromNodeKey())
}
if _, exists := nct.GetNodeMap()[pc.GetToNodeKey()]; !exists {
return fmt.Errorf("ToNodeKey %s 不存在", pc.GetToNodeKey())
}
}
}
return nil
}
func (s *NodeManagerS) PrintConnections() {
PrintConnections(s)
}
// PrintConnections 打印连接情况
func PrintConnections(nct NodeManager) {
fmt.Println("节点连接情况:")
for _, node := range nct.GetNodes() {
for _, pc := range node.GetPathConfigs() {
fmt.Printf("节点 %s -> 节点 %s\n", pc.GetFromNodeKey(), pc.GetToNodeKey())
}
}
}
// addPath 添加路径
func addPath(nct NodeManager, fromNode, toNode AbstractNode) {
pathConfig := nct.NewNodePathConfig(fromNode.GetKey(), toNode.GetKey())
fromNode.SetPathConfigs(append(fromNode.GetPathConfigs(), pathConfig))
}
// removePath 移除路径
func removePath(nct NodeManager, fromNode, toNode AbstractNode) {
var newPathConfigs []AbstractNodePathConfig
for _, pc := range fromNode.GetPathConfigs() {
if !(pc.GetFromNodeKey() == fromNode.GetKey() && pc.GetToNodeKey() == toNode.GetKey()) {
newPathConfigs = append(newPathConfigs, pc)
}
}
fromNode.SetPathConfigs(newPathConfigs)
}
// getOutgoingNodes 获取后继节点
func getOutgoingNodes(nct NodeManager, node AbstractNode) []AbstractNode {
var result []AbstractNode
for _, pc := range node.GetPathConfigs() {
if toNode, exists := nct.GetNodeMap()[pc.GetToNodeKey()]; exists {
result = append(result, toNode)
}
}
return result
}
// getIncomingNodes 获取前驱节点
func getIncomingNodes(nct NodeManager, node AbstractNode) []AbstractNode {
var result []AbstractNode
for _, n := range nct.GetNodes() {
for _, pc := range n.GetPathConfigs() {
if pc.GetToNodeKey() == node.GetKey() {
result = append(result, n)
}
}
}
return result
}

View File

@ -1,182 +0,0 @@
package abstract
import (
"approveflow/app/base"
"fmt"
)
type NodeConnectTest struct {
Nodes []*Node
NodeMap map[string]*Node // 用于快速查找节点
}
type NodeManager interface {
GetNodes() []*Node
SetNodes([]*Node)
GetNodeMap() map[string]*Node
}
type Node struct {
PathConfigs []*NodePathConfig `gorm:"foreignKey:NodeID;constraint:OnDelete:CASCADE" json:"path_configs"`
base.Model
}
type NodePathConfig struct {
NodeID int64 `gorm:"type:bigint;index;not null" json:"node_id"`
FromNodeKey string `gorm:"type:varchar(50);index;not null" json:"from_node_key"`
ToNodeKey string ` gorm:"type:varchar(50);index;not null" json:"to_node_key"`
base.Model
}
func InitializeNodeMap(nct NodeManager) {
for _, node := range nct.GetNodes() {
nct.GetNodeMap()[node.Key] = node
}
}
// InsertNodeBetween 在两个节点之间插入新节点
func InsertNodeBetween(nct NodeManager, fromNode, toNode, newNode *Node) error {
if fromNode == nil || toNode == nil || newNode == nil {
return fmt.Errorf("节点不能为空")
}
// 移除从 fromNode 到 toNode 的路径
removePath(nct, fromNode, toNode)
// 添加从 fromNode 到 newNode 的路径
addPath(nct, fromNode, newNode)
// 添加从 newNode 到 toNode 的路径
addPath(nct, newNode, toNode)
// 如果新节点不在节点列表中,添加进去
if _, exists := nct.GetNodeMap()[newNode.Key]; !exists {
nct.SetNodes(append(nct.GetNodes(), newNode))
nct.GetNodeMap()[newNode.Key] = newNode
}
return nil
}
// InsertNodeAfter 在指定节点之后插入节点
func InsertNodeAfter(nct NodeManager, node, newNode *Node) error {
if node == nil || newNode == nil {
return fmt.Errorf("节点不能为空")
}
// 获取从当前节点出去的所有节点
outgoingNodes := getOutgoingNodes(nct, node)
// 移除这些路径并重新连接
for _, toNode := range outgoingNodes {
removePath(nct, node, toNode)
addPath(nct, newNode, toNode)
}
// 添加从当前节点到新节点的路径
addPath(nct, node, newNode)
// 如果新节点不在节点列表中,添加进去
if _, exists := nct.GetNodeMap()[newNode.Key]; !exists {
nct.SetNodes(append(nct.GetNodes(), newNode))
nct.GetNodeMap()[newNode.Key] = newNode
}
return nil
}
// InsertNodeBefore 在指定节点之前插入节点
func InsertNodeBefore(nct NodeManager, node, newNode *Node) error {
if node == nil || newNode == nil {
return fmt.Errorf("节点不能为空")
}
// 获取指向当前节点的所有节点
incomingNodes := getIncomingNodes(nct, node)
// 移除这些路径并重新连接
for _, fromNode := range incomingNodes {
removePath(nct, fromNode, node)
addPath(nct, fromNode, newNode)
}
// 添加从新节点到当前节点的路径
addPath(nct, newNode, node)
// 如果新节点不在节点列表中,添加进去
if _, exists := nct.GetNodeMap()[newNode.Key]; !exists {
nct.SetNodes(append(nct.GetNodes(), newNode))
nct.GetNodeMap()[newNode.Key] = newNode
}
return nil
}
// VerifyConnections 验证节点连接
func VerifyConnections(nct NodeManager) error {
for _, node := range nct.GetNodes() {
for _, pc := range node.PathConfigs {
if _, exists := nct.GetNodeMap()[pc.FromNodeKey]; !exists {
return fmt.Errorf("FromNodeKey %s 不存在", pc.FromNodeKey)
}
if _, exists := nct.GetNodeMap()[pc.ToNodeKey]; !exists {
return fmt.Errorf("ToNodeKey %s 不存在", pc.ToNodeKey)
}
}
}
return nil
}
// PrintConnections 打印连接情况
func PrintConnections(nct NodeManager) {
fmt.Println("节点连接情况:")
for _, node := range nct.GetNodes() {
for _, pc := range node.PathConfigs {
fmt.Printf("节点 %s -> 节点 %s\n", pc.FromNodeKey, pc.ToNodeKey)
}
}
}
// addPath 添加路径
func addPath(nct NodeManager, fromNode, toNode *Node) {
pathConfig := &NodePathConfig{
FromNodeKey: fromNode.Key,
ToNodeKey: toNode.Key,
}
fromNode.PathConfigs = append(fromNode.PathConfigs, pathConfig)
}
// removePath 移除路径
func removePath(nct NodeManager, fromNode, toNode *Node) {
var newPathConfigs []*NodePathConfig
for _, pc := range fromNode.PathConfigs {
if !(pc.FromNodeKey == fromNode.Key && pc.ToNodeKey == toNode.Key) {
newPathConfigs = append(newPathConfigs, pc)
}
}
fromNode.PathConfigs = newPathConfigs
}
// getOutgoingNodes 获取后继节点
func getOutgoingNodes(nct NodeManager, node *Node) []*Node {
var result []*Node
for _, pc := range node.PathConfigs {
if toNode, exists := nct.GetNodeMap()[pc.ToNodeKey]; exists {
result = append(result, toNode)
}
}
return result
}
// getIncomingNodes 获取前驱节点
func getIncomingNodes(nct NodeManager, node *Node) []*Node {
var result []*Node
for _, n := range nct.GetNodes() {
for _, pc := range n.PathConfigs {
if pc.ToNodeKey == node.Key {
result = append(result, n)
}
}
}
return result
}

View File

@ -2,17 +2,52 @@ package flow_definition
import ( import (
"approveflow/app/base" "approveflow/app/base"
"approveflow/app/provider/abstract/connect"
"approveflow/app/utils"
"encoding/json" "encoding/json"
"fmt" "fmt"
) )
// ApprovalFlow 审批流程表 // ApprovalFlow 审批流程表
type ApprovalFlow struct { type ApprovalFlow struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID
Name string `gorm:"type:varchar(100);not null" json:"name"` // 流程名称 Name string `gorm:"type:varchar(100);not null" json:"name"` // 流程名称
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"` // 流程步骤
base.Model // 通用字段,包括创建时间、更新时间等 NodeMap map[string]connect.AbstractNode `gorm:"-" json:"node_map"` // 流程步骤
base.Model // 通用字段,包括创建时间、更新时间等
}
func (flow *ApprovalFlow) GetNodes() []connect.AbstractNode {
return utils.ConvertToAbstractNodes(flow.Steps, func(step *ApprovalStep) connect.AbstractNode {
return step
})
}
func (flow *ApprovalFlow) SetNodes(nodes []connect.AbstractNode) {
steps := make([]*ApprovalStep, len(nodes))
for i, node := range nodes {
// 类型断言,将 abstract.AbstractNode 转换为 *ApprovalStep
if step, ok := node.(*ApprovalStep); ok {
steps[i] = step
} else {
// 如果转换失败,可以选择处理错误,抛出异常或记录日志
panic("node is not of type *ApprovalStep")
}
}
flow.Steps = steps
}
func (flow *ApprovalFlow) GetNodeMap() map[string]connect.AbstractNode {
if flow.NodeMap == nil {
flow.NodeMap = map[string]connect.AbstractNode{}
connect.InitializeNodeMap(flow)
}
return flow.NodeMap
}
func (flow *ApprovalFlow) NewNodePathConfig(fromNodeKey, toNodeKey string) connect.AbstractNodePathConfig {
return NewPathConfig(fromNodeKey, toNodeKey)
} }
func (flow *ApprovalFlow) MarshalBinary() ([]byte, error) { func (flow *ApprovalFlow) MarshalBinary() ([]byte, error) {
@ -31,10 +66,13 @@ func NewApprovalFlow(name string, description string) *ApprovalFlow {
} }
startStep := NewStartStep() startStep := NewStartStep()
endStep := NewEndStep() endStep := NewEndStep()
path := NewPathBetween(startStep, endStep)
err := startStep.AddPathConfig(path) connect.InsertNodeFirst(approvalFlow, startStep)
err = approvalFlow.AddStep(startStep) err := connect.InsertNodeAfter(approvalFlow, startStep, endStep)
err = approvalFlow.AddStep(endStep) //path := NewPathBetween(startStep, endStep)
//err := startStep.AddPathConfig(path)
//err = approvalFlow.AddStep(startStep)
//err = approvalFlow.AddStep(endStep)
if err != nil { if err != nil {
return nil return nil
} }
@ -115,31 +153,9 @@ func (flow *ApprovalFlow) Validate() error {
} }
// FirstStep 获取审批流程中的第一个审批节点 // FirstStep 获取审批流程中的第一个审批节点
func (flow *ApprovalFlow) FirstStep(data map[string]interface{}) (*ApprovalStep, error) { func (flow *ApprovalFlow) FirstStep() (*ApprovalStep, error) {
// 首先检查是否有步骤
if len(flow.Steps) == 0 {
return nil, fmt.Errorf("no steps found in the approval flow")
}
// 创建一个 map 用于存储所有指向目标节点的 StepKey
toStepKeys := make(map[string]bool)
for _, step := range flow.Steps { for _, step := range flow.Steps {
for _, pathConfig := range step.PathConfigs { if step.StepCode == StepStart {
condition, err := pathConfig.EvaluateCondition(data)
if err != nil {
continue
}
if !condition {
continue
}
// 使用 pathConfig.ToStepKey 替换 ToStepID并且存储为 string 类型
toStepKeys[pathConfig.ToStepKey] = true
}
}
// 遍历步骤,找到第一个没有被指向的步骤
for _, step := range flow.Steps {
// 假设 step.Key 是 string 类型,检查是否存在于 toStepKeys 中
if _, exists := toStepKeys[step.Key]; !exists {
// 如果当前步骤的 Key 不在 toStepKeys 中,则它是第一个审批节点
return step, nil return step, nil
} }
} }

View File

@ -10,15 +10,30 @@ import (
// ApprovalPathConfig 审批路径配置表 // ApprovalPathConfig 审批路径配置表
type ApprovalPathConfig struct { type ApprovalPathConfig struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID
FromStepID int64 `gorm:"index;not null" json:"from_step_id"` // 来源步骤ID NodeID int64 `gorm:"type:bigint;index;not null" json:"node_id"`
FromStepKey string `gorm:"type:varchar(50);index;not null" json:"from_step_key"` // 来源步骤ID FromNodeKey string `gorm:"type:varchar(50);index;not null" json:"from_node_key"`
ToNodeKey string ` gorm:"type:varchar(50);index;not null" json:"to_node_key"`
ToStepKey string `gorm:"type:varchar(50);index;not null" json:"to_step_key"` // 目标步骤ID IsParallel bool `gorm:"type:bool;default:false" json:"is_parallel"` // 是否并行
IsParallel bool `gorm:"type:bool;default:false" json:"is_parallel"` // 是否并行 ConditionExpression string `gorm:"type:text" json:"condition_expression"` // 条件表达式
ConditionExpression string `gorm:"type:text" json:"condition_expression"` // 条件表达式 base.Model
ApprovalConditionCommon `gorm:"-"` ApprovalConditionCommon `gorm:"-"`
base.Model // 通用字段,包括创建时间、更新时间等 }
func (a *ApprovalPathConfig) GetKey() string {
return a.Key
}
func (a *ApprovalPathConfig) GetNodeID() int64 {
return a.NodeID
}
func (a *ApprovalPathConfig) GetFromNodeKey() string {
return a.FromNodeKey
}
func (a *ApprovalPathConfig) GetToNodeKey() string {
return a.ToNodeKey
} }
func (a *ApprovalPathConfig) MarshalBinary() ([]byte, error) { func (a *ApprovalPathConfig) MarshalBinary() ([]byte, error) {
@ -31,23 +46,31 @@ func (a *ApprovalPathConfig) UnmarshalBinary(data []byte) error {
func (a *ApprovalPathConfig) NewPathWithStep(step *ApprovalStep) (*ApprovalPathConfig, *ApprovalPathConfig) { func (a *ApprovalPathConfig) NewPathWithStep(step *ApprovalStep) (*ApprovalPathConfig, *ApprovalPathConfig) {
toPath := &ApprovalPathConfig{ toPath := &ApprovalPathConfig{
FromStepID: step.ID, IsParallel: false,
FromStepKey: step.Key,
ToStepKey: a.ToStepKey,
IsParallel: false,
} }
a.ToStepKey = step.Key a.FromNodeKey = step.Key
a.NodeID = step.ID
a.ToNodeKey = step.Key
return a, toPath return a, toPath
} }
func NewPathBetween(fromStep, toStep *ApprovalStep) *ApprovalPathConfig { func NewPathBetween(fromStep, toStep *ApprovalStep) *ApprovalPathConfig {
return &ApprovalPathConfig{ path := &ApprovalPathConfig{
FromStepID: fromStep.ID,
FromStepKey: fromStep.Key,
//ToStepID: toStep.ID,
ToStepKey: toStep.Key,
IsParallel: false, IsParallel: false,
} }
path.FromNodeKey = fromStep.Key
path.NodeID = fromStep.ID
path.ToNodeKey = toStep.Key
return path
}
func NewPathConfig(fromStepKey, toStepKey string) *ApprovalPathConfig {
path := &ApprovalPathConfig{
IsParallel: false,
}
path.FromNodeKey = fromStepKey
path.ToNodeKey = toStepKey
return path
} }
func NewPathWithTarget(target *ApprovalStep) *ApprovalPathConfig { func NewPathWithTarget(target *ApprovalStep) *ApprovalPathConfig {

View File

@ -2,6 +2,8 @@ package flow_definition
import ( import (
"approveflow/app/base" "approveflow/app/base"
"approveflow/app/provider/abstract/connect"
"approveflow/app/utils"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/google/uuid" "github.com/google/uuid"
@ -10,14 +12,37 @@ import (
// ApprovalStep 审批步骤表 // ApprovalStep 审批步骤表
type ApprovalStep struct { type ApprovalStep struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID
FlowID int64 `gorm:"index;not null" json:"flow_id"` // 所属流程ID FlowID int64 `gorm:"index;not null" json:"flow_id"` // 所属流程ID
Name string `gorm:"type:varchar(100);not null" json:"name"` // 步骤名称 Name string `gorm:"type:varchar(100);not null" json:"name"` // 步骤名称
StepCode string `gorm:"type:varchar(100)" json:"step_code"` // 步骤编号用于标识特殊的审批节点 StepCode string `gorm:"type:varchar(100)" json:"step_code"` // 步骤编号用于标识特殊的审批节点
Rules []*ApprovalRule `gorm:"foreignKey:StepID;constraint:OnDelete:CASCADE" json:"rules"` // Foreign key for Rules Rules []*ApprovalRule `gorm:"foreignKey:StepID;constraint:OnDelete:CASCADE" json:"rules"` // Foreign key for Rules
DynamicConfig *DynamicApprovalStepConfig `gorm:"foreignKey:StepID;constraint:OnDelete:CASCADE" json:"dynamic_config"` // One-to-one with DynamicConfig DynamicConfig *DynamicApprovalStepConfig `gorm:"foreignKey:StepID;constraint:OnDelete:CASCADE" json:"dynamic_config"` // One-to-one with DynamicConfig
PathConfigs []*ApprovalPathConfig `gorm:"foreignKey:FromStepID;constraint:OnDelete:CASCADE" json:"from_path_configs"` // 路径配置集合 PathConfigs []*ApprovalPathConfig `gorm:"foreignKey:NodeID;constraint:OnDelete:CASCADE" json:"path_configs"` // 路径配置集合
base.Model // 通用字段,包括创建时间、更新时间等 base.Model
}
func (step *ApprovalStep) GetPathConfigs() []connect.AbstractNodePathConfig {
abstractConfigs := make([]connect.AbstractNodePathConfig, len(step.PathConfigs))
for i, config := range step.PathConfigs {
abstractConfigs[i] = config // 直接赋值,因为 ApprovalPathConfig 实现了 AbstractNodePathConfig
}
return abstractConfigs
}
func (step *ApprovalStep) SetPathConfigs(configs []connect.AbstractNodePathConfig) {
convertedConfigs, err := utils.ConvertToSpecificType(configs, func(item connect.AbstractNodePathConfig) (*ApprovalPathConfig, bool) {
specificConfig, ok := item.(*ApprovalPathConfig)
return specificConfig, ok
})
if err != nil {
panic(fmt.Sprintf("Error in SetPathConfigs: %v", err))
}
step.PathConfigs = convertedConfigs
}
func (step *ApprovalStep) GetKey() string {
return step.Key
} }
func (step *ApprovalStep) MarshalBinary() ([]byte, error) { func (step *ApprovalStep) MarshalBinary() ([]byte, error) {
@ -105,7 +130,7 @@ func (step *ApprovalStep) AddPathConfig(pathConfig *ApprovalPathConfig) error {
return fmt.Errorf("path config with ID %d already exists in step", pathConfig.ID) return fmt.Errorf("path config with ID %d already exists in step", pathConfig.ID)
} }
} }
pathConfig.FromStepID = step.ID pathConfig.NodeID = step.ID
step.PathConfigs = append(step.PathConfigs, pathConfig) step.PathConfigs = append(step.PathConfigs, pathConfig)
return nil return nil
} }
@ -130,7 +155,7 @@ func (step *ApprovalStep) GetNextStepIDs(data map[string]interface{}) ([]string,
return nil, err return nil, err
} }
if met { if met {
nextStepKeys = append(nextStepKeys, pathConfig.ToStepKey) nextStepKeys = append(nextStepKeys, pathConfig.ToNodeKey)
if !pathConfig.IsParallel { if !pathConfig.IsParallel {
break // 非并行时,只取第一个满足条件的路径 break // 非并行时,只取第一个满足条件的路径
} }
@ -141,7 +166,7 @@ func (step *ApprovalStep) GetNextStepIDs(data map[string]interface{}) ([]string,
func (step *ApprovalStep) GetPathByToKey(toStepKey string) (*ApprovalPathConfig, bool) { func (step *ApprovalStep) GetPathByToKey(toStepKey string) (*ApprovalPathConfig, bool) {
for _, pathConfig := range step.PathConfigs { for _, pathConfig := range step.PathConfigs {
if pathConfig.ToStepKey == toStepKey { if pathConfig.ToNodeKey == toStepKey {
return pathConfig, true return pathConfig, true
} }
} }

View File

@ -19,4 +19,6 @@ func TestModel(t *testing.T) {
base.BaseExtensionField{}, base.BaseExtensionField{},
ApprovalStep{}, ApprovalStep{},
ApprovalRule{}) ApprovalRule{})
db.AutoMigrate(ApprovalStep{})
} }

View File

@ -41,6 +41,7 @@ func (f *FlowDefinitionService) AddStepWithPosition(ctx context.Context, flowID
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) _, err = flow.GetStepByKey(toStepKey)
if err != nil { if err != nil {
return err return err
} }
@ -202,7 +203,7 @@ func (f *FlowDefinitionService) UpdatePathConfig(ctx context.Context, flowID int
if err != nil { if err != nil {
return err return err
} }
pathById.ToStepKey = config.ToStepKey pathById.ToNodeKey = config.ToNodeKey
pathById.IsParallel = config.IsParallel pathById.IsParallel = config.IsParallel
pathById.ConditionExpression = config.ConditionExpression pathById.ConditionExpression = config.ConditionExpression
return nil return nil

View File

@ -2,7 +2,9 @@ package model
import ( import (
"approveflow/app/base" "approveflow/app/base"
"approveflow/app/provider/abstract/connect"
"approveflow/app/provider/flow_definition" "approveflow/app/provider/flow_definition"
"approveflow/app/utils"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -20,20 +22,39 @@ const (
// ApprovalInstance 审批实例表 // ApprovalInstance 审批实例表
type ApprovalInstance struct { type ApprovalInstance struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID
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:"type:varchar(50);not null" 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"` // 实例步骤
DynamicPathConfigs []*DynamicPathConfig `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE" json:"dynamic_path_configs"` // 动态路径配置,自定义,不直接存储 Data string `gorm:"type:json" json:"data"` // 保存审批数据的 JSON
Data string `gorm:"type:json" json:"data"` // 保存审批数据的 JSON
ApprovalFlow *flow_definition.ApprovalFlow `gorm:"-" json:"approval_flow"`
sync.RWMutex sync.RWMutex
base.Model 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 目前审批中的节点 // 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"`
@ -51,27 +72,68 @@ func NewApprovalInstance(
return nil, err return nil, err
} }
instance := &ApprovalInstance{ instance := &ApprovalInstance{
FlowID: approvalFlow.ID, FlowID: approvalFlow.ID,
ApplicantKey: data[base.ApplicantKey].(string), ApplicantKey: data[base.ApplicantKey].(string),
CreatorKey: data[base.CreatorKey].(string), CreatorKey: data[base.CreatorKey].(string),
Steps: []*InstanceStep{}, Steps: []*InstanceStep{},
DynamicPathConfigs: []*DynamicPathConfig{}, Data: string(bytes),
Data: string(bytes),
} }
// Get the first step of the approval flow
// 从审批模板创建 审批节点 firstStep, err := approvalFlow.FirstStep()
for _, step := range approvalFlow.Steps { if err != nil {
approver, err := step.DynamicConfig.GetApprover(ctx, data) return nil, err
if err != nil { }
return nil, err // Build the steps recursively
} processedSteps := make(map[string]*InstanceStep)
instanceStep := NewInstanceStep(step.ID, step.Name, approver.Key, false) err = buildInstanceSteps(ctx, instance, firstStep, nil, data, processedSteps)
instance.Steps = append(instance.Steps, instanceStep) if err != nil {
return nil, err
} }
return instance, nil 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 发送审批通知给第一个审批人的方法 // SendApprovalNotification 发送审批通知给第一个审批人的方法
func (instance *ApprovalInstance) SendApprovalNotification(step *InstanceStep, ctx context.Context) error { func (instance *ApprovalInstance) SendApprovalNotification(step *InstanceStep, ctx context.Context) error {
@ -105,12 +167,7 @@ func (instance *ApprovalInstance) Start(ctx context.Context) error {
} }
// 获取第一个审批步骤 // 获取第一个审批步骤
dataMap, err := instance.DataToMap() firstStep, err := instance.getFirstStep()
if err != nil {
return err
}
firstStep, err := instance.getFirstStep(dataMap)
if err != nil { if err != nil {
return err return err
} }
@ -127,39 +184,13 @@ func (instance *ApprovalInstance) Start(ctx context.Context) error {
} }
// getFirstStep 获取第一个审批步骤 // getFirstStep 获取第一个审批步骤
func (instance *ApprovalInstance) getFirstStep(data map[string]interface{}) (*InstanceStep, error) { func (instance *ApprovalInstance) getFirstStep() (*InstanceStep, error) {
// 从模板路径中获取第一个节点
firstTemplateStep, err := instance.ApprovalFlow.FirstStep(data)
if err != nil {
return nil, fmt.Errorf("error getting first step from template: %v", err)
}
// 找到实例步骤中对应的第一个模板步骤
var firstInstanceStep *InstanceStep
for _, step := range instance.Steps { for _, step := range instance.Steps {
if step.StepID == firstTemplateStep.ID { if step.StepCode == flow_definition.StepStart {
firstInstanceStep = step return step, nil
break
} }
} }
return nil, errors.New("未找到起点")
if firstInstanceStep == nil {
return nil, fmt.Errorf("未找到第一个审批步骤")
}
// 检查动态路径配置,找到优先级更高的路径
for _, dynamicConfig := range instance.DynamicPathConfigs {
if dynamicConfig.ToStepID == firstTemplateStep.ID {
// 在实例的步骤中找到对应的动态路径起点
for _, step := range instance.Steps {
if step.StepID == dynamicConfig.FromStepID {
firstInstanceStep = step
break
}
}
}
}
return firstInstanceStep, nil
} }
// MoveToNextStep 移动到下一个步骤 // MoveToNextStep 移动到下一个步骤
@ -205,41 +236,20 @@ func (instance *ApprovalInstance) MoveToNextStep() error {
// getNextStep 获取下一个步骤 // getNextStep 获取下一个步骤
func (instance *ApprovalInstance) getNextStep(currentStep *InstanceStep, data map[string]interface{}) ([]*InstanceStep, error) { func (instance *ApprovalInstance) getNextStep(currentStep *InstanceStep, data map[string]interface{}) ([]*InstanceStep, error) {
// 先从动态路径查找下一个步骤 pathConfigs := currentStep.InstancePathConfigs
for _, path := range instance.DynamicPathConfigs { var nextSteps []*InstanceStep
if path.FromStepID == currentStep.StepID { for _, pathConfig := range pathConfigs {
stepByID, err := instance.findStepByID(path.ToStepID) met, err := pathConfig.IsConditionMet(data)
if err != nil { if err != nil || !met {
return nil, err continue
}
return []*InstanceStep{stepByID}, nil
} }
} nextStep, err := instance.GetStepByKey(pathConfig.GetToNodeKey())
// 如果动态路径找不到,使用模板路径 , 如果是模板节点,那么必定存在对于的模板路径,如果没有模板路径说明说明审批结束了
flowStep, err := instance.ApprovalFlow.GetStep(currentStep.StepID)
if err != nil {
return nil, err
}
nextStepIDs, err := flowStep.GetNextStepIDs(data)
if len(nextStepIDs) == 0 {
return nil, fmt.Errorf("未找到符合条件的下一个步骤")
}
if len(nextStepIDs) == 1 {
// 如果是只有一个模板节点,那么说明是单审批节点
stepId := nextStepIDs[0]
stepByID, err := instance.findStepByKey(stepId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return []*InstanceStep{stepByID}, nil nextSteps = append(nextSteps, nextStep)
} }
return nextSteps, nil
if len(nextStepIDs) > 1 {
// 说明是并行节点 ,暂不支持
}
return nil, fmt.Errorf("未找到符合条件的下一个步骤")
} }
func (instance *ApprovalInstance) findStepByID(id int64) (*InstanceStep, error) { func (instance *ApprovalInstance) findStepByID(id int64) (*InstanceStep, error) {
@ -353,9 +363,9 @@ func (instance *ApprovalInstance) GetCurrentStepsById(id int64) *InstanceStep {
} }
// executeApprovalStep 执行审批步骤 // executeApprovalStep 执行审批步骤
func (instance *ApprovalInstance) executeApprovalStep() error { func (instance *ApprovalInstance) executeApprovalStep(approvalFlow flow_definition.ApprovalFlow) error {
for _, instanceStep := range instance.GetCurrentSteps() { for _, instanceStep := range instance.GetCurrentSteps() {
flowStep, err := instance.ApprovalFlow.GetStep(instanceStep.StepID) flowStep, err := approvalFlow.GetStep(instanceStep.StepID)
if err != nil { if err != nil {
continue continue
} }
@ -409,24 +419,7 @@ func (instance *ApprovalInstance) executeApprovalStep() error {
return nil // 如果没有规则满足,正常返回 return nil // 如果没有规则满足,正常返回
} }
func (instance *ApprovalInstance) AddStepBetween(step, fromStep, toStep *InstanceStep) *DynamicPathConfig { func (instance *ApprovalInstance) GetPathByFromStepKey(fromStepKey string) (*InstancePathConfig, error) {
// 获取 pathConfig
if fromStep.IsDynamic {
for _, pathConfig := range instance.DynamicPathConfigs {
if pathConfig.FromStepKey == fromStep.Key {
}
}
} else {
steps := instance.ApprovalFlow.Steps
for _, flowStep := range steps {
if flowStep.ID == fromStep.StepID {
flowStep.
}
}
}
}
func (instance *ApprovalInstance) GetPathByFromStepKey(fromStepKey string) (*DynamicPathConfig, error) {
return nil, nil
} }

View File

@ -1,28 +0,0 @@
package model
import "approveflow/app/base"
// DynamicPathConfig 动态路径配置表结构
type DynamicPathConfig struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID
InstanceID int64 `gorm:"type:bigint;index;not null" json:"instance_id"` // 关联的审批实例ID
FromStepID int64 `gorm:"type:bigint;not null" json:"from_step_id"` // 来源步骤ID
FromStepKey string `gorm:"type:varchar(50);not null" json:"from_step_key"` // 来源步骤ID
ToStepKey string `gorm:"type:varchar(50);not null" json:"to_step_key"` // 目标步骤ID
IsParallel bool `gorm:"not null;default:false" json:"is_parallel"` // 是否并行
Priority int `gorm:"not null;default:0" json:"priority"` // 路径优先级
base.Model
}
// NewDynamicPathConfig 创建动态路径
func NewDynamicPathConfig(instanceID, fromStepID int64, FromStepKey, ToStepKey string, isParallel bool) *DynamicPathConfig {
pathConfig := &DynamicPathConfig{
InstanceID: instanceID,
FromStepID: fromStepID,
FromStepKey: FromStepKey,
ToStepKey: ToStepKey,
IsParallel: isParallel,
Priority: 0,
}
return pathConfig
}

View File

@ -1,72 +0,0 @@
package model
import (
"approveflow/app/base"
"fmt"
)
const (
StepStatusPending = "Pending" // 待审批
StepStatusApproved = "Approved" // 已批准
StepStatusRejected = "Rejected" // 已驳回
StepStatusReversed = "Reversed" // 已反转
StepStatusCompleted = "Completed" // 步骤已完成
)
// InstanceStep 审批步骤实例表
type InstanceStep struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID
InstanceID int64 `gorm:"index;not null" json:"instance_id"` // 所属审批实例ID
StepID int64 `gorm:"type:bigint;not null" json:"step_id"` // 关联的流程步骤ID
StepName string `gorm:"type:varchar(50);not null" json:"step_name"`
ApproverKey string `gorm:"type:varchar(50);not null" json:"approver_id"` // 审批人ID
Status string `gorm:"type:varchar(50);not null" json:"status"` // 审批状态
ApproverComments string `gorm:"type:text" json:"approver_comments"` // 审批意见
IsDynamic bool `gorm:"not null;default:false" json:"is_dynamic"` // 是否为动态步骤
Records []*ApprovalRecord `gorm:"foreignKey:InstanceStepID;constraint:OnDelete:CASCADE" json:"records"` // 审批记录
base.Model
}
func NewInstanceStep(stepID int64, stepName, approverKey string, isDynamic bool) *InstanceStep {
instanceStep := &InstanceStep{
StepID: stepID,
StepName: stepName,
ApproverKey: approverKey,
Status: StepStatusPending,
IsDynamic: isDynamic,
}
return instanceStep
}
// Approve 审批通过
func (step *InstanceStep) Approve(comments string) error {
if step.Status != StepStatusPending {
return fmt.Errorf("当前步骤不是待审批状态")
}
step.Status = StepStatusApproved
step.ApproverComments = comments
step.addApprovalRecord(StepStatusApproved, comments)
return nil
}
// Reject 审批驳回
func (step *InstanceStep) Reject(comments string) error {
if step.Status != StepStatusPending {
return fmt.Errorf("当前步骤不是待审批状态")
}
step.Status = StepStatusRejected
step.ApproverComments = comments
step.addApprovalRecord(StepStatusRejected, comments)
return nil
}
// addApprovalRecord 添加审批记录
func (step *InstanceStep) addApprovalRecord(status string, comments string) {
record := &ApprovalRecord{
InstanceStepID: step.ID,
ApproverKey: step.ApproverKey,
Status: status,
Comments: comments,
}
step.Records = append(step.Records, record)
}

View File

@ -0,0 +1,59 @@
package model
import (
"approveflow/app/base"
"approveflow/app/provider/flow_definition"
)
// InstancePathConfig 动态路径配置表结构
type InstancePathConfig struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID
NodeID int64 `gorm:"type:bigint;index;not null" json:"node_id"`
FromNodeKey string `gorm:"type:varchar(50);index;not null" json:"from_node_key"`
ToNodeKey string ` gorm:"type:varchar(50);index;not null" json:"to_node_key"`
IsParallel bool `gorm:"not null;default:false" json:"is_parallel"` // 是否并行
IsDynamic bool `gorm:"not null;default:false" json:"is_dynamic"` // 是否为动态步骤
Priority int `gorm:"not null;default:0" json:"priority"` // 路径优先级
flow_definition.ApprovalConditionCommon
base.Model
}
func (d InstancePathConfig) GetKey() string {
return d.Key
}
func (d InstancePathConfig) GetNodeID() int64 {
return d.NodeID
}
func (d InstancePathConfig) GetFromNodeKey() string {
return d.FromNodeKey
}
func (d InstancePathConfig) GetToNodeKey() string {
return d.ToNodeKey
}
// NewDynamicPathConfig 创建动态路径
func NewDynamicPathConfig(instanceID, nodeId int64, FromStepKey, ToStepKey string, isParallel bool) *InstancePathConfig {
pathConfig := &InstancePathConfig{
NodeID: nodeId,
FromNodeKey: FromStepKey,
ToNodeKey: ToStepKey,
IsParallel: isParallel,
Priority: 0,
}
return pathConfig
}
// IsConditionMet 判断路径条件是否满足
func (a *InstancePathConfig) IsConditionMet(data map[string]interface{}) (bool, error) {
if a.ConditionExpression == "" {
return true, nil // 无条件,默认满足
}
result, err := a.EvaluateCondition(data)
if err != nil {
return false, err
}
return result, nil
}

View File

@ -0,0 +1,101 @@
package model
import (
"approveflow/app/base"
"approveflow/app/provider/abstract/connect"
"approveflow/app/provider/flow_definition"
"approveflow/app/utils"
"fmt"
"github.com/google/uuid"
)
const (
StepStatusPending = "Pending" // 待审批
StepStatusApproved = "Approved" // 已批准
StepStatusRejected = "Rejected" // 已驳回
StepStatusReversed = "Reversed" // 已反转
StepStatusCompleted = "Completed" // 步骤已完成
)
// InstanceStep 审批步骤实例表
type InstanceStep struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键ID
InstanceID int64 `gorm:"index;not null" json:"instance_id"` // 所属审批实例ID
StepID int64 `gorm:"type:bigint;not null" json:"step_id"` // 关联的流程步骤ID
StepName string `gorm:"type:varchar(50);not null" json:"step_name"`
StepCode string `gorm:"type:varchar(100)" json:"step_code"` // 步骤编号用于标识特殊的审批节点
ApproverKey string `gorm:"type:varchar(50);not null" json:"approver_id"` // 审批人ID
Status string `gorm:"type:varchar(50);not null" json:"status"` // 审批状态
ApproverComments string `gorm:"type:text" json:"approver_comments"` // 审批意见
IsDynamic bool `gorm:"not null;default:false" json:"is_dynamic"` // 是否为动态步骤
InstancePathConfigs []*InstancePathConfig `gorm:"foreignKey:NodeID;constraint:OnDelete:CASCADE" json:"instance_path_configs"` // 动态路径配置,自定义,不直接存储
Records []*ApprovalRecord `gorm:"foreignKey:InstanceStepID;constraint:OnDelete:CASCADE" json:"records"` // 审批记录
base.Model
}
func (step *InstanceStep) GetPathConfigs() []connect.AbstractNodePathConfig {
return utils.ConvertToAbstractNodes(step.InstancePathConfigs, func(t *InstancePathConfig) connect.AbstractNodePathConfig {
return t
})
}
func (step *InstanceStep) SetPathConfigs(configs []connect.AbstractNodePathConfig) {
convertedConfigs, err := utils.ConvertToSpecificType(configs, func(item connect.AbstractNodePathConfig) (*InstancePathConfig, bool) {
specificConfig, ok := item.(*InstancePathConfig)
return specificConfig, ok
})
if err != nil {
panic(fmt.Sprintf("Error in SetPathConfigs: %v", err))
}
step.InstancePathConfigs = convertedConfigs
}
func (step *InstanceStep) GetKey() string {
return step.Key
}
func NewInstanceStep(flowStep *flow_definition.ApprovalStep, approverKey string, isDynamic bool) *InstanceStep {
instanceStep := &InstanceStep{
StepID: flowStep.ID,
StepName: flowStep.Name,
StepCode: flowStep.StepCode,
ApproverKey: approverKey,
Status: StepStatusPending,
IsDynamic: isDynamic,
}
instanceStep.Key = uuid.New().String()
return instanceStep
}
// Approve 审批通过
func (step *InstanceStep) Approve(comments string) error {
if step.Status != StepStatusPending {
return fmt.Errorf("当前步骤不是待审批状态")
}
step.Status = StepStatusApproved
step.ApproverComments = comments
step.addApprovalRecord(StepStatusApproved, comments)
return nil
}
// Reject 审批驳回
func (step *InstanceStep) Reject(comments string) error {
if step.Status != StepStatusPending {
return fmt.Errorf("当前步骤不是待审批状态")
}
step.Status = StepStatusRejected
step.ApproverComments = comments
step.addApprovalRecord(StepStatusRejected, comments)
return nil
}
// addApprovalRecord 添加审批记录
func (step *InstanceStep) addApprovalRecord(status string, comments string) {
record := &ApprovalRecord{
InstanceStepID: step.ID,
ApproverKey: step.ApproverKey,
Status: status,
Comments: comments,
}
step.Records = append(step.Records, record)
}

View File

@ -82,9 +82,10 @@ func (f *FlowInstanceService) AddCustomStep(ctx context.Context, instanceID int6
} }
// 如果是 自定义阶段 那么它的路径配置能够实例对象中找到 // 如果是 自定义阶段 那么它的路径配置能够实例对象中找到
if fromStep.IsDynamic { if fromStep.IsDynamic {
instance.DynamicPathConfigs //instance.InstancePathConfigs
} }
return nil
}) })
} }

View File

@ -3,10 +3,31 @@ package utils
import ( import (
"approveflow/app/http/base" "approveflow/app/http/base"
"encoding/json" "encoding/json"
"fmt"
"github.com/Superdanda/hade/framework/gin" "github.com/Superdanda/hade/framework/gin"
"net/http" "net/http"
) )
func ConvertToSpecificType[T any, I interface{}](items []I, convertFunc func(I) (T, bool)) ([]T, error) {
specificItems := make([]T, 0, len(items))
for i, item := range items {
if specificItem, ok := convertFunc(item); ok {
specificItems = append(specificItems, specificItem)
} else {
return nil, fmt.Errorf("invalid type at index %d", i)
}
}
return specificItems, nil
}
func ConvertToAbstractNodes[T any, I interface{}](items []T, toInterface func(T) I) []I {
nodes := make([]I, len(items))
for i, item := range items {
nodes[i] = toInterface(item)
}
return nodes
}
func QuickBind[T any](c *gin.Context) *T { func QuickBind[T any](c *gin.Context) *T {
var params T var params T
if err := c.ShouldBindJSON(&params); err != nil { if err := c.ShouldBindJSON(&params); err != nil {