[add] WorkFlow 定义
This commit is contained in:
parent
98377d4a3b
commit
668b33ffb7
23 changed files with 661 additions and 39 deletions
|
|
@ -27,22 +27,24 @@ type Config struct {
|
|||
|
||||
// XiangConfig 象传应用引擎配置
|
||||
type XiangConfig struct {
|
||||
Mode string `json:"mode,omitempty" env:"XIANG_MODE" envDefault:"release"` // 象传引擎模式 debug/release/test
|
||||
Source string `json:"source,omitempty" env:"XIANG_SOURCE" envDefault:"fs://."` // 源码路径(用于单元测试载入数据)
|
||||
Path string `json:"path,omitempty" env:"XIANG_PATH" envDefault:"bin://xiang"` // 引擎文件目录
|
||||
Root string `json:"root,omitempty" env:"XIANG_ROOT" envDefault:"fs://."` // 应用文件目录
|
||||
RootUI string `json:"root_ui,omitempty" env:"XIANG_ROOT_UI"` // 应用界面静态文件目录
|
||||
RootDB string `json:"root_db,omitempty" env:"XIANG_ROOT_DB"` // 应用SQLite数据库目录
|
||||
RootData string `json:"root_data,omitempty" env:"XIANG_ROOT_DATA"` // 应用数据文件目录
|
||||
RootAPI string `json:"root_api,omitempty" env:"XIANG_ROOT_API"` // 应用API文件目录
|
||||
RootModel string `json:"root_model,omitempty" env:"XIANG_ROOT_MODEL"` // 应用模型文件目录
|
||||
RootFLow string `json:"root_flow,omitempty" env:"XIANG_ROOT_FLOW"` // 应用业务逻辑文件目录
|
||||
RootPlugin string `json:"root_plugin,omitempty" env:"XIANG_ROOT_PLUGIN"` // 应用业务插件文件目录
|
||||
RootLib string `json:"root_lib,omitempty" env:"XIANG_ROOT_LIB"` // 应用资料库文件目录
|
||||
RootTable string `json:"root_table,omitempty" env:"XIANG_ROOT_TABLE"` // 应用数据表格文件目录
|
||||
RootChart string `json:"root_chart,omitempty" env:"XIANG_ROOT_CHART"` // 应用分析图表文件目录
|
||||
RootPage string `json:"root_page,omitempty" env:"XIANG_ROOT_PAGE"` // 应用通用页面文件目录
|
||||
RootScreen string `json:"root_screen,omitempty" env:"XIANG_ROOT_SCREEN"` // 应用大屏文件目录
|
||||
Mode string `json:"mode,omitempty" env:"XIANG_MODE" envDefault:"release"` // 象传引擎模式 debug/release/test
|
||||
Source string `json:"source,omitempty" env:"XIANG_SOURCE" envDefault:"fs://."` // 源码路径(用于单元测试载入数据)
|
||||
Path string `json:"path,omitempty" env:"XIANG_PATH" envDefault:"bin://xiang"` // 引擎文件目录
|
||||
Root string `json:"root,omitempty" env:"XIANG_ROOT" envDefault:"fs://."` // 应用文件目录
|
||||
RootUI string `json:"root_ui,omitempty" env:"XIANG_ROOT_UI"` // 应用界面静态文件目录
|
||||
RootDB string `json:"root_db,omitempty" env:"XIANG_ROOT_DB"` // 应用SQLite数据库目录
|
||||
RootData string `json:"root_data,omitempty" env:"XIANG_ROOT_DATA"` // 应用数据文件目录
|
||||
RootAPI string `json:"root_api,omitempty" env:"XIANG_ROOT_API"` // 应用API文件目录
|
||||
RootModel string `json:"root_model,omitempty" env:"XIANG_ROOT_MODEL"` // 应用模型文件目录
|
||||
RootFLow string `json:"root_flow,omitempty" env:"XIANG_ROOT_FLOW"` // 应用业务逻辑文件目录
|
||||
RootPlugin string `json:"root_plugin,omitempty" env:"XIANG_ROOT_PLUGIN"` // 应用业务插件文件目录
|
||||
RootLib string `json:"root_lib,omitempty" env:"XIANG_ROOT_LIB"` // 应用资料库文件目录
|
||||
RootTable string `json:"root_table,omitempty" env:"XIANG_ROOT_TABLE"` // 应用数据表格文件目录
|
||||
RootChart string `json:"root_chart,omitempty" env:"XIANG_ROOT_CHART"` // 应用分析图表文件目录
|
||||
RootPage string `json:"root_page,omitempty" env:"XIANG_ROOT_PAGE"` // 应用通用页面文件目录
|
||||
RootScreen string `json:"root_screen,omitempty" env:"XIANG_ROOT_SCREEN"` // 应用大屏文件目录
|
||||
RootForm string `json:"root_form,omitempty" env:"XIANG_ROOT_FORM"` // 表单文件目录
|
||||
RootWorkFlow string `json:"root_workflow,omitempty" env:"XIANG_ROOT_WORKFLOW"` // 工作流文件目录
|
||||
}
|
||||
|
||||
// ServiceConfig 服务配置
|
||||
|
|
|
|||
|
|
@ -20,5 +20,5 @@ func check(t *testing.T) {
|
|||
for key := range gou.Flows {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
assert.Equal(t, 2, len(keys))
|
||||
assert.Equal(t, 10, len(keys))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package helper
|
||||
|
||||
import "github.com/yaoapp/gou"
|
||||
import "github.com/yaoapp/kun/maps"
|
||||
|
||||
// MapValues 返回映射的数值
|
||||
// MapValues 返回映射表的数值
|
||||
func MapValues(record map[string]interface{}) []interface{} {
|
||||
values := []interface{}{}
|
||||
for _, value := range record {
|
||||
|
|
@ -11,7 +11,7 @@ func MapValues(record map[string]interface{}) []interface{} {
|
|||
return values
|
||||
}
|
||||
|
||||
// MapKeys 返回映射的键
|
||||
// MapKeys 返回映射表的键
|
||||
func MapKeys(record map[string]interface{}) []string {
|
||||
keys := []string{}
|
||||
for key := range record {
|
||||
|
|
@ -20,16 +20,8 @@ func MapKeys(record map[string]interface{}) []string {
|
|||
return keys
|
||||
}
|
||||
|
||||
// ProcessMapValues xiang.helper.MapValues 返回映射的数值
|
||||
func ProcessMapValues(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
record := process.ArgsMap(0)
|
||||
return MapValues(record)
|
||||
}
|
||||
|
||||
// ProcessMapKeys xiang.helper.MapKeys 返回映射的键
|
||||
func ProcessMapKeys(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
record := process.ArgsMap(0)
|
||||
return MapKeys(record)
|
||||
// MapGet xiang.helper.MapGet 返回映射表给定键的值
|
||||
func MapGet(record map[string]interface{}, key string) interface{} {
|
||||
data := maps.MapOf(record).Dot()
|
||||
return data.Get(key)
|
||||
}
|
||||
|
|
|
|||
25
helper/map.process.go
Normal file
25
helper/map.process.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package helper
|
||||
|
||||
import "github.com/yaoapp/gou"
|
||||
|
||||
// ProcessMapValues xiang.helper.MapValues 返回映射表的数值
|
||||
func ProcessMapValues(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
record := process.ArgsMap(0)
|
||||
return MapValues(record)
|
||||
}
|
||||
|
||||
// ProcessMapKeys xiang.helper.MapKeys 返回映射表的键
|
||||
func ProcessMapKeys(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
record := process.ArgsMap(0)
|
||||
return MapKeys(record)
|
||||
}
|
||||
|
||||
// ProcessMapGet xiang.helper.MapKey 返回映射表给定键的值
|
||||
func ProcessMapGet(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
record := process.ArgsMap(0)
|
||||
key := process.ArgsString(1)
|
||||
return MapGet(record, key)
|
||||
}
|
||||
|
|
@ -12,17 +12,24 @@ func init() {
|
|||
gou.RegisterProcessHandler("xiang.helper.ArrayColumn", ProcessArrayColumn)
|
||||
gou.RegisterProcessHandler("xiang.helper.ArrayKeep", ProcessArrayKeep)
|
||||
gou.RegisterProcessHandler("xiang.helper.ArrayTree", ProcessArrayTree)
|
||||
|
||||
gou.RegisterProcessHandler("xiang.helper.MapKeys", ProcessMapKeys)
|
||||
gou.RegisterProcessHandler("xiang.helper.MapValues", ProcessMapValues)
|
||||
gou.RegisterProcessHandler("xiang.helper.MapGet", ProcessMapGet)
|
||||
|
||||
gou.RegisterProcessHandler("xiang.helper.Captcha", ProcessCaptcha)
|
||||
gou.RegisterProcessHandler("xiang.helper.CaptchaValidate", ProcessCaptchaValidate)
|
||||
|
||||
gou.RegisterProcessHandler("xiang.helper.PasswordValidate", ProcessPasswordValidate)
|
||||
|
||||
gou.RegisterProcessHandler("xiang.helper.JwtMake", ProcessJwtMake)
|
||||
gou.RegisterProcessHandler("xiang.helper.JwtValidate", ProcessJwtValidate)
|
||||
|
||||
gou.RegisterProcessHandler("xiang.helper.For", ProcessFor)
|
||||
gou.RegisterProcessHandler("xiang.helper.Each", ProcessEach)
|
||||
gou.RegisterProcessHandler("xiang.helper.Case", ProcessCase)
|
||||
gou.RegisterProcessHandler("xiang.helper.IF", ProcessIF)
|
||||
|
||||
gou.RegisterProcessHandler("xiang.helper.Print", ProcessPrint)
|
||||
}
|
||||
|
||||
|
|
|
|||
12
main_test.go
12
main_test.go
|
|
@ -13,7 +13,6 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/yaoapp/kun/any"
|
||||
"github.com/yaoapp/kun/maps"
|
||||
"github.com/yaoapp/kun/utils"
|
||||
"github.com/yaoapp/xiang/config"
|
||||
"github.com/yaoapp/xiang/service"
|
||||
)
|
||||
|
|
@ -53,7 +52,7 @@ func TestCommandStart(t *testing.T) {
|
|||
// 发送请求
|
||||
request := func() (maps.MapStr, error) {
|
||||
time.Sleep(time.Microsecond * 2000)
|
||||
url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "local.iqka.com", config.Conf.Service.Port)
|
||||
url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "127.0.0.1", config.Conf.Service.Port)
|
||||
// utils.Dump(url)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
|
|
@ -70,11 +69,14 @@ func TestCommandStart(t *testing.T) {
|
|||
}
|
||||
|
||||
// 等待服务启动
|
||||
time.Sleep(time.Second * 2)
|
||||
times := 0
|
||||
for times < 20 { // 2秒超时
|
||||
times++
|
||||
fmt.Printf("Trying(%d)...", times)
|
||||
res, err := request()
|
||||
if err != nil {
|
||||
fmt.Printf(" %s\n", err.Error())
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, 1, any.Of(res.Get("id")).CInt())
|
||||
|
|
@ -98,8 +100,7 @@ func TestCommandStop(t *testing.T) {
|
|||
// 发送请求
|
||||
request := func() (maps.MapStr, error) {
|
||||
time.Sleep(time.Microsecond * 2000)
|
||||
url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "local.iqka.com", config.Conf.Service.Port)
|
||||
utils.Dump(url)
|
||||
url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "127.0.0.1", config.Conf.Service.Port)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -115,6 +116,7 @@ func TestCommandStop(t *testing.T) {
|
|||
}
|
||||
|
||||
// 等待服务启动
|
||||
time.Sleep(time.Second * 2)
|
||||
times := 0
|
||||
for times < 20 { // 2秒超时
|
||||
times++
|
||||
|
|
@ -127,7 +129,7 @@ func TestCommandStop(t *testing.T) {
|
|||
|
||||
// 测试关闭
|
||||
service.Stop(func() { log.Println("服务已关闭") })
|
||||
time.Sleep(time.Second * 2)
|
||||
time.Sleep(time.Second * 5)
|
||||
_, err = request()
|
||||
assert.NotNil(t, err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ var AppFileServer http.Handler = http.FileServer(http.Dir(config.Conf.RootUI))
|
|||
|
||||
// Middlewares 服务中间件
|
||||
var Middlewares = []gin.HandlerFunc{
|
||||
BindDomain,
|
||||
// BindDomain,
|
||||
BinStatic,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,5 +48,5 @@ func check(t *testing.T) {
|
|||
for key := range Tables {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
assert.Equal(t, 3, len(keys))
|
||||
assert.Equal(t, 4, len(keys))
|
||||
}
|
||||
|
|
|
|||
43
tests/flows/assign/reject/reset.flow.json
Normal file
43
tests/flows/assign/reject/reset.flow.json
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"label": "审批驳回并重置状态",
|
||||
"version": "1.0.0",
|
||||
"description": "审批驳回并重置状态",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "当前用户",
|
||||
"process": "session.Get",
|
||||
"args": ["user"]
|
||||
},
|
||||
{
|
||||
"name": "工作流",
|
||||
"process": "xiang.workflow.Save",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$name}}",
|
||||
"{{$node}}",
|
||||
"{{$id}}",
|
||||
"{{$input}}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "审批驳回",
|
||||
"process": "xiang.workflow.Next",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$res.工作流.id}}",
|
||||
{ "审批状态": "驳回", "驳回原因": "{{$input.reason}}" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "重置工作流",
|
||||
"process": "xiang.workflow.Goto",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$res.工作流.id}}",
|
||||
"选择商务负责人",
|
||||
{}
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": "{{$res.重置工作流}}"
|
||||
}
|
||||
38
tests/flows/assign/reject/terminal.flow.json
Normal file
38
tests/flows/assign/reject/terminal.flow.json
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"label": "审批驳回并关闭流程",
|
||||
"version": "1.0.0",
|
||||
"description": "审批驳回并关闭流程",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "当前用户",
|
||||
"process": "session.Get",
|
||||
"args": ["user"]
|
||||
},
|
||||
{
|
||||
"name": "工作流",
|
||||
"process": "xiang.workflow.Save",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$name}}",
|
||||
"{{$node}}",
|
||||
"{{$id}}",
|
||||
"{{$input}}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "审批驳回",
|
||||
"process": "xiang.workflow.Next",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$res.工作流.id}}",
|
||||
{ "审批状态": "驳回", "驳回原因": "{{$input.reason}}" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "关闭流程",
|
||||
"process": "xiang.workflow.Status",
|
||||
"args": ["{{$res.工作流.id}}", "已关闭"]
|
||||
}
|
||||
],
|
||||
"output": "{{$res.关闭流程}}"
|
||||
}
|
||||
33
tests/flows/assign/resolve.flow.json
Normal file
33
tests/flows/assign/resolve.flow.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"label": "指派信息审批通过",
|
||||
"version": "1.0.0",
|
||||
"description": "指派信息审批通过",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "当前用户",
|
||||
"process": "session.Get",
|
||||
"args": ["user"]
|
||||
},
|
||||
{
|
||||
"name": "工作流",
|
||||
"process": "xiang.workflow.Save",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$name}}",
|
||||
"{{$node}}",
|
||||
"{{$id}}",
|
||||
"{{$input}}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "审批通过",
|
||||
"process": "xiang.workflow.Next",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$res.工作流.id}}",
|
||||
{ "审批状态": "通过" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": "{{$res.审批通过}}"
|
||||
}
|
||||
24
tests/flows/assign/save.flow.json
Normal file
24
tests/flows/assign/save.flow.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"label": "保存指派信息",
|
||||
"version": "1.0.0",
|
||||
"description": "保存指派信息",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "当前用户",
|
||||
"process": "session.Get",
|
||||
"args": ["user"]
|
||||
},
|
||||
{
|
||||
"name": "工作流",
|
||||
"process": "xiang.workflow.Save",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$name}}",
|
||||
"{{$node}}",
|
||||
"{{$id}}",
|
||||
"{{$input}}"
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": "{{$res.工作流}}"
|
||||
}
|
||||
29
tests/flows/assign/submit.flow.json
Normal file
29
tests/flows/assign/submit.flow.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"label": "提交指派信息",
|
||||
"version": "1.0.0",
|
||||
"description": "提交指派信息",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "当前用户",
|
||||
"process": "session.Get",
|
||||
"args": ["user"]
|
||||
},
|
||||
{
|
||||
"name": "工作流",
|
||||
"process": "xiang.workflow.Save",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$name}}",
|
||||
"{{$node}}",
|
||||
"{{$id}}",
|
||||
"{{$input}}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "提交审批",
|
||||
"process": "xiang.workflow.Next",
|
||||
"args": ["{{$res.当前用户.id}}", "{{$res.工作流.id}}"]
|
||||
}
|
||||
],
|
||||
"output": "{{$res.关闭流程}}"
|
||||
}
|
||||
29
tests/flows/assign/terminal.flow.json
Normal file
29
tests/flows/assign/terminal.flow.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"label": "关闭流程",
|
||||
"version": "1.0.0",
|
||||
"description": "关闭流程",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "当前用户",
|
||||
"process": "session.Get",
|
||||
"args": ["user"]
|
||||
},
|
||||
{
|
||||
"name": "工作流",
|
||||
"process": "xiang.workflow.Save",
|
||||
"args": [
|
||||
"{{$res.当前用户.id}}",
|
||||
"{{$name}}",
|
||||
"{{$node}}",
|
||||
"{{$id}}",
|
||||
"{{$input}}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "关闭流程",
|
||||
"process": "xiang.workflow.Status",
|
||||
"args": ["{{$res.当前用户.id}}", "{{$res.工作流.id}}", "已关闭"]
|
||||
}
|
||||
],
|
||||
"output": "{{$res.关闭流程}}"
|
||||
}
|
||||
18
tests/flows/current/user.flow.json
Normal file
18
tests/flows/current/user.flow.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"label": "当前用户",
|
||||
"version": "1.0.0",
|
||||
"description": "读取当前用户",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "用户",
|
||||
"process": "session.Get",
|
||||
"args": ["user"]
|
||||
},
|
||||
{
|
||||
"name": "ID",
|
||||
"process": "xiang.helper.MapGet",
|
||||
"args": ["{{$res.用户}}", "ID"]
|
||||
}
|
||||
],
|
||||
"output": 1
|
||||
}
|
||||
19
tests/flows/select/manager.flow.json
Normal file
19
tests/flows/select/manager.flow.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"label": "选择管理者",
|
||||
"version": "1.0.0",
|
||||
"description": "选择管理者",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "负责人",
|
||||
"engine": "xiang",
|
||||
"query": {
|
||||
"select": ["id"],
|
||||
"from": "$xiang.user",
|
||||
"wheres": [{ ":type": "类型", "=": "staff" }],
|
||||
"first": true
|
||||
},
|
||||
"orders": [":RAND()"]
|
||||
}
|
||||
],
|
||||
"output": "{{$res.负责人.id}}"
|
||||
}
|
||||
30
tests/libs/assign.json
Normal file
30
tests/libs/assign.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"保存": {
|
||||
"label": "保存",
|
||||
"api": "/api/xiang/workflow/assign/save"
|
||||
},
|
||||
"提交": {
|
||||
"label": "提交",
|
||||
"api": "/api/xiang/workflow/assign/submit"
|
||||
},
|
||||
"同意": {
|
||||
"label": "同意",
|
||||
"api": "/api/xiang/workflow/assign/resolve"
|
||||
},
|
||||
"驳回并重填": {
|
||||
"label": "驳回并重填",
|
||||
"api": "/api/xiang/workflow/assign/reject/reset"
|
||||
},
|
||||
"驳回并关闭": {
|
||||
"label": "驳回并关闭",
|
||||
"api": "/api/xiang/workflow/assign/reject/reset"
|
||||
},
|
||||
"审批同意": {
|
||||
"label": "审批同意",
|
||||
"api": "/api/xiang/workflow/assign/terminal"
|
||||
},
|
||||
"关闭": {
|
||||
"label": "关闭",
|
||||
"api": "/api/xiang/workflow/terminal"
|
||||
}
|
||||
}
|
||||
|
|
@ -39,5 +39,29 @@
|
|||
"format": "YYYY年MM月DD日 @hh:mm:ss"
|
||||
}
|
||||
}
|
||||
},
|
||||
"商务负责人": {
|
||||
"label": "商务负责人",
|
||||
"view": {
|
||||
"name": "label",
|
||||
"props": {
|
||||
"value": ":kind.name"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"type": "select",
|
||||
"props": {
|
||||
"value": ":kind.id",
|
||||
"tree": true,
|
||||
"searchable": true,
|
||||
"remote": {
|
||||
"api": "/api/kind/tree",
|
||||
"query": {
|
||||
"select": ["id", "name"],
|
||||
"keyword": "where_name_like"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
tests/tables/assign.tab.json
Normal file
16
tests/tables/assign.tab.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "指派商务负责人",
|
||||
"version": "1.0.0",
|
||||
"decription": "指派商务负责人表单(?Next:表格和表单解耦)",
|
||||
"bind": { "model": "service" },
|
||||
"edit": {
|
||||
"primary": "id",
|
||||
"layout": {
|
||||
"fieldset": [
|
||||
{
|
||||
"columns": [{ "name": "商务负责人", "width": 24 }]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
83
tests/workflows/assign.wflow.json
Normal file
83
tests/workflows/assign.wflow.json
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
{
|
||||
"label": "指派商务负责人",
|
||||
"version": "1.0.0",
|
||||
"description": "指派商务负责人",
|
||||
"apis": {
|
||||
"/save": {
|
||||
"name": "保存",
|
||||
"process": "xiang.assign.save"
|
||||
},
|
||||
"/terminal": {
|
||||
"name": "关闭",
|
||||
"process": "xiang.assign.terminal"
|
||||
},
|
||||
"/submit": {
|
||||
"name": "提交审批",
|
||||
"process": "flows.assign.submit"
|
||||
},
|
||||
"/reject/reset": {
|
||||
"name": "审批驳回并重填",
|
||||
"process": "flows.assign.reject.rest"
|
||||
},
|
||||
"/reject/terminal": {
|
||||
"name": "审批驳回并关闭",
|
||||
"process": "flows.assign.reject.terminal"
|
||||
},
|
||||
"/resolve": {
|
||||
"name": "审批同意",
|
||||
"process": "flows.assign.resolve"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"关闭": { "@": "assign.关闭" },
|
||||
"保存": { "@": "assign.保存" },
|
||||
"提交": { "@": "assign.提交" },
|
||||
"同意": { "@": "assign.同意" },
|
||||
"驳回并重填": { "@": "assign.驳回并重填" },
|
||||
"驳回并关闭": { "@": "assign.驳回并关闭" }
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"name": "选择商务负责人",
|
||||
"body": {
|
||||
"type": "form",
|
||||
"props": { "name": "assign" }
|
||||
},
|
||||
"actions": ["关闭", "保存", "提交"],
|
||||
"user": { "process": "flows.current.user", "args": ["id"] }
|
||||
},
|
||||
{
|
||||
"name": "项目负责人审批",
|
||||
"body": {
|
||||
"type": "iframe",
|
||||
"props": { "src": "/workflows/assign/approve.html" }
|
||||
},
|
||||
"actions": ["驳回并重填", "驳回并关闭", "同意"],
|
||||
"user": { "process": "flows.select.manager", "args": ["id"] }
|
||||
},
|
||||
{
|
||||
"name": "审批通过",
|
||||
"when": [{ "审批通过": "{{$data.审批状态}}", "=": "通过" }],
|
||||
"body": {
|
||||
"type": "markdown",
|
||||
"props": {
|
||||
"content": "审批通过, {{$data.项目名称}}商务负责人为: **{{$data.商务负责人名称}}**"
|
||||
}
|
||||
},
|
||||
"actions": ["关闭"],
|
||||
"user": { "process": "flows.current.user", "args": ["id"] }
|
||||
},
|
||||
{
|
||||
"name": "审批驳回",
|
||||
"when": [{ "审批通过": "{{$data.审批状态}}", "=": "驳回" }],
|
||||
"body": {
|
||||
"type": "markdown",
|
||||
"props": {
|
||||
"content": "您的请求被驳回, 驳回原因: **{{$data.驳回原因}}**"
|
||||
}
|
||||
},
|
||||
"actions": ["关闭"],
|
||||
"user": { "process": "flows.current.user", "args": ["id"] }
|
||||
}
|
||||
]
|
||||
}
|
||||
35
workflow/types.go
Normal file
35
workflow/types.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package workflow
|
||||
|
||||
import "github.com/yaoapp/xiang/share"
|
||||
|
||||
// WorkFlow 工作流配置结构
|
||||
type WorkFlow struct {
|
||||
Name string `json:"-"`
|
||||
Source string `json:"-"`
|
||||
Version string `json:"version"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Decription string `json:"decription,omitempty"`
|
||||
Nodes []Node `json:"nodes"`
|
||||
APIs []API `json:"apis"`
|
||||
}
|
||||
|
||||
// Node 工作流节点
|
||||
type Node struct {
|
||||
Name string `json:"name"`
|
||||
Body share.Render `json:"body,omitempty"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
User User `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// User 工作相关用户读取条件
|
||||
type User struct {
|
||||
Process string `json:"process"`
|
||||
Args []interface{} `json:"args"`
|
||||
}
|
||||
|
||||
// API 工作相关API
|
||||
type API struct {
|
||||
Name string `json:"name"`
|
||||
Process string `json:"process"`
|
||||
Args []interface{} `json:"args"`
|
||||
}
|
||||
49
workflow/workflow.go
Normal file
49
workflow/workflow.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package workflow
|
||||
|
||||
import "github.com/yaoapp/xiang/config"
|
||||
|
||||
// WorkFlows 工作流列表
|
||||
var WorkFlows = map[string]*WorkFlow{}
|
||||
|
||||
// Load 加载数据表格
|
||||
func Load(cfg config.Config) {
|
||||
LoadFrom(cfg.RootWorkFlow, "")
|
||||
}
|
||||
|
||||
// LoadFrom 从特定目录加载
|
||||
func LoadFrom(dir string, prefix string) {}
|
||||
|
||||
// Select 读取已加载图表
|
||||
func Select(name string) *WorkFlow {
|
||||
return WorkFlows[name]
|
||||
}
|
||||
|
||||
// Process
|
||||
// 读取工作流 xiang.workflow.Get(uid, name, data_id)
|
||||
// 保存工作流 xiang.workflow.Save(uid, name, node, input)
|
||||
// 进入下一个节点 xiang.workflow.Next(uid, id, input)
|
||||
// 跳转到指定节点 xiang.workflow.Goto(uid, id, node, input)
|
||||
|
||||
// API:
|
||||
// 读取工作流 GET /api/xiang/workflow/<工作流名称>/get
|
||||
// 读取工作流配置 GET /api/xiang/workflow/<工作流名称>/setting
|
||||
// 调用自定义API POST /api/xiang/workflow/<工作流名称>/<自定义API路由>
|
||||
|
||||
// Setting 返回配置信息
|
||||
func (workflow *WorkFlow) Setting(id int) {}
|
||||
|
||||
// SetupAPIs 注册API
|
||||
func (workflow *WorkFlow) SetupAPIs(id int) {}
|
||||
|
||||
// Get 读取当前工作流(未完成的)
|
||||
func (workflow *WorkFlow) Get(uid int, name string, dataID interface{}) {}
|
||||
|
||||
// Save 保存工作流节点数据
|
||||
func (workflow *WorkFlow) Save(uid int, name string, node string, dataID interface{}, input map[string]interface{}) {
|
||||
}
|
||||
|
||||
// Next 下一个工作流
|
||||
func (workflow *WorkFlow) Next(uid int, id int, input map[string]interface{}) {}
|
||||
|
||||
// Goto 工作流跳转
|
||||
func (workflow *WorkFlow) Goto(uid int, id int, node string, input map[string]interface{}) {}
|
||||
124
xiang/models/workflow.mod.json
Normal file
124
xiang/models/workflow.mod.json
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
{
|
||||
"name": "工作流",
|
||||
"table": {
|
||||
"name": "xiang_workflow",
|
||||
"comment": "工作流数据表",
|
||||
"engine": "InnoDB"
|
||||
},
|
||||
"columns": [
|
||||
{ "label": "ID", "name": "id", "type": "ID" },
|
||||
{
|
||||
"label": "名称",
|
||||
"comment": "工作流名称",
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"length": 255,
|
||||
"index": true,
|
||||
"validations": [
|
||||
{
|
||||
"method": "typeof",
|
||||
"args": ["string"],
|
||||
"message": "{{input}}类型错误, {{label}}应该为字符串"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "数据标识",
|
||||
"comment": "关联数据标识, 关联数据模型表格ID",
|
||||
"name": "data_id",
|
||||
"type": "bigInteger",
|
||||
"index": true,
|
||||
"nullable": true
|
||||
},
|
||||
{
|
||||
"label": "节点",
|
||||
"comment": "当前节点名称",
|
||||
"name": "node_name",
|
||||
"type": "string",
|
||||
"length": 255,
|
||||
"index": true,
|
||||
"nullable": true
|
||||
},
|
||||
{
|
||||
"label": "节点状态",
|
||||
"comment": "当前节点状态",
|
||||
"name": "node_status",
|
||||
"type": "enum",
|
||||
"default": "未开始",
|
||||
"option": ["未开始", "进行中", "已完成"],
|
||||
"index": true,
|
||||
"validations": [
|
||||
{
|
||||
"method": "typeof",
|
||||
"args": ["string"],
|
||||
"message": "{{input}}类型错误, {{label}}应该为字符串"
|
||||
},
|
||||
{
|
||||
"method": "enum",
|
||||
"args": ["未开始", "进行中", "已完成"],
|
||||
"message": "{{input}}不在许可范围, {{label}}应该为 未开始/进行中/已完成"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "当前处理人",
|
||||
"comment": "当前处理人",
|
||||
"name": "user_id",
|
||||
"type": "bigInteger",
|
||||
"index": true,
|
||||
"nullable": true
|
||||
},
|
||||
{
|
||||
"label": "处理人",
|
||||
"comment": "处理人选取条件",
|
||||
"name": "user",
|
||||
"type": "json",
|
||||
"nullable": true
|
||||
},
|
||||
{
|
||||
"label": "数据",
|
||||
"comment": "各节点输入数据(Key-Value)",
|
||||
"name": "input",
|
||||
"type": "json",
|
||||
"nullable": true
|
||||
},
|
||||
{
|
||||
"label": "处理结果",
|
||||
"comment": "各节点处理结果(Key-Value)",
|
||||
"name": "output",
|
||||
"type": "json",
|
||||
"nullable": true
|
||||
},
|
||||
{
|
||||
"label": "操作记录",
|
||||
"comment": "操作记录",
|
||||
"name": "history",
|
||||
"type": "json",
|
||||
"nullable": true
|
||||
},
|
||||
|
||||
{
|
||||
"label": "状态",
|
||||
"comment": "流程状态",
|
||||
"name": "status",
|
||||
"type": "enum",
|
||||
"default": "未开始",
|
||||
"option": ["未开始", "进行中", "已完成", "已关闭"],
|
||||
"index": true,
|
||||
"validations": [
|
||||
{
|
||||
"method": "typeof",
|
||||
"args": ["string"],
|
||||
"message": "{{input}}类型错误, {{label}}应该为字符串"
|
||||
},
|
||||
{
|
||||
"method": "enum",
|
||||
"args": ["未开始", "进行中", "已完成", "已关闭"],
|
||||
"message": "{{input}}不在许可范围, {{label}}应该为 未开始/进行中/已完成/已关闭"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"indexes": [],
|
||||
"option": { "timestamps": true, "soft_deletes": true }
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue