From 65a8e59689c9232c0cdfd91a3e204950a41c71ce Mon Sep 17 00:00:00 2001 From: jingganjiaoyu Date: Tue, 3 Sep 2024 18:28:19 +0800 Subject: [PATCH] opt some --- Makefile | 4 +-- go.mod | 1 + go.sum | 2 ++ src/dashboard/client.go | 21 ++++++++++++++ src/dashboard/common.go | 41 ++++++++++++++++++++++++++ src/dashboard/model/model.go | 35 ++++++++++++++++++++++ src/dashboard/portal.go | 13 +++++++++ src/dashboard/portal.json | 34 ++++++++++++++++++++++ src/dashboard/summary.go | 25 ++++++++++++++++ src/dashboard/summary.js | 9 ++++++ src/dashboard/userClient.go | 15 ++++++++++ src/development/common.go | 41 ++++++++++++++++++++++++++ src/development/model/model.go | 35 ++++++++++++++++++++++ src/development/portal.go | 13 +++++++++ src/development/portal.json | 30 +++++++++++++++++++ src/development/repos.go | 21 ++++++++++++++ src/development/userRepos.go | 15 ++++++++++ src/development/version.go | 25 ++++++++++++++++ src/development/version.js | 9 ++++++ src/main.go | 5 +++- src/main.jpg | Bin 0 -> 18443 bytes src/operation/cloud.go | 21 ++++++++++++++ src/operation/common.go | 51 +++++++++++++++++++++++++++++++++ src/operation/model/model.go | 35 ++++++++++++++++++++++ src/operation/portal.go | 13 +++++++++ src/operation/portal.json | 30 +++++++++++++++++++ src/operation/release.go | 25 ++++++++++++++++ src/operation/release.js | 9 ++++++ src/operation/userCloud.go | 27 +++++++++++++++++ src/option.go | 5 ++++ src/production/common.go | 41 ++++++++++++++++++++++++++ src/production/issue.go | 39 +++++++++++++++++++++++++ src/production/issue.js | 9 ++++++ src/production/model/model.go | 35 ++++++++++++++++++++++ src/production/portal.go | 13 +++++++++ src/production/portal.json | 35 ++++++++++++++++++++++ src/production/story.go | 23 +++++++++++++++ src/production/userStory.go | 15 ++++++++++ 38 files changed, 817 insertions(+), 3 deletions(-) create mode 100644 src/dashboard/client.go create mode 100644 src/dashboard/common.go create mode 100644 src/dashboard/model/model.go create mode 100644 src/dashboard/portal.go create mode 100644 src/dashboard/portal.json create mode 100644 src/dashboard/summary.go create mode 100644 src/dashboard/summary.js create mode 100644 src/dashboard/userClient.go create mode 100644 src/development/common.go create mode 100644 src/development/model/model.go create mode 100644 src/development/portal.go create mode 100644 src/development/portal.json create mode 100644 src/development/repos.go create mode 100644 src/development/userRepos.go create mode 100644 src/development/version.go create mode 100644 src/development/version.js create mode 100644 src/main.jpg create mode 100644 src/operation/cloud.go create mode 100644 src/operation/common.go create mode 100644 src/operation/model/model.go create mode 100644 src/operation/portal.go create mode 100644 src/operation/portal.json create mode 100644 src/operation/release.go create mode 100644 src/operation/release.js create mode 100644 src/operation/userCloud.go create mode 100644 src/option.go create mode 100644 src/production/common.go create mode 100644 src/production/issue.go create mode 100644 src/production/issue.js create mode 100644 src/production/model/model.go create mode 100644 src/production/portal.go create mode 100644 src/production/portal.json create mode 100644 src/production/story.go create mode 100644 src/production/userStory.go diff --git a/Makefile b/Makefile index 5ed7821..da655ab 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,8 @@ flags = -ldflags "-w -s" -v all: def @date +"%Y-%m-%d %H:%M:%S" - go build ${flags} -o ${binarys} src/main.go src/option.go${version} ${binpack} && ./${binarys} forever restart &>/dev/null + go build ${flags} -o ${binarys} src/main.go src/option.go ${version} ${binpack} && ./${binarys} forever restart &>/dev/null def: @[ -f ${version} ] || echo "package main">${version} - @[ -f ${binpack} ] || echo "package main">${binpack} \ No newline at end of file + @[ -f ${binpack} ] || echo "package main">${binpack} diff --git a/go.mod b/go.mod index 351a837..3d86825 100644 --- a/go.mod +++ b/go.mod @@ -5,4 +5,5 @@ go 1.13 require ( shylinux.com/x/community v0.0.5 // indirect shylinux.com/x/ice v1.5.44 // indirect + shylinux.com/x/mysql-story v0.6.18 // indirect ) diff --git a/go.sum b/go.sum index 2bc0ee5..cc0e5d2 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,7 @@ github.com/glerchundi/subcommands v0.0.0-20181212083838-923a6ccb11f8/go.mod h1:r github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -170,6 +171,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/src/dashboard/client.go b/src/dashboard/client.go new file mode 100644 index 0000000..8816d69 --- /dev/null +++ b/src/dashboard/client.go @@ -0,0 +1,21 @@ +package dashboard + +import "shylinux.com/x/ice" + +type client struct{ Table } + +func init() { ice.TeamCtxCmd(client{}) } + +type ClientType int + +const ( + ClientMySQL ClientType = iota + ClientRedis +) + +var ClientTypeList = map[ClientType]string{ + ClientMySQL: "mysql", + ClientRedis: "redis", +} + +func (s ClientType) String() string { return ClientTypeList[s] } diff --git a/src/dashboard/common.go b/src/dashboard/common.go new file mode 100644 index 0000000..059c312 --- /dev/null +++ b/src/dashboard/common.go @@ -0,0 +1,41 @@ +package dashboard + +import ( + "shylinux.com/x/ice" + kit "shylinux.com/x/toolkits" + + "shylinux.com/x/operation/src/dashboard/model" + "shylinux.com/x/operation/src/operation" +) + +type Table struct { + operation.Table + list string `name:"list client_uid uid auto" role:"void"` +} + +func (s Table) Inputs(m *ice.Message, arg ...string) { + switch arg[0] { + case model.USER_CLIENT_ROLE: + s.InputsListRole(m, UserClientRoleList, arg...) + case model.CLIENT_TYPE: + s.InputsList(m, ClientTypeList, arg...) + default: + s.Table.Inputs(m, arg...) + } +} +func (s Table) RewriteAppend(m *ice.Message, arg ...string) *ice.Message { + m.RewriteAppend(func(value, key string, index int) string { + switch key { + case model.USER_CLIENT_ROLE: + value = UserClientRole(kit.Int(value)).String() + case model.CLIENT_TYPE: + value = ClientType(kit.Int(value)).String() + } + return value + }) + return s.Table.RewriteAppend(m) +} + +type Tables struct{ Table } + +func (s Tables) BeforeMigrate(m *ice.Message, arg ...string) {} diff --git a/src/dashboard/model/model.go b/src/dashboard/model/model.go new file mode 100644 index 0000000..6423ece --- /dev/null +++ b/src/dashboard/model/model.go @@ -0,0 +1,35 @@ +package model + +import "shylinux.com/x/mysql-story/src/db" + +const ( + UID = "uid" + NAME = "name" + TYPE = "type" + ROLE = "role" + TITLE = "title" + CONTENT = "content" + USER_UID = "user_uid" + USER_CLIENT_ROLE = "user_client_role" + CLIENT_UID = "client_uid" + CLIENT_NAME = "client_name" + CLIENT_TYPE = "client_type" + SUMMARY_UID = "summary_uid" + COMPANY_UID = "company_uid" + CITY_UID = "city_uid" +) + +type UserClient struct { + db.ModelUserPlace + ClientUID string `gorm:"type:char(32);index"` +} +type Client struct { + db.ModelPlace + CompanyUID string `gorm:"type:char(32);index"` +} +type Summary struct { + db.ModelContent + ClientUID string `gorm:"type:char(32);index"` +} + +func init() { db.CmdModels("", &UserClient{}, &Client{}, &Summary{}) } diff --git a/src/dashboard/portal.go b/src/dashboard/portal.go new file mode 100644 index 0000000..826178a --- /dev/null +++ b/src/dashboard/portal.go @@ -0,0 +1,13 @@ +package dashboard + +import ( + "shylinux.com/x/community/src/gonganxitong" + "shylinux.com/x/operation/src/operation" +) + +type Portal struct { + operation.Portal + placeCreate string `name:"placeCreate city_name* company_name* client_type*:select client_name*" role:"void"` +} + +func init() { gonganxitong.PortalCmd(Portal{Portal: operation.NewPortal(userClient{}, client{})}) } diff --git a/src/dashboard/portal.json b/src/dashboard/portal.json new file mode 100644 index 0000000..235ddc2 --- /dev/null +++ b/src/dashboard/portal.json @@ -0,0 +1,34 @@ +{ + "portal": "数据分析", + "summary": "数据汇总", + "icons": { + "summary": "https://img.icons8.com/officel/80/activity-grid.png" + }, + "input": { + "My Client": "我的数据", + "user_client_role": "用户角色", + "client_name": "数据名称", + "client_type": "数据类型" + }, + "value": { + "user_client_role": { + "visitor": "访客", + "creator": "创建人", + "leader": "管理人员", + "worker": "工作人员", + "server": "服务人员", + "style": { + "creator": "danger", + "leader": "danger" + } + }, + "client_type": { + "mysql": "MySQL", + "redis": "Redis", + "icons": { + "mysql": "https://2023-contexts.shylinux.com/p/src/studio/studio.png?pod=20230511-mysql-story", + "redis": "https://2023-contexts.shylinux.com/p/src/client/redis.png?pod=20230511-redis-story" + } + } + } +} \ No newline at end of file diff --git a/src/dashboard/summary.go b/src/dashboard/summary.go new file mode 100644 index 0000000..f819831 --- /dev/null +++ b/src/dashboard/summary.go @@ -0,0 +1,25 @@ +package dashboard + +import ( + "shylinux.com/x/ice" + kit "shylinux.com/x/toolkits" + + "shylinux.com/x/operation/src/dashboard/model" +) + +type summary struct { + Table + client client + userClient userClient + create string `name:"create title* content*" role:"leader"` +} + +func (s summary) Create(m *ice.Message, arg ...string) { + s.Table.Create(m, kit.Simple(arg, m.OptionSimple(model.USER_UID, model.CLIENT_UID))...) + s.RecordEventWithName(m, "") +} +func (s summary) List(m *ice.Message, arg ...string) { + s.TablesWithRole(m, arg, s.userClient, s.client, s, model.TITLE, model.CONTENT).Display("") +} + +func init() { ice.TeamCtxCmd(summary{}) } diff --git a/src/dashboard/summary.js b/src/dashboard/summary.js new file mode 100644 index 0000000..b7ff53c --- /dev/null +++ b/src/dashboard/summary.js @@ -0,0 +1,9 @@ +Volcanos(chat.ONIMPORT, { + _init: function(can, msg) { can.onimport.shareTitle(can, msg) + can.onimport.itemcards(can, msg, function(value) { return [ + {view: html.TITLE, list: [value.title]}, + {view: html.STATUS, list: [value.uid.slice(0, 6), can.base.TimeTrim(value.created_at), value.user_name]}, + {view: html.OUTPUT, list: [value.content]}, + ] }) + }, +}) \ No newline at end of file diff --git a/src/dashboard/userClient.go b/src/dashboard/userClient.go new file mode 100644 index 0000000..83aa10f --- /dev/null +++ b/src/dashboard/userClient.go @@ -0,0 +1,15 @@ +package dashboard + +import ( + "shylinux.com/x/ice" + + "shylinux.com/x/operation/src/operation" +) + +type userClient struct{ Table } + +func init() { ice.TeamCtxCmd(userClient{}) } + +type UserClientRole = operation.UserCloudRole + +var UserClientRoleList = operation.UserCloudRoleList diff --git a/src/development/common.go b/src/development/common.go new file mode 100644 index 0000000..bd30285 --- /dev/null +++ b/src/development/common.go @@ -0,0 +1,41 @@ +package development + +import ( + "shylinux.com/x/ice" + kit "shylinux.com/x/toolkits" + + "shylinux.com/x/operation/src/development/model" + "shylinux.com/x/operation/src/operation" +) + +type Table struct { + operation.Table + list string `name:"list repos_uid uid auto" role:"void"` +} + +func (s Table) Inputs(m *ice.Message, arg ...string) { + switch arg[0] { + case model.USER_REPOS_ROLE: + s.InputsListRole(m, UserReposRoleList, arg...) + case model.REPOS_TYPE: + s.InputsList(m, ReposTypeList, arg...) + default: + s.Table.Inputs(m, arg...) + } +} +func (s Table) RewriteAppend(m *ice.Message, arg ...string) *ice.Message { + m.RewriteAppend(func(value, key string, index int) string { + switch key { + case model.USER_REPOS_ROLE: + value = UserReposRole(kit.Int(value)).String() + case model.REPOS_TYPE: + value = ReposType(kit.Int(value)).String() + } + return value + }) + return s.Table.RewriteAppend(m) +} + +type Tables struct{ Table } + +func (s Tables) BeforeMigrate(m *ice.Message, arg ...string) {} diff --git a/src/development/model/model.go b/src/development/model/model.go new file mode 100644 index 0000000..1b9e884 --- /dev/null +++ b/src/development/model/model.go @@ -0,0 +1,35 @@ +package model + +import "shylinux.com/x/mysql-story/src/db" + +const ( + UID = "uid" + NAME = "name" + TYPE = "type" + ROLE = "role" + TITLE = "title" + CONTENT = "content" + USER_UID = "user_uid" + USER_REPOS_ROLE = "user_repos_role" + REPOS_UID = "repos_uid" + REPOS_NAME = "repos_name" + REPOS_TYPE = "repos_type" + VERSION_UID = "version_uid" + COMPANY_UID = "company_uid" + CITY_UID = "city_uid" +) + +type UserRepos struct { + db.ModelUserPlace + ReposUID string `gorm:"type:char(32);index"` +} +type Repos struct { + db.ModelPlace + CompanyUID string `gorm:"type:char(32);index"` +} +type Version struct { + db.ModelContent + ReposUID string `gorm:"type:char(32);index"` +} + +func init() { db.CmdModels("", &UserRepos{}, &Repos{}, &Version{}) } diff --git a/src/development/portal.go b/src/development/portal.go new file mode 100644 index 0000000..7ecd0a1 --- /dev/null +++ b/src/development/portal.go @@ -0,0 +1,13 @@ +package development + +import ( + "shylinux.com/x/community/src/gonganxitong" + "shylinux.com/x/operation/src/operation" +) + +type Portal struct { + operation.Portal + placeCreate string `name:"placeCreate city_name* company_name* repos_type*:select repos_name*" role:"void"` +} + +func init() { gonganxitong.PortalCmd(Portal{Portal: operation.NewPortal(userRepos{}, repos{})}) } diff --git a/src/development/portal.json b/src/development/portal.json new file mode 100644 index 0000000..83af8fd --- /dev/null +++ b/src/development/portal.json @@ -0,0 +1,30 @@ +{ + "portal": "软件开发", + "version": "版本", + "icons": { + "version": "https://img.icons8.com/officel/80/activity-grid.png" + }, + "input": { + "My Repos": "我的源码", + "user_repos_role": "用户角色", + "repos_name": "源码名称", + "repos_type": "源码类型" + }, + "value": { + "user_repos_role": { + "visitor": "访客", + "creator": "创建人", + "leader": "管理人员", + "worker": "工作人员", + "server": "服务人员", + "style": { + "creator": "danger", + "leader": "danger" + } + }, + "repos_type": { + "local": "本地库", + "remote": "远程库" + } + } +} \ No newline at end of file diff --git a/src/development/repos.go b/src/development/repos.go new file mode 100644 index 0000000..044cb30 --- /dev/null +++ b/src/development/repos.go @@ -0,0 +1,21 @@ +package development + +import "shylinux.com/x/ice" + +type repos struct{ Table } + +func init() { ice.TeamCtxCmd(repos{}) } + +type ReposType int + +const ( + ReposLocal ReposType = iota + ReposRemote +) + +var ReposTypeList = map[ReposType]string{ + ReposLocal: "local", + ReposRemote: "remote", +} + +func (s ReposType) String() string { return ReposTypeList[s] } diff --git a/src/development/userRepos.go b/src/development/userRepos.go new file mode 100644 index 0000000..feea3ec --- /dev/null +++ b/src/development/userRepos.go @@ -0,0 +1,15 @@ +package development + +import ( + "shylinux.com/x/ice" + + "shylinux.com/x/operation/src/operation" +) + +type userRepos struct{ Table } + +func init() { ice.TeamCtxCmd(userRepos{}) } + +type UserReposRole = operation.UserCloudRole + +var UserReposRoleList = operation.UserCloudRoleList diff --git a/src/development/version.go b/src/development/version.go new file mode 100644 index 0000000..955f815 --- /dev/null +++ b/src/development/version.go @@ -0,0 +1,25 @@ +package development + +import ( + "shylinux.com/x/ice" + kit "shylinux.com/x/toolkits" + + "shylinux.com/x/operation/src/development/model" +) + +type version struct { + Table + repos repos + userRepos userRepos + create string `name:"create title* content*" role:"leader"` +} + +func (s version) Create(m *ice.Message, arg ...string) { + s.Table.Create(m, kit.Simple(arg, m.OptionSimple(model.USER_UID, model.REPOS_UID))...) + s.RecordEventWithName(m, "") +} +func (s version) List(m *ice.Message, arg ...string) { + s.TablesWithRole(m, arg, s.userRepos, s.repos, s, model.TITLE, model.CONTENT).Display("") +} + +func init() { ice.TeamCtxCmd(version{}) } diff --git a/src/development/version.js b/src/development/version.js new file mode 100644 index 0000000..b7ff53c --- /dev/null +++ b/src/development/version.js @@ -0,0 +1,9 @@ +Volcanos(chat.ONIMPORT, { + _init: function(can, msg) { can.onimport.shareTitle(can, msg) + can.onimport.itemcards(can, msg, function(value) { return [ + {view: html.TITLE, list: [value.title]}, + {view: html.STATUS, list: [value.uid.slice(0, 6), can.base.TimeTrim(value.created_at), value.user_name]}, + {view: html.OUTPUT, list: [value.content]}, + ] }) + }, +}) \ No newline at end of file diff --git a/src/main.go b/src/main.go index f19cfe4..c892629 100644 --- a/src/main.go +++ b/src/main.go @@ -3,7 +3,10 @@ package main import ( "shylinux.com/x/ice" - _ "shylinux.com/x/community/src/gonganxitong" + _ "shylinux.com/x/operation/src/dashboard" + _ "shylinux.com/x/operation/src/development" + _ "shylinux.com/x/operation/src/operation" + _ "shylinux.com/x/operation/src/production" ) func main() { print(ice.Run()) } diff --git a/src/main.jpg b/src/main.jpg new file mode 100644 index 0000000000000000000000000000000000000000..63fad235e04042d6c83320c794119034e0a32f4f GIT binary patch literal 18443 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 DI-9;T literal 0 HcmV?d00001 diff --git a/src/operation/cloud.go b/src/operation/cloud.go new file mode 100644 index 0000000..e1f69f1 --- /dev/null +++ b/src/operation/cloud.go @@ -0,0 +1,21 @@ +package operation + +import "shylinux.com/x/ice" + +type cloud struct{ Table } + +func init() { ice.TeamCtxCmd(cloud{}) } + +type CloudType int + +const ( + CloudAliyun CloudType = iota + CloudTencent +) + +var CloudTypeList = map[CloudType]string{ + CloudAliyun: "aliyun", + CloudTencent: "tencent", +} + +func (s CloudType) String() string { return CloudTypeList[s] } diff --git a/src/operation/common.go b/src/operation/common.go new file mode 100644 index 0000000..e0254f1 --- /dev/null +++ b/src/operation/common.go @@ -0,0 +1,51 @@ +package operation + +import ( + "shylinux.com/x/ice" + kit "shylinux.com/x/toolkits" + + "shylinux.com/x/community/src/gonganxitong" + "shylinux.com/x/enterprise/src/guanlixitong" + "shylinux.com/x/operation/src/operation/model" +) + +type Table struct { + guanlixitong.Table + list string `name:"list cloud_uid uid auto" role:"void"` +} + +func (s Table) Inputs(m *ice.Message, arg ...string) { + switch arg[0] { + case model.USER_CLOUD_ROLE: + s.InputsListRole(m, UserCloudRoleList, arg...) + case model.CLOUD_TYPE: + s.InputsList(m, CloudTypeList, arg...) + default: + s.Table.Inputs(m, arg...) + } +} +func (s Table) RewriteAppend(m *ice.Message, arg ...string) *ice.Message { + m.RewriteAppend(func(value, key string, index int) string { + switch key { + case model.USER_CLOUD_ROLE: + value = UserCloudRole(kit.Int(value)).String() + case model.CLOUD_TYPE: + value = CloudType(kit.Int(value)).String() + } + return value + }) + return s.Table.RewriteAppend(m) +} +func (s Table) CheckRole(m *ice.Message, arg ...string) *ice.Message { + role := UserCloudRole(s.UserPlaceRole(m)) + m.WarnNotRight(!kit.IsIn(role.String(), append(arg, UserCloudCreator.String())...), role.String()) + return m +} + +type Tables struct{ Table } + +func (s Tables) BeforeMigrate(m *ice.Message, arg ...string) {} + +func NewPortal(userPlace gonganxitong.UserPlacer, place gonganxitong.Placer) Portal { + return Portal{Portal: guanlixitong.NewPortal(userPlace, place)} +} diff --git a/src/operation/model/model.go b/src/operation/model/model.go new file mode 100644 index 0000000..3f16f07 --- /dev/null +++ b/src/operation/model/model.go @@ -0,0 +1,35 @@ +package model + +import "shylinux.com/x/mysql-story/src/db" + +const ( + UID = "uid" + NAME = "name" + TYPE = "type" + ROLE = "role" + TITLE = "title" + CONTENT = "content" + USER_UID = "user_uid" + USER_CLOUD_ROLE = "user_cloud_role" + CLOUD_UID = "cloud_uid" + CLOUD_NAME = "cloud_name" + CLOUD_TYPE = "cloud_type" + RELEASE_UID = "release_uid" + COMPANY_UID = "company_uid" + CITY_UID = "city_uid" +) + +type UserCloud struct { + db.ModelUserPlace + CloudUID string `gorm:"type:char(32);index"` +} +type Cloud struct { + db.ModelPlace + CompanyUID string `gorm:"type:char(32);index"` +} +type Release struct { + db.ModelContent + CloudUID string `gorm:"type:char(32);index"` +} + +func init() { db.CmdModels("", &UserCloud{}, &Cloud{}, &Release{}) } diff --git a/src/operation/portal.go b/src/operation/portal.go new file mode 100644 index 0000000..e229601 --- /dev/null +++ b/src/operation/portal.go @@ -0,0 +1,13 @@ +package operation + +import ( + "shylinux.com/x/community/src/gonganxitong" + "shylinux.com/x/enterprise/src/guanlixitong" +) + +type Portal struct { + guanlixitong.Portal + placeCreate string `name:"placeCreate city_name* company_name* cloud_type*:select cloud_name*" role:"void"` +} + +func init() { gonganxitong.PortalCmd(Portal{Portal: guanlixitong.NewPortal(userCloud{}, cloud{})}) } diff --git a/src/operation/portal.json b/src/operation/portal.json new file mode 100644 index 0000000..9d8b774 --- /dev/null +++ b/src/operation/portal.json @@ -0,0 +1,30 @@ +{ + "portal": "系统运维", + "release": "发布", + "icons": { + "release": "https://img.icons8.com/officel/80/activity-grid.png" + }, + "input": { + "My Cloud": "我的系统", + "user_cloud_role": "用户角色", + "cloud_name": "系统名称", + "cloud_type": "系统类型" + }, + "value": { + "user_cloud_role": { + "visitor": "访客", + "creator": "创建人", + "leader": "管理人员", + "worker": "工作人员", + "server": "服务人员", + "style": { + "creator": "danger", + "leader": "danger" + } + }, + "cloud_type": { + "aliyun": "阿里云", + "tencent": "腾讯云" + } + } +} \ No newline at end of file diff --git a/src/operation/release.go b/src/operation/release.go new file mode 100644 index 0000000..4633a35 --- /dev/null +++ b/src/operation/release.go @@ -0,0 +1,25 @@ +package operation + +import ( + "shylinux.com/x/ice" + kit "shylinux.com/x/toolkits" + + "shylinux.com/x/operation/src/operation/model" +) + +type release struct { + Table + cloud cloud + userCloud userCloud + create string `name:"create title* content*" role:"leader"` +} + +func (s release) Create(m *ice.Message, arg ...string) { + s.Table.Create(m, kit.Simple(arg, m.OptionSimple(model.USER_UID, model.CLOUD_UID))...) + s.RecordEventWithName(m, "") +} +func (s release) List(m *ice.Message, arg ...string) { + s.TablesWithRole(m, arg, s.userCloud, s.cloud, s, model.TITLE, model.CONTENT).Display("") +} + +func init() { ice.TeamCtxCmd(release{}) } diff --git a/src/operation/release.js b/src/operation/release.js new file mode 100644 index 0000000..b7ff53c --- /dev/null +++ b/src/operation/release.js @@ -0,0 +1,9 @@ +Volcanos(chat.ONIMPORT, { + _init: function(can, msg) { can.onimport.shareTitle(can, msg) + can.onimport.itemcards(can, msg, function(value) { return [ + {view: html.TITLE, list: [value.title]}, + {view: html.STATUS, list: [value.uid.slice(0, 6), can.base.TimeTrim(value.created_at), value.user_name]}, + {view: html.OUTPUT, list: [value.content]}, + ] }) + }, +}) \ No newline at end of file diff --git a/src/operation/userCloud.go b/src/operation/userCloud.go new file mode 100644 index 0000000..4aaf27a --- /dev/null +++ b/src/operation/userCloud.go @@ -0,0 +1,27 @@ +package operation + +import "shylinux.com/x/ice" + +type userCloud struct{ Table } + +func init() { ice.TeamCtxCmd(userCloud{}) } + +type UserCloudRole int + +const ( + UserCloudVisitor UserCloudRole = iota + UserCloudCreator + UserCloudLeader + UserCloudWorker + UserCloudServer +) + +var UserCloudRoleList = map[UserCloudRole]string{ + UserCloudVisitor: "visitor", + UserCloudCreator: "creator", + UserCloudLeader: "leader", + UserCloudWorker: "worker", + UserCloudServer: "server", +} + +func (s UserCloudRole) String() string { return UserCloudRoleList[s] } diff --git a/src/option.go b/src/option.go new file mode 100644 index 0000000..4a3afff --- /dev/null +++ b/src/option.go @@ -0,0 +1,5 @@ +package main + +import ( + _ "shylinux.com/x/mysql-story/src/db/mysql" +) diff --git a/src/production/common.go b/src/production/common.go new file mode 100644 index 0000000..597dfcf --- /dev/null +++ b/src/production/common.go @@ -0,0 +1,41 @@ +package production + +import ( + "shylinux.com/x/ice" + kit "shylinux.com/x/toolkits" + + "shylinux.com/x/operation/src/operation" + "shylinux.com/x/operation/src/production/model" +) + +type Table struct { + operation.Table + list string `name:"list story_uid uid auto" role:"void"` +} + +func (s Table) Inputs(m *ice.Message, arg ...string) { + switch arg[0] { + case model.USER_STORY_ROLE: + s.InputsListRole(m, UserStoryRoleList, arg...) + case model.STORY_TYPE: + s.InputsList(m, StoryTypeList, arg...) + default: + s.Table.Inputs(m, arg...) + } +} +func (s Table) RewriteAppend(m *ice.Message, arg ...string) *ice.Message { + m.RewriteAppend(func(value, key string, index int) string { + switch key { + case model.USER_STORY_ROLE: + value = UserStoryRole(kit.Int(value)).String() + case model.STORY_TYPE: + value = StoryType(kit.Int(value)).String() + } + return value + }) + return s.Table.RewriteAppend(m) +} + +type Tables struct{ Table } + +func (s Tables) BeforeMigrate(m *ice.Message, arg ...string) {} diff --git a/src/production/issue.go b/src/production/issue.go new file mode 100644 index 0000000..4fcbf5a --- /dev/null +++ b/src/production/issue.go @@ -0,0 +1,39 @@ +package production + +import ( + "shylinux.com/x/ice" + kit "shylinux.com/x/toolkits" + + "shylinux.com/x/operation/src/production/model" +) + +type issue struct { + Table + story story + userStory userStory + create string `name:"create title* content*" role:"leader"` +} + +func (s issue) Create(m *ice.Message, arg ...string) { + s.Table.Create(m, kit.Simple(arg, m.OptionSimple(model.USER_UID, model.STORY_UID))...) + s.RecordEventWithName(m, "") +} +func (s issue) List(m *ice.Message, arg ...string) { + s.TablesWithRole(m, arg, s.userStory, s.story, s, model.TITLE, model.CONTENT).Display("") +} + +func init() { ice.TeamCtxCmd(issue{}) } + +type IssueType int + +const ( + IssueFeature IssueType = iota + IssueBugfix +) + +var IssueTypeList = map[IssueType]string{ + IssueFeature: "feature", + IssueBugfix: "bugfix", +} + +func (s IssueType) String() string { return IssueTypeList[s] } diff --git a/src/production/issue.js b/src/production/issue.js new file mode 100644 index 0000000..b7ff53c --- /dev/null +++ b/src/production/issue.js @@ -0,0 +1,9 @@ +Volcanos(chat.ONIMPORT, { + _init: function(can, msg) { can.onimport.shareTitle(can, msg) + can.onimport.itemcards(can, msg, function(value) { return [ + {view: html.TITLE, list: [value.title]}, + {view: html.STATUS, list: [value.uid.slice(0, 6), can.base.TimeTrim(value.created_at), value.user_name]}, + {view: html.OUTPUT, list: [value.content]}, + ] }) + }, +}) \ No newline at end of file diff --git a/src/production/model/model.go b/src/production/model/model.go new file mode 100644 index 0000000..fe31cfa --- /dev/null +++ b/src/production/model/model.go @@ -0,0 +1,35 @@ +package model + +import "shylinux.com/x/mysql-story/src/db" + +const ( + UID = "uid" + NAME = "name" + TYPE = "type" + ROLE = "role" + TITLE = "title" + CONTENT = "content" + USER_UID = "user_uid" + USER_STORY_ROLE = "user_story_role" + STORY_UID = "story_uid" + STORY_NAME = "story_name" + STORY_TYPE = "story_type" + ISSUE_UID = "issue_uid" + COMPANY_UID = "company_uid" + CITY_UID = "city_uid" +) + +type UserStory struct { + db.ModelUserPlace + StoryUID string `gorm:"type:char(32);index"` +} +type Story struct { + db.ModelPlace + CompanyUID string `gorm:"type:char(32);index"` +} +type Issue struct { + db.ModelContent + StoryUID string `gorm:"type:char(32);index"` +} + +func init() { db.CmdModels("", &UserStory{}, &Story{}, &Issue{}) } diff --git a/src/production/portal.go b/src/production/portal.go new file mode 100644 index 0000000..fad7069 --- /dev/null +++ b/src/production/portal.go @@ -0,0 +1,13 @@ +package production + +import ( + "shylinux.com/x/community/src/gonganxitong" + "shylinux.com/x/enterprise/src/guanlixitong" +) + +type Portal struct { + guanlixitong.Portal + placeCreate string `name:"placeCreate city_name* company_name* story_type*:select story_name*" role:"void"` +} + +func init() { gonganxitong.PortalCmd(Portal{Portal: guanlixitong.NewPortal(userStory{}, story{})}) } diff --git a/src/production/portal.json b/src/production/portal.json new file mode 100644 index 0000000..cf37ab7 --- /dev/null +++ b/src/production/portal.json @@ -0,0 +1,35 @@ +{ + "portal": "产品迭代", + "issue": "场景应用", + "icons": { + "issue": "https://img.icons8.com/officel/80/activity-grid.png" + }, + "input": { + "My Story": "我的场景", + "user_story_role": "用户角色", + "story_name": "场景名称", + "story_type": "场景类型" + }, + "value": { + "user_story_role": { + "visitor": "访客", + "creator": "创建人", + "leader": "管理人员", + "worker": "工作人员", + "server": "服务人员", + "style": { + "creator": "danger", + "leader": "danger" + } + }, + "story_type": { + "RD": "研发群", + "OP": "运维群", + "HR": "人力群" + }, + "issue_type": { + "feature": "新功能", + "bugfix": "问题修复" + } + } +} \ No newline at end of file diff --git a/src/production/story.go b/src/production/story.go new file mode 100644 index 0000000..47cee23 --- /dev/null +++ b/src/production/story.go @@ -0,0 +1,23 @@ +package production + +import "shylinux.com/x/ice" + +type story struct{ Table } + +func init() { ice.TeamCtxCmd(story{}) } + +type StoryType int + +const ( + StoryRD StoryType = iota + StoryOP + StoryHR +) + +var StoryTypeList = map[StoryType]string{ + StoryRD: "RD", + StoryOP: "OP", + StoryHR: "HR", +} + +func (s StoryType) String() string { return StoryTypeList[s] } diff --git a/src/production/userStory.go b/src/production/userStory.go new file mode 100644 index 0000000..c084ec9 --- /dev/null +++ b/src/production/userStory.go @@ -0,0 +1,15 @@ +package production + +import ( + "shylinux.com/x/ice" + + "shylinux.com/x/operation/src/operation" +) + +type userStory struct{ Table } + +func init() { ice.TeamCtxCmd(userStory{}) } + +type UserStoryRole = operation.UserCloudRole + +var UserStoryRoleList = operation.UserCloudRoleList