提交 b1233097 编写于 作者: Mr.奇淼('s avatar Mr.奇淼(

增加工作流使用方法 接口

增加工作流通用完成方法
增加查询待办
查询我发起的工作流功能
增加测试示例
上级 26e46a74
......@@ -2,6 +2,7 @@ package v1
import (
"fmt"
"gin-vue-admin/global"
"gin-vue-admin/model"
"gin-vue-admin/model/request"
"gin-vue-admin/model/response"
......@@ -146,3 +147,70 @@ func GetWorkflowProcessList(c *gin.Context) {
}, c)
}
}
// @Tags WorkflowProcess
// @Summary 开启工作流
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /workflowProcess/startWorkflow [post]
func StartWorkflow(c *gin.Context) {
business := c.Query("businessType")
wfInfo := model.WorkflowBusinessStruct[business]()
c.ShouldBindJSON(wfInfo)
err := service.StartWorkflow(wfInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage("启动成功", c)
}
// @Tags WorkflowProcess
// @Summary 我发起的工作流
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /workflowProcess/getMyStated [get]
func GetMyStated(c *gin.Context) {
if claims, exists := c.Get("claims"); !exists {
errStr := "从Gin的Context中获取从jwt解析出来的用户ID失败, 请检查路由是否使用jwt中间件"
global.GVA_LOG.Error(errStr)
response.FailWithMessage(errStr, c)
} else {
waitUse := claims.(*request.CustomClaims)
err, wfms := service.GetMyStated(waitUse.ID)
if err != nil {
errStr := err.Error()
global.GVA_LOG.Error(errStr)
response.FailWithMessage(errStr, c)
}
response.OkWithData(gin.H{"wfms": wfms}, c)
}
}
// @Tags WorkflowProcess
// @Summary 我的待办
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /workflowProcess/getMyNeed [get]
func GetMyNeed(c *gin.Context) {
if claims, exists := c.Get("claims"); !exists {
errStr := "从Gin的Context中获取从jwt解析出来的用户ID失败, 请检查路由是否使用jwt中间件"
global.GVA_LOG.Error(errStr)
response.FailWithMessage(errStr, c)
} else {
waitUse := claims.(*request.CustomClaims)
err, wfms := service.GetMyNeed(waitUse.ID, waitUse.AuthorityId)
if err != nil {
errStr := err.Error()
global.GVA_LOG.Error(errStr)
response.FailWithMessage(errStr, c)
}
response.OkWithData(gin.H{"wfms": wfms}, c)
}
}
......@@ -83,6 +83,9 @@ var Apis = []model.SysApi{
{global.GVA_MODEL{ID: 72, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/workflowProcess/findWorkflowProcess", "根据ID获取工作流", "workflowProcess", "GET"},
{global.GVA_MODEL{ID: 73, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/workflowProcess/getWorkflowProcessList", "获取工作流", "workflowProcess", "GET"},
{global.GVA_MODEL{ID: 74, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/workflowProcess/findWorkflowStep", "获取工作流步骤", "workflowProcess", "GET"},
{global.GVA_MODEL{ID: 75, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/workflowProcess/startWorkflow", "启动工作流", "workflowProcess", "POST"},
{global.GVA_MODEL{ID: 76, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/workflowProcess/getMyStated", "获取我发起的工作流", "workflowProcess", "GET"},
{global.GVA_MODEL{ID: 77, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/workflowProcess/GetMyNeed", "获取我的待办", "workflowProcess", "GET"},
}
func InitSysApi(db *gorm.DB) (err error) {
......
......@@ -42,6 +42,8 @@ var AuthorityMenus = []SysAuthorityMenus{
{"888", 29},
{"888", 30},
{"888", 31},
{"888", 32},
{"888", 33},
{"8881", 1},
{"8881", 2},
{"8881", 8},
......
......@@ -81,6 +81,9 @@ var Carbines = []gormadapter.CasbinRule{
{PType: "p", V0: "888", V1: "/workflowProcess/findWorkflowProcess", V2: "GET"},
{PType: "p", V0: "888", V1: "/workflowProcess/getWorkflowProcessList", V2: "GET"},
{PType: "p", V0: "888", V1: "/workflowProcess/findWorkflowStep", V2: "GET"},
{PType: "p", V0: "888", V1: "/workflowProcess/startWorkflow", V2: "POST"},
{PType: "p", V0: "888", V1: "/workflowProcess/getMyStated", V2: "GET"},
{PType: "p", V0: "888", V1: "/workflowProcess/GetMyNeed", V2: "GET"},
{PType: "p", V0: "8881", V1: "/base/login", V2: "POST"},
{PType: "p", V0: "8881", V1: "/user/register", V2: "POST"},
{PType: "p", V0: "8881", V1: "/api/createApi", V2: "POST"},
......
......@@ -41,6 +41,8 @@ var BaseMenus = []model.SysBaseMenu{
{GVA_MODEL: global.GVA_MODEL{ID: 29, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "28", Path: "workflowCreate", Name: "workflowCreate", Hidden: false, Component: "view/workflow/workflowCreate/workflowCreate.vue", Sort: 0, Meta: model.Meta{Title: "工作流绘制", Icon: "circle-plus"}},
{GVA_MODEL: global.GVA_MODEL{ID: 30, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "28", Path: "workflowProcess", Name: "workflowProcess", Hidden: false, Component: "view/workflow/workflowProcess/workflowProcess.vue", Sort: 0, Meta: model.Meta{Title: "工作流列表", Icon: "s-cooperation"}},
{GVA_MODEL: global.GVA_MODEL{ID: 31, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "28", Path: "workflowUse", Name: "workflowUse", Hidden: true, Component: "view/workflow/workflowUse/workflowUse.vue", Sort: 0, Meta: model.Meta{Title: "使用工作流", Icon: "video-play"}},
{GVA_MODEL: global.GVA_MODEL{ID: 32, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "28", Path: "started", Name: "started", Hidden: false, Component: "view/workflow/userList/started.vue", Sort: 0, Meta: model.Meta{Title: "我发起的", Icon: "s-order"}},
{GVA_MODEL: global.GVA_MODEL{ID: 33, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "28", Path: "need", Name: "need", Hidden: false, Component: "view/workflow/userList/need.vue", Sort: 0, Meta: model.Meta{Title: "我的待办", Icon: "s-platform"}},
}
func InitSysBaseMenus(db *gorm.DB) (err error) {
......
......@@ -17,6 +17,7 @@ func RunWindowsServer() {
// 初始化redis服务
initialize.Redis()
}
initialize.InitWkMode()
Router := initialize.Routers()
Router.Static("/form-generator", "./resource/page")
......
module gin-vue-admin
go 1.12
go 1.14
require (
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
......
......@@ -42,6 +42,8 @@ func MysqlTables(db *gorm.DB) {
model.WorkflowEdge{},
model.WorkflowStartPoint{},
model.WorkflowEndPoint{},
model.WorkflowMove{},
model.ExaWfLeave{},
)
if err != nil {
global.GVA_LOG.Error("register table failed", zap.Any("err", err))
......
......@@ -42,6 +42,7 @@ func Routers() *gin.Engine {
router.InitSysOperationRecordRouter(ApiGroup) // 操作记录
router.InitEmailRouter(ApiGroup) // 邮件相关路由
router.InitWorkflowProcessRouter(ApiGroup) // 工作流创建相关接口
router.InitExaWfLeaveRouter(ApiGroup) // 工作流创建相关接口
global.GVA_LOG.Info("router register success")
return Router
......
package initialize
import "gin-vue-admin/model"
func initWkModel() {
model.WorkflowBusinessStruct = make(map[string]func() model.GVA_Workflow)
model.WorkflowBusinessStruct["leave"] = func() model.GVA_Workflow {
return new(model.ExaWfLeaveWorkflow)
}
}
func initWkTable() {
model.WorkflowBusinessTable = make(map[string]string)
model.WorkflowBusinessTable["leave"] = "exa_wf_leaves"
}
func InitWkMode() {
initWkModel()
initWkTable()
}
// 自动生成模板ExaWfLeave
package model
import (
"gin-vue-admin/global"
"time"
)
// 如果含有time.Time 请自行import time包
type ExaWfLeave struct {
global.GVA_MODEL
Cause string `json:"cause" form:"cause" gorm:"column:cause;comment:"`
StartTime time.Time `json:"startTime" form:"startTime" gorm:"column:start_time;comment:"`
EndTime time.Time `json:"endTime" form:"endTime" gorm:"column:end_time;comment:"`
}
type ExaWfLeaveWorkflow struct {
// 工作流操作结构体
WorkflowBase `json:"wf"`
ExaWfLeave `json:"business"`
}
......@@ -6,24 +6,49 @@ import (
"time"
)
var WorkflowBusinessStruct map[string]func() GVA_Workflow
var WorkflowBusinessTable map[string]string
type GVA_Workflow interface {
CreateWorkflow(WorkflowNode,GVA_Workflow)
GetBusinessType()string
GetTableName() string
CreateWorkflowMove() *WorkflowMove
GetBusinessType() string
GetWorkflowBase() WorkflowBase
}
type WorkflowFunc struct {
BusinessType string
type WorkflowBase struct {
BusinessID uint `gorm:"<-:false;column:id"` // 业务对应ID(businessID)的返回
BusinessType string `json:"businessType" gorm:"-"`
PromoterID uint `json:"promoterID" gorm:"-"`
WorkflowProcessID string `json:"workflowProcessID" gorm:"-"`
WorkflowNodeID string `json:"workflowNodeID" gorm:"-"`
Action string `json:"action" gorm:"-"`
}
func(w *WorkflowFunc)CreateWorkflow(node WorkflowNode,businessModel GVA_Workflow){
func (w WorkflowBase) CreateWorkflowMove() (businessModel *WorkflowMove) {
return &WorkflowMove{
BusinessType: w.BusinessType,
PromoterID: w.PromoterID,
WorkflowProcessID: w.WorkflowProcessID,
WorkflowNodeID: w.WorkflowNodeID,
BusinessID: w.BusinessID,
Action: w.Action,
IsActive: true,
}
}
func(w *WorkflowFunc)GetBusinessType()(businessType string){
func (w WorkflowBase) GetBusinessType() (businessType string) {
return w.BusinessType
}
func (w WorkflowBase) GetWorkflowBase() (workflowBase WorkflowBase) {
return w
}
func (w WorkflowBase) GetTableName() string {
return WorkflowBusinessTable[w.BusinessType]
}
//定义clazz常量
const (
......@@ -40,23 +65,16 @@ const (
PROCESS string = "process"
)
type WorkflowMove struct {
type WorkflowMove struct {
global.GVA_MODEL
WorkflowProcessID string `json:"workflowProcessID" gorm:"comment:工作流模板ID"`
BusinessType string `json:"businessType" gorm:"comment:业务标记"`
BusinessID uint `json:"businessID" gorm:"comment:业务ID"`
WorkflowMoveInfo []WorkflowMoveInfo `json:"workflowMoveInfo" gorm:"comment:当前流转详情"`
Promoter uint `json:"Promoter" gorm:"comment:当前流转发起人"`
}
type WorkflowMoveInfo struct {
global.GVA_MODEL
WorkflowMoveID uint `json:"workflowMoveID" gorm:"comment:关联WorkflowMove"`
WorkflowNodeID string `json:"workflowNodeID" gorm:"comment:流程节点ID"`
Params string `json:"params" gorm:"comment:流转参数"`
Description string `json:"description" gorm:"comment:流转说明"`
Status int `json:"status" gorm:"comment:流转状态(来自字典 0为进行中)"`
WorkflowNodeID string `json:"workflowNodeID" gorm:"comment:工作流节点ID"`
BusinessType string `json:"businessType" gorm:"comment:业务标记"`
BusinessID uint `json:"businessID" gorm:"comment:业务ID"`
PromoterID uint `json:"promoterID" gorm:"comment:当前流转发起人"`
Action string `json:"action" gorm:"comment:工作流驱动事件"`
Param string `json:"param" gorm:"comment:工作流驱动参数"`
IsActive bool `json:"isActive" gorm:"comment:是否是活跃节点 "`
}
type WorkflowProcess struct {
......@@ -80,7 +98,7 @@ type WorkflowNode struct {
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
WorkflowProcessID string `json:"-" gorm:"comment:流程标识"`
WorkflowProcessID string `json:"workflowProcessID" gorm:"comment:流程标识"`
Clazz string `json:"clazz" gorm:"comment:节点类型"`
Label string `json:"label" gorm:"comment:节点名称"`
Type string `json:"type" gorm:"comment:图标类型"`
......
......@@ -14,7 +14,10 @@ func InitWorkflowProcessRouter(Router *gin.RouterGroup) {
WorkflowProcessRouter.DELETE("deleteWorkflowProcessByIds", v1.DeleteWorkflowProcessByIds) // 批量删除WorkflowProcess
WorkflowProcessRouter.PUT("updateWorkflowProcess", v1.UpdateWorkflowProcess) // 更新WorkflowProcess
WorkflowProcessRouter.GET("findWorkflowProcess", v1.FindWorkflowProcess) // 根据ID获取WorkflowProcess
WorkflowProcessRouter.GET("findWorkflowStep", v1.FindWorkflowStep) // 根据ID获取工作流步骤
WorkflowProcessRouter.GET("findWorkflowStep", v1.FindWorkflowStep) // 根据ID获取工作流步骤
WorkflowProcessRouter.GET("getWorkflowProcessList", v1.GetWorkflowProcessList) // 获取WorkflowProcess列表
WorkflowProcessRouter.POST("startWorkflow", v1.StartWorkflow) // 开启工作流
WorkflowProcessRouter.GET("getMyStated", v1.GetMyStated) // 获取我发起的工作流
WorkflowProcessRouter.GET("getMyNeed", v1.GetMyNeed) // 获取我的待办
}
}
package service
import (
"errors"
"gin-vue-admin/global"
"gin-vue-admin/model"
"gin-vue-admin/model/request"
"gorm.io/gorm"
"strconv"
)
//@author: [piexlmax](https://github.com/piexlmax)
......@@ -151,3 +153,127 @@ func GetWorkflowProcessInfoList(info request.WorkflowProcessSearch) (err error,
err = db.Limit(limit).Offset(offset).Find(&workflowProcesss).Error
return err, workflowProcesss, total
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: StartWorkflow
//@description: 开启一个工作流
//@param: wfInterface model.GVA_Workflow
//@return: err error
func StartWorkflow(wfInterface model.GVA_Workflow) (err error) {
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
var txErr error
txErr = tx.Table(wfInterface.GetTableName()).Create(wfInterface).Error
if txErr != nil {
return txErr
}
wfm := wfInterface.CreateWorkflowMove()
txErr = tx.Create(wfm).Error
if txErr != nil {
return txErr
}
txErr = complete(tx, wfm)
if txErr != nil {
return txErr
}
return nil
})
return err
}
//func CompleteWorkflowNode(wfInterface model.GVA_Workflow)(err error){
//
//}
func complete(tx *gorm.DB, wfm *model.WorkflowMove) (err error) {
var returnWfm model.WorkflowMove
var nodeInfo model.WorkflowNode
var Edges []model.WorkflowEdge
txErr := tx.First(&returnWfm, "business_type = ? and business_id = ? and workflow_process_id = ? and workflow_node_id = ? and is_active = ?", wfm.BusinessType, wfm.BusinessID, wfm.WorkflowProcessID, wfm.WorkflowNodeID, true).Error
if txErr != nil {
return txErr
}
txErr = tx.First(&nodeInfo, "ID = ?", wfm.WorkflowNodeID).Error
if txErr != nil {
return txErr
}
if nodeInfo.Clazz == model.START || nodeInfo.Clazz == model.USER_TASK {
txErr = tx.Find(&Edges, "workflow_process_id = ? and source = ?", wfm.WorkflowProcessID, wfm.WorkflowNodeID).Error
if txErr != nil {
return txErr
}
if len(Edges) == 0 {
return errors.New("不存在当前节点为起点的后续流程")
}
if len(Edges) == 1 {
//当前节点为初始节点时候
if nodeInfo.Clazz == model.START {
txErr = tx.Where("id = ?", returnWfm.ID).First(&model.WorkflowMove{}).Update("is_active", false).Error
if txErr != nil {
return txErr
}
}
//当前节点为流转节点时候
if nodeInfo.Clazz == model.USER_TASK {
txErr = tx.Where("id = ?", returnWfm.ID).First(&model.WorkflowMove{}).Update("action", "complete").Update("is_active", false).Error
if txErr != nil {
return txErr
}
}
newWfm := createNewWorkflowMove(&returnWfm, Edges[0].Target)
txErr = tx.Create(newWfm).Error
if txErr != nil {
return txErr
}
// 当target为自动节点时候 需要做一些事情 这里暂时先不处理 后续慢慢完善
}
if len(Edges) > 1 {
var needUseTargetNodeID string
txErr = tx.Where("id = ?", returnWfm.ID).Update("is_active", false).Error
if txErr != nil {
return txErr
}
for _, v := range Edges {
if v.ConditionExpression == wfm.Param {
needUseTargetNodeID = v.Target
break
}
}
newWfm := createNewWorkflowMove(&returnWfm, needUseTargetNodeID)
txErr = tx.Create(newWfm).Error
if txErr != nil {
return txErr
}
// 当target为自动节点时候 需要做一些事情 这里暂时先不处理 后续慢慢完善
}
} else {
return errors.New("目前只支持start节点和userTask功能,其他功能正在开发中")
}
return nil
}
func createNewWorkflowMove(oldWfm *model.WorkflowMove, targetNodeID string) (newWfm *model.WorkflowMove) {
return &model.WorkflowMove{
BusinessID: oldWfm.BusinessID,
BusinessType: oldWfm.BusinessType,
PromoterID: oldWfm.PromoterID,
WorkflowNodeID: targetNodeID,
WorkflowProcessID: oldWfm.WorkflowProcessID,
Action: "",
IsActive: true,
}
}
func GetMyStated(userID uint) (err error, wfms []model.WorkflowMove) {
err = global.GVA_DB.Find(&wfms, "promoter_id = ? and is_active", userID, true).Error
return err, wfms
}
func GetMyNeed(userID uint, AuthorityID string) (err error, wfms []model.WorkflowMove) {
user := "%," + strconv.Itoa(int(userID)) + ",%"
auth := "%," + AuthorityID + ",%"
err = global.GVA_DB.Joins("INNER JOIN workflow_nodes as node ON workflow_moves.workflow_node_id = node.id").Where("(node.assign_type = ? AND node.assign_value LIKE ? ) OR (node.assign_type = ? AND node.assign_value LIKE ? )", "user", user, "authority", auth).Find(&wfms).Error
return err, wfms
}
<template>
<div>
<el-form :model="formData" label-position="right" label-width="80px">
<el-form-item label="请假原因:">
<el-input
v-model="formData.cause"
clearable
placeholder="请输入"
></el-input>
</el-form-item>
<el-form-item label="开始时间:">
<el-date-picker
type="date"
placeholder="选择日期"
v-model="formData.startTime"
clearable
></el-date-picker>
</el-form-item>
<el-form-item label="结束时间:">
<el-date-picker
type="date"
placeholder="选择日期"
v-model="formData.endTime"
clearable
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button @click="start" type="primary">启动</el-button>
<el-button @click="back" type="primary">返回</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {
startWorkflow,
} from "@/api/workflowProcess";
import infoList from "@/mixins/infoList";
import { mapGetters } from "vuex";
export default {
name: "ExaWfLeave",
mixins: [infoList],
props:{
wf:{
type:Object,
default:function(){return{}}
}
},
data() {
return {
type: "",formData: {
cause:"",
startTime:new Date(),
endTime:new Date(),
}
};
},
computed:{
...mapGetters("user", ["userInfo"])
},
methods: {
async start() {
const res = await startWorkflow({
business:this.formData,
wf:{
businessId:0,
businessType:"leave",
workflowProcessID:this.wf.workflowProcessID,
workflowNodeID:this.wf.id,
promoterID:this.userInfo.ID,
action:"create",
param:""
}
});
if (res.code == 0) {
this.$message({
type:"success",
message:"启动成功"
})
}
},
back(){
this.$router.go(-1)
}
},
async created() {
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
}
};
</script>
<style>
</style>
\ No newline at end of file
<template>
<div class="workflow-use">
<WorkflowInfo v-if="done" :wf="this.node"/>
</div>
</template>
<script>
......@@ -9,7 +10,7 @@ export default {
name:"WorklowUse",
data(){
return{
done:false
}
},
async created(){
......@@ -20,9 +21,13 @@ export default {
const res = await findWorkflowStep({id:workflowId})
if(res.code == 0){
this.workflow = res.data.workflow
this.node = res.data.workflow.node[0]
this.node = res.data.workflow.nodes[0]
this.done = true
}
}
},
beforeCreate(){
this.$options.components.WorkflowInfo = ()=>import("@/"+this.node.view)
}
}
</script>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册