正在显示
75 个修改的文件
包含
3948 行增加
和
0 行删除
.gitignore
0 → 100644
1 | +# Compiled Object codefiles, Static and Dynamic libs (Shared Objects) | ||
2 | +*.o | ||
3 | +*.a | ||
4 | +*.so | ||
5 | + | ||
6 | +# Folders | ||
7 | +_obj | ||
8 | +_test | ||
9 | + | ||
10 | +# Architecture specific extensions/prefixes | ||
11 | +*.[568vq] | ||
12 | +[568vq].out | ||
13 | + | ||
14 | +*.cgo1.go | ||
15 | +*.cgo2.c | ||
16 | +_cgo_defun.c | ||
17 | +_cgo_gotypes.go | ||
18 | +_cgo_export.* | ||
19 | + | ||
20 | +_testmain.go | ||
21 | + | ||
22 | +*.exe | ||
23 | +*.test | ||
24 | +.log | ||
25 | +.idea | ||
26 | +.vscode | ||
27 | +.gitkeep | ||
28 | + | ||
29 | +app.log | ||
30 | +go.sum | ||
31 | +lastupdate.tmp | ||
32 | +*.log | ||
33 | + | ||
34 | +public/* | ||
35 | +logs/ | ||
36 | +cmd/discuss/api/etc/core.local.yaml |
Makefile
0 → 100644
1 | +.PHONY: bsi-model | ||
2 | +bsi-model: | ||
3 | + goctl model mysql ddl -s .\cmd\bsi\deploy\database\table.sql -d cmd/bsi | ||
4 | + | ||
5 | +.PHONY: bsi-api | ||
6 | +bsi-api: | ||
7 | + goctl api go -api .\cmd\bsi\api\dsl\core.api -dir cmd/bsi/api -style go_zero | ||
8 | + | ||
9 | +.PHONY: bsi-swagger | ||
10 | +bsi-swagger: | ||
11 | + goctl api plugin -plugin goctl-swagger="swagger -filename core.json" -api .\cmd\bsi\api\dsl\core.api -dir .\cmd\discuss\api\dsl | ||
12 | + | ||
13 | +.PHONY: bsi-build | ||
14 | +bsi-build: | ||
15 | + docker build -f cmd/bsi/deploy/docker/Dockerfile -t sumifcc/bsi:1.0.0 . |
cmd/bsi/api/core.go
0 → 100644
1 | +package main | ||
2 | + | ||
3 | +import ( | ||
4 | + "flag" | ||
5 | + "fmt" | ||
6 | + "github.com/zeromicro/go-zero/core/logx" | ||
7 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/interanl/pkg/db" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/interanl/pkg/domain" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/xerr" | ||
11 | + "net/http" | ||
12 | + "strings" | ||
13 | + | ||
14 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/config" | ||
15 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/handler" | ||
16 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
17 | + | ||
18 | + "github.com/golang-jwt/jwt/v4/request" | ||
19 | + "github.com/zeromicro/go-zero/core/conf" | ||
20 | + "github.com/zeromicro/go-zero/rest" | ||
21 | +) | ||
22 | + | ||
23 | +var configFile = flag.String("f", "etc/core.yaml", "the config file") | ||
24 | + | ||
25 | +func main() { | ||
26 | + flag.Parse() | ||
27 | + | ||
28 | + var c config.Config | ||
29 | + conf.MustLoad(*configFile, &c) | ||
30 | + | ||
31 | + // 系统设置 | ||
32 | + systemSetup(c) | ||
33 | + | ||
34 | + // 服务初始化 | ||
35 | + opts := make([]rest.RunOption, 0) | ||
36 | + opts = append(opts, rest.WithCustomCors(func(header http.Header) { | ||
37 | + header.Set("Access-Control-Allow-Headers", "*") | ||
38 | + }, func(writer http.ResponseWriter) { | ||
39 | + | ||
40 | + })) | ||
41 | + opts = append(opts, rest.WithUnauthorizedCallback(func(w http.ResponseWriter, r *http.Request, err error) { | ||
42 | + if err != nil { | ||
43 | + logx.Debugf("unauthorized: %s \n", err.Error()) | ||
44 | + } | ||
45 | + })) | ||
46 | + | ||
47 | + server := rest.MustNewServer(c.RestConf, opts...) | ||
48 | + defer server.Stop() | ||
49 | + | ||
50 | + ctx := svc.NewServiceContext(c) | ||
51 | + handler.RegisterHandlers(server, ctx) | ||
52 | + | ||
53 | + // 数据迁移 | ||
54 | + if c.Migrate { | ||
55 | + db.Migrate(ctx.DB) | ||
56 | + } | ||
57 | + | ||
58 | + fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) | ||
59 | + server.Start() | ||
60 | +} | ||
61 | + | ||
62 | +func systemSetup(c config.Config) { | ||
63 | + // 初始化Domain里面的配置 | ||
64 | + domain.ProjectName = c.Name | ||
65 | + | ||
66 | + // 默认的token头 Authorization 修改为 x-mmm-accesstoken | ||
67 | + request.AuthorizationHeaderExtractor = &request.PostExtractionFilter{ | ||
68 | + Extractor: request.HeaderExtractor{"x-mmm-accesstoken"}, | ||
69 | + Filter: func(tok string) (string, error) { | ||
70 | + // Should be a bearer token | ||
71 | + if len(tok) > 6 && strings.ToUpper(tok[0:7]) == "BEARER " { | ||
72 | + return tok[7:], nil | ||
73 | + } | ||
74 | + return tok, nil | ||
75 | + }, | ||
76 | + } | ||
77 | + | ||
78 | + // 系统错误应答包装 | ||
79 | + httpx.SetErrorHandlerCtx(xerr.ErrorHandlerCtx) | ||
80 | + | ||
81 | + // 系统成功应答包装 | ||
82 | + httpx.SetOkHandler(xerr.OkHandlerCtx) | ||
83 | +} |
cmd/bsi/api/dsl/core.api
0 → 100644
cmd/bsi/api/dsl/core/common.api
0 → 100644
1 | + // 通用接口 | ||
2 | + @server( | ||
3 | + prefix: v1 | ||
4 | + group: common | ||
5 | + ) | ||
6 | + service Core { | ||
7 | + @doc "日志查询" | ||
8 | + @handler commonGetLog | ||
9 | + get /log/:module | ||
10 | + } | ||
11 | + | ||
12 | + // 通用接口 | ||
13 | + @server( | ||
14 | + prefix: v1 | ||
15 | + middleware: LogRequest | ||
16 | + group: common | ||
17 | + ) | ||
18 | + service Core { | ||
19 | + @doc "短信验证码" | ||
20 | + @handler commonSmsCode | ||
21 | + post /common/sms/code (CommonSmsCodeRequest) returns (CommonSmsCodeResposne) | ||
22 | + | ||
23 | + @doc "微信二维码" | ||
24 | + @handler miniQrcodeInvite | ||
25 | + post /mini/qrcode (MiniQrCodeRequest) | ||
26 | + | ||
27 | + @doc "清理缓存" | ||
28 | + @handler commonGetClearCache | ||
29 | + get /clear | ||
30 | + } | ||
31 | + | ||
32 | + // 短信验证码 | ||
33 | + type( | ||
34 | + CommonSmsCodeRequest{ | ||
35 | + Phone string `json:"phone"` | ||
36 | + } | ||
37 | + CommonSmsCodeResposne{ | ||
38 | + | ||
39 | + } | ||
40 | + ) | ||
41 | + | ||
42 | + type( | ||
43 | + MiniQrCodeRequest{ | ||
44 | + Page string `json:"page"` // 微信页面入口 | ||
45 | + Scene string `json:"scene"` // 参数 | ||
46 | + } | ||
47 | + ) |
cmd/bsi/api/dsl/core/screen.api
0 → 100644
1 | +syntax = "v1" | ||
2 | + | ||
3 | +info( | ||
4 | + title: "易数家显示大屏互动服务" | ||
5 | + desc: "易数家显示大屏互动服务 BSI(Big screen interaction)" | ||
6 | + author: "bsi" | ||
7 | + email: "bsi@gmail.com" | ||
8 | + version: "v1" | ||
9 | +) | ||
10 | + | ||
11 | +// H5接口 | ||
12 | +@server( | ||
13 | + prefix: v1/h5 | ||
14 | + group: todo | ||
15 | + middleware: LogRequest | ||
16 | + //jwt: MiniAuth | ||
17 | +) | ||
18 | +service Core { | ||
19 | + @doc "" | ||
20 | + @handler H5Todo | ||
21 | + get /todo | ||
22 | + @doc "" | ||
23 | + @handler H5TodoErrorCommon | ||
24 | + get /todo/err1 returns(TodoResonse) | ||
25 | + @doc "" | ||
26 | + @handler H5TodoErrorInternal | ||
27 | + get /todo/err2 returns(TodoResonse) | ||
28 | + @doc "" | ||
29 | + @handler H5TodoErrorGrpc | ||
30 | + get /todo/err3 returns(TodoResonse) | ||
31 | +} | ||
32 | + | ||
33 | +type ( | ||
34 | + TodoRequest struct{} | ||
35 | + TodoResonse struct{} | ||
36 | +) |
cmd/bsi/api/etc/core.yaml
0 → 100644
1 | +Name: Core | ||
2 | +Host: 0.0.0.0 | ||
3 | +Port: 8080 | ||
4 | + | ||
5 | +Verbose: false | ||
6 | +Migrate: true | ||
7 | +Timeout: 30000 | ||
8 | +LogRequest: true # 记录详细请求日志 | ||
9 | +ContentSecurityCheck: true # 内容安全检查(调用微信接口) | ||
10 | + | ||
11 | +Log: | ||
12 | + #Mode: file | ||
13 | + Encoding: plain | ||
14 | + Level: debug # info | ||
15 | + MaxSize: 1 # 2MB | ||
16 | + TimeFormat: 2006-01-02 15:04:05 | ||
17 | + Rotation: size | ||
18 | + MaxContentLength: 10240 | ||
19 | + | ||
20 | +SystemAuth: | ||
21 | + AccessSecret: digital-platform | ||
22 | + AccessExpire: 360000 | ||
23 | + | ||
24 | +MiniAuth: | ||
25 | + AccessSecret: discuss-secret | ||
26 | + AccessExpire: 360000 | ||
27 | + | ||
28 | +Redis: | ||
29 | + Host: 127.0.0.1:6379 | ||
30 | + Type: node | ||
31 | + Pass: | ||
32 | +DB: | ||
33 | + DataSource: host=114.55.200.59 user=postgres password=eagle1010 dbname=sumifcc-discuss-dev port=31543 sslmode=disable TimeZone=Asia/Shanghai | ||
34 | + | ||
35 | +ApiAuth: | ||
36 | + Name: ApiAuth | ||
37 | + Host: http://digital-platform-dev.fjmaimaimai.com | ||
38 | + Timeout: 0s | ||
39 | + | ||
40 | +Wechat: | ||
41 | + AppID: wxae5b305849343ec8 | ||
42 | + AppSecret: f584adb68f7d784425b60e1ebb2ffd4b | ||
43 | + QrcodeEnv: trial |
cmd/bsi/api/internal/config/config.go
0 → 100644
1 | +package config | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/zeromicro/go-zero/core/stores/redis" | ||
5 | + "github.com/zeromicro/go-zero/rest" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/config" | ||
7 | + "time" | ||
8 | +) | ||
9 | + | ||
10 | +type Config struct { | ||
11 | + rest.RestConf | ||
12 | + config.Config | ||
13 | + Redis redis.RedisConf `json:",optional"` | ||
14 | + SystemAuth config.Auth | ||
15 | + MiniAuth config.Auth | ||
16 | + Migrate bool `json:",optional,default=true"` | ||
17 | + ApiAuth ApiService | ||
18 | + DebugSmsCode string `json:",optional,default=999512"` | ||
19 | + LogRequest bool `json:",optional,default=true"` | ||
20 | + ContentSecurityCheck bool `json:",optional,default=false"` | ||
21 | +} | ||
22 | + | ||
23 | +type ApiService struct { | ||
24 | + Name string | ||
25 | + Host string | ||
26 | + Timeout time.Duration | ||
27 | +} |
1 | +package common | ||
2 | + | ||
3 | +import ( | ||
4 | + "net/http" | ||
5 | + | ||
6 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/common" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | +) | ||
10 | + | ||
11 | +func CommonGetClearCacheHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
12 | + return func(w http.ResponseWriter, r *http.Request) { | ||
13 | + l := common.NewCommonGetClearCacheLogic(r.Context(), svcCtx) | ||
14 | + err := l.CommonGetClearCache() | ||
15 | + if err != nil { | ||
16 | + httpx.ErrorCtx(r.Context(), w, err) | ||
17 | + } else { | ||
18 | + httpx.Ok(w) | ||
19 | + } | ||
20 | + } | ||
21 | +} |
1 | +package common | ||
2 | + | ||
3 | +import ( | ||
4 | + "net/http" | ||
5 | + "path/filepath" | ||
6 | + "strings" | ||
7 | + | ||
8 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
10 | +) | ||
11 | + | ||
12 | +func CommonGetLogHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
13 | + return func(w http.ResponseWriter, r *http.Request) { | ||
14 | + var req struct { | ||
15 | + Module string `path:"module"` | ||
16 | + } | ||
17 | + if err := httpx.Parse(r, &req); err != nil { | ||
18 | + httpx.ErrorCtx(r.Context(), w, err) | ||
19 | + return | ||
20 | + } | ||
21 | + path := svcCtx.Config.Log.Path | ||
22 | + if svcCtx.Config.Log.Mode != "file" { | ||
23 | + return | ||
24 | + } | ||
25 | + if path == "" { | ||
26 | + path = "logs" | ||
27 | + } | ||
28 | + if !strings.HasSuffix(req.Module, ".log") { | ||
29 | + req.Module += ".log" | ||
30 | + } | ||
31 | + handler := http.FileServer(http.Dir(path)) | ||
32 | + r.URL.Path = filepath.Join(req.Module) | ||
33 | + handler.ServeHTTP(w, r) | ||
34 | + } | ||
35 | +} |
1 | +package common | ||
2 | + | ||
3 | +import ( | ||
4 | + "net/http" | ||
5 | + | ||
6 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/common" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types" | ||
10 | +) | ||
11 | + | ||
12 | +func CommonSmsCodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
13 | + return func(w http.ResponseWriter, r *http.Request) { | ||
14 | + var req types.CommonSmsCodeRequest | ||
15 | + if err := httpx.Parse(r, &req); err != nil { | ||
16 | + httpx.ErrorCtx(r.Context(), w, err) | ||
17 | + return | ||
18 | + } | ||
19 | + | ||
20 | + l := common.NewCommonSmsCodeLogic(r.Context(), svcCtx) | ||
21 | + resp, err := l.CommonSmsCode(&req) | ||
22 | + if err != nil { | ||
23 | + httpx.ErrorCtx(r.Context(), w, err) | ||
24 | + } else { | ||
25 | + httpx.OkJsonCtx(r.Context(), w, resp) | ||
26 | + } | ||
27 | + } | ||
28 | +} |
1 | +package common | ||
2 | + | ||
3 | +import ( | ||
4 | + "net/http" | ||
5 | + | ||
6 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/common" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types" | ||
10 | +) | ||
11 | + | ||
12 | +func MiniQrcodeInviteHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
13 | + return func(w http.ResponseWriter, r *http.Request) { | ||
14 | + var req types.MiniQrCodeRequest | ||
15 | + if err := httpx.Parse(r, &req); err != nil { | ||
16 | + httpx.ErrorCtx(r.Context(), w, err) | ||
17 | + return | ||
18 | + } | ||
19 | + | ||
20 | + l := common.NewMiniQrcodeInviteLogic(r.Context(), svcCtx) | ||
21 | + err := l.MiniQrcodeInvite(&req) | ||
22 | + if err != nil { | ||
23 | + httpx.ErrorCtx(r.Context(), w, err) | ||
24 | + } else { | ||
25 | + httpx.Ok(w) | ||
26 | + } | ||
27 | + } | ||
28 | +} |
cmd/bsi/api/internal/handler/routes.go
0 → 100644
1 | +// Code generated by goctl. DO NOT EDIT. | ||
2 | +package handler | ||
3 | + | ||
4 | +import ( | ||
5 | + "net/http" | ||
6 | + | ||
7 | + common "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/handler/common" | ||
8 | + todo "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/handler/todo" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
10 | + | ||
11 | + "github.com/zeromicro/go-zero/rest" | ||
12 | +) | ||
13 | + | ||
14 | +func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { | ||
15 | + server.AddRoutes( | ||
16 | + []rest.Route{ | ||
17 | + { | ||
18 | + Method: http.MethodGet, | ||
19 | + Path: "/log/:module", | ||
20 | + Handler: common.CommonGetLogHandler(serverCtx), | ||
21 | + }, | ||
22 | + }, | ||
23 | + rest.WithPrefix("/v1"), | ||
24 | + ) | ||
25 | + | ||
26 | + server.AddRoutes( | ||
27 | + rest.WithMiddlewares( | ||
28 | + []rest.Middleware{serverCtx.LogRequest}, | ||
29 | + []rest.Route{ | ||
30 | + { | ||
31 | + Method: http.MethodPost, | ||
32 | + Path: "/common/sms/code", | ||
33 | + Handler: common.CommonSmsCodeHandler(serverCtx), | ||
34 | + }, | ||
35 | + { | ||
36 | + Method: http.MethodPost, | ||
37 | + Path: "/mini/qrcode", | ||
38 | + Handler: common.MiniQrcodeInviteHandler(serverCtx), | ||
39 | + }, | ||
40 | + { | ||
41 | + Method: http.MethodGet, | ||
42 | + Path: "/clear", | ||
43 | + Handler: common.CommonGetClearCacheHandler(serverCtx), | ||
44 | + }, | ||
45 | + }..., | ||
46 | + ), | ||
47 | + rest.WithPrefix("/v1"), | ||
48 | + ) | ||
49 | + | ||
50 | + server.AddRoutes( | ||
51 | + rest.WithMiddlewares( | ||
52 | + []rest.Middleware{serverCtx.LogRequest}, | ||
53 | + []rest.Route{ | ||
54 | + { | ||
55 | + Method: http.MethodGet, | ||
56 | + Path: "/todo", | ||
57 | + Handler: todo.H5TodoHandler(serverCtx), | ||
58 | + }, | ||
59 | + { | ||
60 | + Method: http.MethodGet, | ||
61 | + Path: "/todo/err1", | ||
62 | + Handler: todo.H5TodoErrorCommonHandler(serverCtx), | ||
63 | + }, | ||
64 | + { | ||
65 | + Method: http.MethodGet, | ||
66 | + Path: "/todo/err2", | ||
67 | + Handler: todo.H5TodoErrorInternalHandler(serverCtx), | ||
68 | + }, | ||
69 | + { | ||
70 | + Method: http.MethodGet, | ||
71 | + Path: "/todo/err3", | ||
72 | + Handler: todo.H5TodoErrorGrpcHandler(serverCtx), | ||
73 | + }, | ||
74 | + }..., | ||
75 | + ), | ||
76 | + rest.WithPrefix("/v1/h5"), | ||
77 | + ) | ||
78 | +} |
1 | +package todo | ||
2 | + | ||
3 | +import ( | ||
4 | + "net/http" | ||
5 | + | ||
6 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/todo" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | +) | ||
10 | + | ||
11 | +func H5TodoErrorCommonHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
12 | + return func(w http.ResponseWriter, r *http.Request) { | ||
13 | + l := todo.NewH5TodoErrorCommonLogic(r.Context(), svcCtx) | ||
14 | + resp, err := l.H5TodoErrorCommon() | ||
15 | + if err != nil { | ||
16 | + httpx.ErrorCtx(r.Context(), w, err) | ||
17 | + } else { | ||
18 | + httpx.OkJsonCtx(r.Context(), w, resp) | ||
19 | + } | ||
20 | + } | ||
21 | +} |
1 | +package todo | ||
2 | + | ||
3 | +import ( | ||
4 | + "net/http" | ||
5 | + | ||
6 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/todo" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | +) | ||
10 | + | ||
11 | +func H5TodoErrorGrpcHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
12 | + return func(w http.ResponseWriter, r *http.Request) { | ||
13 | + l := todo.NewH5TodoErrorGrpcLogic(r.Context(), svcCtx) | ||
14 | + resp, err := l.H5TodoErrorGrpc() | ||
15 | + if err != nil { | ||
16 | + httpx.ErrorCtx(r.Context(), w, err) | ||
17 | + } else { | ||
18 | + httpx.OkJsonCtx(r.Context(), w, resp) | ||
19 | + } | ||
20 | + } | ||
21 | +} |
1 | +package todo | ||
2 | + | ||
3 | +import ( | ||
4 | + "net/http" | ||
5 | + | ||
6 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/todo" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | +) | ||
10 | + | ||
11 | +func H5TodoErrorInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
12 | + return func(w http.ResponseWriter, r *http.Request) { | ||
13 | + l := todo.NewH5TodoErrorInternalLogic(r.Context(), svcCtx) | ||
14 | + resp, err := l.H5TodoErrorInternal() | ||
15 | + if err != nil { | ||
16 | + httpx.ErrorCtx(r.Context(), w, err) | ||
17 | + } else { | ||
18 | + httpx.OkJsonCtx(r.Context(), w, resp) | ||
19 | + } | ||
20 | + } | ||
21 | +} |
1 | +package todo | ||
2 | + | ||
3 | +import ( | ||
4 | + "net/http" | ||
5 | + | ||
6 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/todo" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | +) | ||
10 | + | ||
11 | +func H5TodoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
12 | + return func(w http.ResponseWriter, r *http.Request) { | ||
13 | + l := todo.NewH5TodoLogic(r.Context(), svcCtx) | ||
14 | + resp, err := l.H5Todo() | ||
15 | + if err != nil { | ||
16 | + httpx.ErrorCtx(r.Context(), w, err) | ||
17 | + } else { | ||
18 | + httpx.OkJson(w, resp) | ||
19 | + } | ||
20 | + } | ||
21 | +} |
1 | +package common | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "fmt" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/xerr" | ||
7 | + "strings" | ||
8 | + | ||
9 | + "github.com/zeromicro/go-zero/core/logx" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
11 | +) | ||
12 | + | ||
13 | +type CommonGetClearCacheLogic struct { | ||
14 | + logx.Logger | ||
15 | + ctx context.Context | ||
16 | + svcCtx *svc.ServiceContext | ||
17 | +} | ||
18 | + | ||
19 | +func NewCommonGetClearCacheLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommonGetClearCacheLogic { | ||
20 | + return &CommonGetClearCacheLogic{ | ||
21 | + Logger: logx.WithContext(ctx), | ||
22 | + ctx: ctx, | ||
23 | + svcCtx: svcCtx, | ||
24 | + } | ||
25 | +} | ||
26 | + | ||
27 | +func (l *CommonGetClearCacheLogic) CommonGetClearCache() error { | ||
28 | + var ( | ||
29 | + appName = l.svcCtx.Config.Name | ||
30 | + success int | ||
31 | + ) | ||
32 | + if strings.TrimSpace(appName) == "" { | ||
33 | + return nil | ||
34 | + } | ||
35 | + keyPattern := fmt.Sprintf("%s*", appName) | ||
36 | + list, err := l.svcCtx.Redis.Keys(keyPattern) | ||
37 | + if err != nil { | ||
38 | + return xerr.NewErrMsg(err.Error()) | ||
39 | + } | ||
40 | + for _, key := range list { | ||
41 | + if _, err = l.svcCtx.Redis.Del(key); err == nil { | ||
42 | + success++ | ||
43 | + } | ||
44 | + } | ||
45 | + logx.Infof("清理缓存:%d/%d", success, len(list)) | ||
46 | + return nil | ||
47 | +} |
1 | +package common | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + | ||
6 | + "github.com/zeromicro/go-zero/core/logx" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
8 | +) | ||
9 | + | ||
10 | +type CommonGetLogLogic struct { | ||
11 | + logx.Logger | ||
12 | + ctx context.Context | ||
13 | + svcCtx *svc.ServiceContext | ||
14 | +} | ||
15 | + | ||
16 | +func NewCommonGetLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommonGetLogLogic { | ||
17 | + return &CommonGetLogLogic{ | ||
18 | + Logger: logx.WithContext(ctx), | ||
19 | + ctx: ctx, | ||
20 | + svcCtx: svcCtx, | ||
21 | + } | ||
22 | +} | ||
23 | + | ||
24 | +func (l *CommonGetLogLogic) CommonGetLog() error { | ||
25 | + // todo: add your logic here and delete this line | ||
26 | + | ||
27 | + return nil | ||
28 | +} |
1 | +package common | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types" | ||
8 | + | ||
9 | + "github.com/zeromicro/go-zero/core/logx" | ||
10 | +) | ||
11 | + | ||
12 | +type CommonSmsCodeLogic struct { | ||
13 | + logx.Logger | ||
14 | + ctx context.Context | ||
15 | + svcCtx *svc.ServiceContext | ||
16 | +} | ||
17 | + | ||
18 | +func NewCommonSmsCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommonSmsCodeLogic { | ||
19 | + return &CommonSmsCodeLogic{ | ||
20 | + Logger: logx.WithContext(ctx), | ||
21 | + ctx: ctx, | ||
22 | + svcCtx: svcCtx, | ||
23 | + } | ||
24 | +} | ||
25 | + | ||
26 | +func (l *CommonSmsCodeLogic) CommonSmsCode(req *types.CommonSmsCodeRequest) (resp *types.CommonSmsCodeResposne, err error) { | ||
27 | + // todo: add your logic here and delete this line | ||
28 | + | ||
29 | + return | ||
30 | +} |
1 | +package common | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types" | ||
8 | + | ||
9 | + "github.com/zeromicro/go-zero/core/logx" | ||
10 | +) | ||
11 | + | ||
12 | +type MiniQrcodeInviteLogic struct { | ||
13 | + logx.Logger | ||
14 | + ctx context.Context | ||
15 | + svcCtx *svc.ServiceContext | ||
16 | +} | ||
17 | + | ||
18 | +func NewMiniQrcodeInviteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MiniQrcodeInviteLogic { | ||
19 | + return &MiniQrcodeInviteLogic{ | ||
20 | + Logger: logx.WithContext(ctx), | ||
21 | + ctx: ctx, | ||
22 | + svcCtx: svcCtx, | ||
23 | + } | ||
24 | +} | ||
25 | + | ||
26 | +func (l *MiniQrcodeInviteLogic) MiniQrcodeInvite(req *types.MiniQrCodeRequest) error { | ||
27 | + // todo: add your logic here and delete this line | ||
28 | + | ||
29 | + return nil | ||
30 | +} |
1 | +package todo | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "fmt" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/xerr" | ||
7 | + | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types" | ||
10 | + | ||
11 | + "github.com/zeromicro/go-zero/core/logx" | ||
12 | +) | ||
13 | + | ||
14 | +type H5TodoErrorCommonLogic struct { | ||
15 | + logx.Logger | ||
16 | + ctx context.Context | ||
17 | + svcCtx *svc.ServiceContext | ||
18 | +} | ||
19 | + | ||
20 | +func NewH5TodoErrorCommonLogic(ctx context.Context, svcCtx *svc.ServiceContext) *H5TodoErrorCommonLogic { | ||
21 | + return &H5TodoErrorCommonLogic{ | ||
22 | + Logger: logx.WithContext(ctx), | ||
23 | + ctx: ctx, | ||
24 | + svcCtx: svcCtx, | ||
25 | + } | ||
26 | +} | ||
27 | + | ||
28 | +func (l *H5TodoErrorCommonLogic) H5TodoErrorCommon() (resp *types.TodoResonse, err error) { | ||
29 | + // todo: add your logic here and delete this line | ||
30 | + err = xerr.NewCodeErr(xerr.DbError, fmt.Errorf("no result")) | ||
31 | + return | ||
32 | +} |
1 | +package todo | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "google.golang.org/grpc/codes" | ||
6 | + "google.golang.org/grpc/status" | ||
7 | + | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types" | ||
10 | + | ||
11 | + "github.com/zeromicro/go-zero/core/logx" | ||
12 | +) | ||
13 | + | ||
14 | +type H5TodoErrorGrpcLogic struct { | ||
15 | + logx.Logger | ||
16 | + ctx context.Context | ||
17 | + svcCtx *svc.ServiceContext | ||
18 | +} | ||
19 | + | ||
20 | +func NewH5TodoErrorGrpcLogic(ctx context.Context, svcCtx *svc.ServiceContext) *H5TodoErrorGrpcLogic { | ||
21 | + return &H5TodoErrorGrpcLogic{ | ||
22 | + Logger: logx.WithContext(ctx), | ||
23 | + ctx: ctx, | ||
24 | + svcCtx: svcCtx, | ||
25 | + } | ||
26 | +} | ||
27 | + | ||
28 | +func (l *H5TodoErrorGrpcLogic) H5TodoErrorGrpc() (resp *types.TodoResonse, err error) { | ||
29 | + // todo: add your logic here and delete this line | ||
30 | + err = status.Error(codes.DeadlineExceeded, "Grpc deadline...") | ||
31 | + return | ||
32 | +} |
1 | +package todo | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "fmt" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/xerr" | ||
7 | + | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types" | ||
10 | + | ||
11 | + "github.com/zeromicro/go-zero/core/logx" | ||
12 | +) | ||
13 | + | ||
14 | +type H5TodoErrorInternalLogic struct { | ||
15 | + logx.Logger | ||
16 | + ctx context.Context | ||
17 | + svcCtx *svc.ServiceContext | ||
18 | +} | ||
19 | + | ||
20 | +func NewH5TodoErrorInternalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *H5TodoErrorInternalLogic { | ||
21 | + return &H5TodoErrorInternalLogic{ | ||
22 | + Logger: logx.WithContext(ctx), | ||
23 | + ctx: ctx, | ||
24 | + svcCtx: svcCtx, | ||
25 | + } | ||
26 | +} | ||
27 | + | ||
28 | +func (l *H5TodoErrorInternalLogic) H5TodoErrorInternal() (resp *types.TodoResonse, err error) { | ||
29 | + // todo: add your logic here and delete this line | ||
30 | + err = xerr.NewErrMsgErr("busy", fmt.Errorf("internal error")) | ||
31 | + return | ||
32 | +} |
1 | +package todo | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "fmt" | ||
6 | + | ||
7 | + "github.com/zeromicro/go-zero/core/logx" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc" | ||
9 | +) | ||
10 | + | ||
11 | +type H5TodoLogic struct { | ||
12 | + logx.Logger | ||
13 | + ctx context.Context | ||
14 | + svcCtx *svc.ServiceContext | ||
15 | +} | ||
16 | + | ||
17 | +func NewH5TodoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *H5TodoLogic { | ||
18 | + return &H5TodoLogic{ | ||
19 | + Logger: logx.WithContext(ctx), | ||
20 | + ctx: ctx, | ||
21 | + svcCtx: svcCtx, | ||
22 | + } | ||
23 | +} | ||
24 | + | ||
25 | +func (l *H5TodoLogic) H5Todo() (interface{}, error) { | ||
26 | + // todo: add your logic here and delete this line | ||
27 | + | ||
28 | + return "todo", fmt.Errorf("todo error") | ||
29 | +} |
1 | +package middleware | ||
2 | + | ||
3 | +import "net/http" | ||
4 | + | ||
5 | +type LogRequestMiddleware struct { | ||
6 | +} | ||
7 | + | ||
8 | +func NewLogRequestMiddleware() *LogRequestMiddleware { | ||
9 | + return &LogRequestMiddleware{} | ||
10 | +} | ||
11 | + | ||
12 | +func (m *LogRequestMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { | ||
13 | + return func(w http.ResponseWriter, r *http.Request) { | ||
14 | + // TODO generate middleware implement function, delete after code implementation | ||
15 | + | ||
16 | + // Passthrough to next handler if need | ||
17 | + next(w, r) | ||
18 | + } | ||
19 | +} |
cmd/bsi/api/internal/svc/service_context.go
0 → 100644
1 | +package svc | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/zeromicro/go-zero/core/stores/redis" | ||
5 | + "github.com/zeromicro/go-zero/rest" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/config" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/database" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway/authlib" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway/openlib" | ||
11 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway/smslib" | ||
12 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/middleware" | ||
13 | + "gorm.io/gorm" | ||
14 | +) | ||
15 | + | ||
16 | +type ServiceContext struct { | ||
17 | + Config config.Config | ||
18 | + DB *gorm.DB | ||
19 | + Redis *redis.Redis | ||
20 | + | ||
21 | + LogRequest rest.Middleware | ||
22 | + LoginStatusCheck rest.Middleware | ||
23 | + | ||
24 | + ApiAuthService authlib.ApiAuthService | ||
25 | + SmsService smslib.SMSService | ||
26 | + OpenApiService openlib.OpenApiService | ||
27 | +} | ||
28 | + | ||
29 | +func NewServiceContext(c config.Config) *ServiceContext { | ||
30 | + db := database.OpenGormPGDB(c.DB.DataSource, c.Log.Mode) | ||
31 | + | ||
32 | + //mlCache := cache.NewMultiLevelCache([]string{c.Redis.Host}, c.Redis.Pass) | ||
33 | + redis, _ := redis.NewRedis(redis.RedisConf{Host: c.Redis.Host, Pass: c.Redis.Pass, Type: "node"}) | ||
34 | + apiAuth := authlib.ApiAuthService{ | ||
35 | + Service: gateway.NewService(c.ApiAuth.Name, c.ApiAuth.Host, c.ApiAuth.Timeout), | ||
36 | + } | ||
37 | + | ||
38 | + return &ServiceContext{ | ||
39 | + Config: c, | ||
40 | + DB: db, | ||
41 | + Redis: redis, | ||
42 | + ApiAuthService: apiAuth, | ||
43 | + LogRequest: middleware.NewLogRequestMiddleware(c.LogRequest).Handle, | ||
44 | + } | ||
45 | +} |
cmd/bsi/api/internal/types/types.go
0 → 100644
1 | +// Code generated by goctl. DO NOT EDIT. | ||
2 | +package types | ||
3 | + | ||
4 | +type CommonSmsCodeRequest struct { | ||
5 | + Phone string `json:"phone"` | ||
6 | +} | ||
7 | + | ||
8 | +type CommonSmsCodeResposne struct { | ||
9 | +} | ||
10 | + | ||
11 | +type MiniQrCodeRequest struct { | ||
12 | + Page string `json:"page"` // 微信页面入口 | ||
13 | + Scene string `json:"scene"` // 参数 | ||
14 | +} | ||
15 | + | ||
16 | +type TodoRequest struct { | ||
17 | +} | ||
18 | + | ||
19 | +type TodoResonse struct { | ||
20 | +} |
cmd/bsi/deploy/database/table.sql
0 → 100644
cmd/bsi/deploy/docker/Dockerfile
0 → 100644
1 | +FROM golang:1.19-alpine as builder | ||
2 | + | ||
3 | +# Define the project name | 定义项目名称 | ||
4 | +ARG PROJECT=core | ||
5 | +ARG PROJECTCODE=bsi | ||
6 | + | ||
7 | +WORKDIR /build | ||
8 | +COPY . . | ||
9 | + | ||
10 | +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories | ||
11 | +RUN apk update --no-cache && apk add --no-cache tzdata | ||
12 | +RUN go env -w GO111MODULE=on \ | ||
13 | + && go env -w GOPROXY=https://goproxy.cn,direct \ | ||
14 | + && go env -w CGO_ENABLED=0 \ | ||
15 | + && go env \ | ||
16 | + && go mod tidy \ | ||
17 | + && cd cmd/${PROJECTCODE}/api \ | ||
18 | + && go build -ldflags="-s -w" -o /build/api/${PROJECT} ${PROJECT}.go | ||
19 | + | ||
20 | +FROM alpine:latest | ||
21 | + | ||
22 | +# Define the project name | 定义项目名称 | ||
23 | +ARG PROJECT=core | ||
24 | +ARG PROJECTCODE=bsi | ||
25 | +# Define the config file name | 定义配置文件名 | ||
26 | +ARG CONFIG_FILE=core.yaml | ||
27 | +# Define the author | 定义作者 | ||
28 | +ARG AUTHOR=785409885@qq.com | ||
29 | + | ||
30 | +LABEL org.opencontainers.image.authors=${AUTHOR} | ||
31 | + | ||
32 | +WORKDIR /app | ||
33 | +ENV PROJECT=${PROJECT} | ||
34 | +ENV CONFIG_FILE=${CONFIG_FILE} | ||
35 | +ENV TZ Asia/Shanghai | ||
36 | + | ||
37 | +COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai | ||
38 | +COPY --from=builder /build/api/${PROJECT} ./ | ||
39 | +COPY --from=builder /build/cmd/${PROJECTCODE}/api/etc/${CONFIG_FILE} ./etc/ | ||
40 | + | ||
41 | +EXPOSE 8080 | ||
42 | +ENTRYPOINT ./${PROJECT} -f etc/${CONFIG_FILE} |
cmd/bsi/deploy/k8s/dev/install.sh
0 → 100644
1 | +#!/bin/bash | ||
2 | +export PATH=/root/local/bin:$PATH | ||
3 | +kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
4 | +if [ "$?" == "1" ];then | ||
5 | + kubectl create -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml --record | ||
6 | + kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss | ||
7 | + if [ "$?" == "0" ];then | ||
8 | + echo "sumifcc-discuss service install success!" | ||
9 | + else | ||
10 | + echo "sumifcc-discuss service install fail!" | ||
11 | + fi | ||
12 | + kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
13 | + if [ "$?" == "0" ];then | ||
14 | + echo "sumifcc-discuss deployment install success!" | ||
15 | + else | ||
16 | + echo "sumifcc-discuss deployment install fail!" | ||
17 | + fi | ||
18 | +else | ||
19 | + kubectl delete -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml | ||
20 | + kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss | ||
21 | + while [ "$?" == "0" ] | ||
22 | + do | ||
23 | + kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss | ||
24 | + done | ||
25 | + kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
26 | + while [ "$?" == "0" ] | ||
27 | + do | ||
28 | + kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
29 | + done | ||
30 | + kubectl create -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml --record | ||
31 | + kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss | ||
32 | + if [ "$?" == "0" ];then | ||
33 | + echo "sumifcc-discuss service update success!" | ||
34 | + else | ||
35 | + echo "sumifcc-discuss service update fail!" | ||
36 | + fi | ||
37 | + kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
38 | + if [ "$?" == "0" ];then | ||
39 | + echo "sumifcc-discuss deployment update success!" | ||
40 | + else | ||
41 | + echo "sumifcc-discuss deployment update fail!" | ||
42 | + fi | ||
43 | +fi |
cmd/bsi/deploy/k8s/test/install.sh
0 → 100644
1 | +#!/bin/bash | ||
2 | +export PATH=/root/local/bin:$PATH | ||
3 | +kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
4 | +if [ "$?" == "1" ];then | ||
5 | + kubectl create -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml --record | ||
6 | + kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss | ||
7 | + if [ "$?" == "0" ];then | ||
8 | + echo "sumifcc-discuss service install success!" | ||
9 | + else | ||
10 | + echo "sumifcc-discuss service install fail!" | ||
11 | + fi | ||
12 | + kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
13 | + if [ "$?" == "0" ];then | ||
14 | + echo "sumifcc-discuss deployment install success!" | ||
15 | + else | ||
16 | + echo "sumifcc-discuss deployment install fail!" | ||
17 | + fi | ||
18 | +else | ||
19 | + kubectl delete -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml | ||
20 | + kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss | ||
21 | + while [ "$?" == "0" ] | ||
22 | + do | ||
23 | + kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss | ||
24 | + done | ||
25 | + kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
26 | + while [ "$?" == "0" ] | ||
27 | + do | ||
28 | + kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
29 | + done | ||
30 | + kubectl create -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml --record | ||
31 | + kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss | ||
32 | + if [ "$?" == "0" ];then | ||
33 | + echo "sumifcc-discuss service update success!" | ||
34 | + else | ||
35 | + echo "sumifcc-discuss service update fail!" | ||
36 | + fi | ||
37 | + kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss | ||
38 | + if [ "$?" == "0" ];then | ||
39 | + echo "sumifcc-discuss deployment update success!" | ||
40 | + else | ||
41 | + echo "sumifcc-discuss deployment update fail!" | ||
42 | + fi | ||
43 | +fi |
cmd/bsi/deploy/k8s/test/sumifcc-discuss.yaml
0 → 100644
1 | +apiVersion: v1 | ||
2 | +kind: Service | ||
3 | +metadata: | ||
4 | + name: sumifcc-discuss | ||
5 | + namespace: mmm-suplus-test | ||
6 | + labels: | ||
7 | + k8s-app: sumifcc-discuss | ||
8 | +spec: | ||
9 | + ports: | ||
10 | + - name: "http" | ||
11 | + port: 80 | ||
12 | + targetPort: 8081 | ||
13 | + - name: "https" | ||
14 | + port: 443 | ||
15 | + targetPort: 443 | ||
16 | + selector: | ||
17 | + k8s-app: sumifcc-discuss | ||
18 | +--- | ||
19 | +apiVersion: extensions/v1beta1 | ||
20 | +kind: Deployment | ||
21 | +metadata: | ||
22 | + name: sumifcc-discuss | ||
23 | + namespace: mmm-suplus-test | ||
24 | + labels: | ||
25 | + k8s-app: sumifcc-discuss | ||
26 | +spec: | ||
27 | + replicas: 1 | ||
28 | + template: | ||
29 | + metadata: | ||
30 | + labels: | ||
31 | + k8s-app: sumifcc-discuss | ||
32 | + spec: | ||
33 | + affinity: | ||
34 | + nodeAffinity: | ||
35 | + preferredDuringSchedulingIgnoredDuringExecution: | ||
36 | + - preference: {} | ||
37 | + weight: 100 | ||
38 | + requiredDuringSchedulingIgnoredDuringExecution: | ||
39 | + nodeSelectorTerms: | ||
40 | + - matchExpressions: | ||
41 | + - key: kubernetes.io/hostname | ||
42 | + operator: In | ||
43 | + values: | ||
44 | + - cn-hangzhou.i-bp1djh1xn7taumbue1ze | ||
45 | + | ||
46 | + containers: | ||
47 | + - name: sumifcc-discuss | ||
48 | + image: 192.168.0.243:5000/mmm/sumifcc-discuss:dev | ||
49 | + imagePullPolicy: Always | ||
50 | + ports: | ||
51 | + - containerPort: 8081 | ||
52 | + - containerPort: 443 | ||
53 | + volumeMounts: | ||
54 | + - mountPath: /opt/logs | ||
55 | + name: accesslogs | ||
56 | + env: | ||
57 | + - name: LOG_LEVEL | ||
58 | + value: "debug" | ||
59 | + - name: LOG_FILE | ||
60 | + value: "true" | ||
61 | + - name: REDIS_HOST | ||
62 | + valueFrom: | ||
63 | + configMapKeyRef: | ||
64 | + name: suplus-config | ||
65 | + key: redis.ip | ||
66 | + - name: REDIS_PORT | ||
67 | + valueFrom: | ||
68 | + configMapKeyRef: | ||
69 | + name: suplus-config | ||
70 | + key: redis.port | ||
71 | + volumes: | ||
72 | + - name: accesslogs | ||
73 | + emptyDir: {} |
cmd/bsi/doc/dsl/api/todo.api
0 → 100644
1 | + | ||
2 | +syntax = "v1" | ||
3 | + | ||
4 | +info( | ||
5 | + title: "xx实例" | ||
6 | + desc: "xx实例" | ||
7 | + author: "author" | ||
8 | + email: "email" | ||
9 | + version: "v1" | ||
10 | +) | ||
11 | + | ||
12 | +@server( | ||
13 | + prefix: todo/v1 | ||
14 | + group: todo | ||
15 | + jwt: JwtAuth | ||
16 | +) | ||
17 | +service Core { | ||
18 | + @doc "详情" | ||
19 | + @handler todoGet | ||
20 | + get /todo/:id (TodoGetRequest) returns (TodoGetResponse) | ||
21 | + @doc "保存" | ||
22 | + @handler todoSave | ||
23 | + post /todo (TodoSaveRequest) returns (TodoSaveResponse) | ||
24 | + @doc "删除" | ||
25 | + @handler todoDelete | ||
26 | + delete /todo/:id (TodoDeleteRequest) returns (TodoDeleteResponse) | ||
27 | + @doc "更新" | ||
28 | + @handler todoUpdate | ||
29 | + put /todo/:id (TodoUpdateRequest) returns (TodoUpdateResponse) | ||
30 | + @doc "搜索" | ||
31 | + @handler todoSearch | ||
32 | + post /todo/search (TodoSearchRequest) returns (TodoSearchResponse) | ||
33 | +} | ||
34 | + | ||
35 | +type ( | ||
36 | + TodoGetRequest { | ||
37 | + Id int64 `path:"id"` | ||
38 | + } | ||
39 | + TodoGetResponse struct{ | ||
40 | + Todo TodoItem `json:"todo"` | ||
41 | + } | ||
42 | + | ||
43 | + TodoSaveRequest struct{ | ||
44 | + Todo TodoItem `json:"todo"` | ||
45 | + } | ||
46 | + TodoSaveResponse struct{} | ||
47 | + | ||
48 | + TodoDeleteRequest struct{ | ||
49 | + Id int64 `path:"id"` | ||
50 | + } | ||
51 | + TodoDeleteResponse struct{} | ||
52 | + | ||
53 | + TodoUpdateRequest struct{ | ||
54 | + Id int64 `path:"id"` | ||
55 | + Todo TodoItem `json:"todo"` | ||
56 | + } | ||
57 | + TodoUpdateResponse struct{} | ||
58 | + | ||
59 | + TodoSearchRequest struct{ | ||
60 | + Page int `json:"page"` | ||
61 | + Size int `json:"size"` | ||
62 | + } | ||
63 | + TodoSearchResponse{ | ||
64 | + List []TodoItem `json:"list"` | ||
65 | + Total int64 `json:"total"` | ||
66 | + } | ||
67 | + TodoItem struct{ | ||
68 | + | ||
69 | + } | ||
70 | +) | ||
71 | + | ||
72 | +// logic CRUD | ||
73 | +// Save | ||
74 | + //var ( | ||
75 | + // conn = l.svcCtx.DefaultDBConn() | ||
76 | + // dm *domain.Todo | ||
77 | + //) | ||
78 | + //// 唯一判断 | ||
79 | + | ||
80 | + //dm = NewDomainTodo(req.Todo) | ||
81 | + //if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error { | ||
82 | + // dm, err = l.svcCtx.TodoRepository.Insert(l.ctx, conn, dm) | ||
83 | + // return err | ||
84 | + //}, true); err != nil { | ||
85 | + // return nil, xerr.NewErrMsg("保存失败") | ||
86 | + //} | ||
87 | + ////resp = &types.TodoSaveResponse{} | ||
88 | + //return | ||
89 | + | ||
90 | +//func NewDomainTodo(item types.TodoItem) *domain.Todo { | ||
91 | +// return &domain.Todo{ | ||
92 | + | ||
93 | +// } | ||
94 | +//} | ||
95 | +// | ||
96 | +//func NewTypesTodo(item *domain.Todo) types.TodoItem { | ||
97 | +// return types.TodoItem{ | ||
98 | +// Id: item.Id, | ||
99 | +// } | ||
100 | +//} | ||
101 | + | ||
102 | +// Get | ||
103 | + //var ( | ||
104 | + // conn = l.svcCtx.DefaultDBConn() | ||
105 | + // dm *domain.Todo | ||
106 | + //) | ||
107 | + //// 货号唯一 | ||
108 | + //if dm, err = l.svcCtx.TodoRepository.FindOne(l.ctx, conn, req.Id); err != nil { | ||
109 | + // return nil, xerr.NewErrMsgErr("不存在", err) | ||
110 | + //} | ||
111 | + //resp = &types.TodoGetResponse{ | ||
112 | + // Todo: NewTypesTodo(dm), | ||
113 | + //} | ||
114 | + //return | ||
115 | + | ||
116 | +// Delete | ||
117 | + //var ( | ||
118 | + // conn = l.svcCtx.DefaultDBConn() | ||
119 | + // dm *domain.Todo | ||
120 | + //) | ||
121 | + //if dm, err = l.svcCtx.TodoRepository.FindOne(l.ctx, conn, req.Id); err != nil { | ||
122 | + // return nil, xerr.NewErrMsgErr("不存在", err) | ||
123 | + //} | ||
124 | + //if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error { | ||
125 | + // if dm, err = l.svcCtx.TodoRepository.Delete(l.ctx, conn, dm); err != nil { | ||
126 | + // return err | ||
127 | + // } | ||
128 | + // return nil | ||
129 | + //}, true); err != nil { | ||
130 | + // return nil, xerr.NewErrMsgErr("移除失败", err) | ||
131 | + //} | ||
132 | + //return | ||
133 | + | ||
134 | +// Search | ||
135 | + //var ( | ||
136 | + // conn = l.svcCtx.DefaultDBConn() | ||
137 | + // dms []*domain.Todo | ||
138 | + // total int64 | ||
139 | + //) | ||
140 | + // | ||
141 | + //queryOptions := domain.NewQueryOptions().WithOffsetLimit(req.Page, req.Size). | ||
142 | + // WithKV("", "") | ||
143 | + | ||
144 | + //total, dms, err = l.svcCtx.TodoRepository.Find(l.ctx, conn, queryOptions) | ||
145 | + //list := make([]types.TodoItem, 0) | ||
146 | + //for i := range dms { | ||
147 | + // list = append(list, NewTypesTodo(dms[i])) | ||
148 | + //} | ||
149 | + //resp = &types.TodoSearchResponse{ | ||
150 | + // List: list, | ||
151 | + // Total: total, | ||
152 | + //} | ||
153 | + //return | ||
154 | + | ||
155 | +// Update | ||
156 | + //var ( | ||
157 | + // conn = l.svcCtx.DefaultDBConn() | ||
158 | + // dm *domain.Todo | ||
159 | + //) | ||
160 | + //if dm, err = l.svcCtx.TodoRepository.FindOne(l.ctx, conn, req.Id); err != nil { | ||
161 | + // return nil, xerr.NewErrMsgErr("不存在", err) | ||
162 | + //} | ||
163 | + //// 不可编辑判断 | ||
164 | + | ||
165 | + //// 赋值 | ||
166 | + | ||
167 | + //// 更新 | ||
168 | + //if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error { | ||
169 | + // dm, err = l.svcCtx.TodoRepository.UpdateWithVersion(l.ctx, conn, dm) | ||
170 | + // return err | ||
171 | + //}, true); err != nil { | ||
172 | + // return nil, xerr.NewErrMsg("更新失败") | ||
173 | + //} | ||
174 | + //resp = &types.TodoUpdateResponse{} | ||
175 | + //return |
cmd/bsi/doc/dsl/rpc/todo.proto
0 → 100644
1 | + | ||
2 | +syntax = "proto3"; | ||
3 | + | ||
4 | +option go_package ="./pb"; | ||
5 | + | ||
6 | +package pb; | ||
7 | + | ||
8 | +message TodoGetReq { | ||
9 | + int64 Id = 1; | ||
10 | +} | ||
11 | +message TodoGetResp{ | ||
12 | + TodoItem User = 1; | ||
13 | +} | ||
14 | + | ||
15 | +message TodoSaveReq { | ||
16 | + | ||
17 | +} | ||
18 | +message TodoSaveResp{ | ||
19 | + | ||
20 | +} | ||
21 | + | ||
22 | +message TodoDeleteReq { | ||
23 | + int64 Id = 1; | ||
24 | +} | ||
25 | +message TodoDeleteResp{ | ||
26 | + | ||
27 | +} | ||
28 | + | ||
29 | +message TodoUpdateReq { | ||
30 | + int64 Id = 1; | ||
31 | +} | ||
32 | +message TodoUpdateResp{ | ||
33 | + | ||
34 | +} | ||
35 | + | ||
36 | +message TodoSearchReq { | ||
37 | + int64 PageNumber = 1; | ||
38 | + int64 PageSize = 2; | ||
39 | +} | ||
40 | +message TodoSearchResp{ | ||
41 | + repeated TodoItem List =1; | ||
42 | + int64 Total =2; | ||
43 | +} | ||
44 | +message TodoItem { | ||
45 | + | ||
46 | +} | ||
47 | + | ||
48 | +service TodoService { | ||
49 | + rpc TodoGet(TodoGetReq) returns(TodoGetResp); | ||
50 | + rpc TodoSave(TodoSaveReq) returns(TodoSaveResp); | ||
51 | + rpc TodoDelete(TodoDeleteReq) returns(TodoDeleteResp); | ||
52 | + rpc TodoUpdate(TodoUpdateReq) returns(TodoUpdateResp); | ||
53 | + rpc TodoSearch(TodoSearchReq) returns(TodoSearchResp); | ||
54 | +} |
cmd/bsi/interanl/pkg/db/migrate.go
0 → 100644
cmd/bsi/interanl/pkg/db/models/todo.go
0 → 100644
1 | +package models | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/interanl/pkg/domain" | ||
6 | + "gorm.io/gorm" | ||
7 | + "gorm.io/plugin/soft_delete" | ||
8 | +) | ||
9 | + | ||
10 | +type Todo struct { | ||
11 | + Id int64 // 唯一标识 | ||
12 | + CreatedAt int64 | ||
13 | + UpdatedAt int64 | ||
14 | + IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"` | ||
15 | + DeletedAt int64 | ||
16 | + Version int | ||
17 | +} | ||
18 | + | ||
19 | +func (m *Todo) TableName() string { | ||
20 | + return "todo" | ||
21 | +} | ||
22 | + | ||
23 | +func (m *Todo) BeforeCreate(tx *gorm.DB) (err error) { | ||
24 | + // m.CreatedAt = time.Now().Unix() | ||
25 | + // m.UpdatedAt = time.Now().Unix() | ||
26 | + return | ||
27 | +} | ||
28 | + | ||
29 | +func (m *Todo) BeforeUpdate(tx *gorm.DB) (err error) { | ||
30 | + // m.UpdatedAt = time.Now().Unix() | ||
31 | + return | ||
32 | +} | ||
33 | + | ||
34 | +func (m *Todo) CacheKeyFunc() string { | ||
35 | + if m.Id == 0 { | ||
36 | + return "" | ||
37 | + } | ||
38 | + return fmt.Sprintf("%v:cache:%v:id:%v", domain.ProjectName, m.TableName(), m.Id) | ||
39 | +} | ||
40 | + | ||
41 | +func (m *Todo) CacheKeyFuncByObject(obj interface{}) string { | ||
42 | + if v, ok := obj.(*Todo); ok { | ||
43 | + return v.CacheKeyFunc() | ||
44 | + } | ||
45 | + return "" | ||
46 | +} | ||
47 | + | ||
48 | +func (m *Todo) CachePrimaryKeyFunc() string { | ||
49 | + if len("") == 0 { | ||
50 | + return "" | ||
51 | + } | ||
52 | + return fmt.Sprintf("%v:cache:%v:primarykey:%v", domain.ProjectName, m.TableName(), "key") | ||
53 | +} |
1 | +package repository | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "github.com/jinzhu/copier" | ||
6 | + "github.com/pkg/errors" | ||
7 | + "github.com/tiptok/gocomm/pkg/cache" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/interanl/pkg/db/models" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/interanl/pkg/domain" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/transaction" | ||
11 | + "gorm.io/gorm" | ||
12 | +) | ||
13 | + | ||
14 | +type TodoRepository struct { | ||
15 | + *cache.CachedRepository | ||
16 | +} | ||
17 | + | ||
18 | +func (repository *TodoRepository) Insert(ctx context.Context, conn transaction.Conn, dm *domain.Todo) (*domain.Todo, error) { | ||
19 | + var ( | ||
20 | + err error | ||
21 | + m = &models.Todo{} | ||
22 | + tx = conn.DB() | ||
23 | + ) | ||
24 | + if m, err = repository.DomainModelToModel(dm); err != nil { | ||
25 | + return nil, err | ||
26 | + } | ||
27 | + if tx = tx.Model(m).Save(m); tx.Error != nil { | ||
28 | + return nil, tx.Error | ||
29 | + } | ||
30 | + dm.Id = m.Id | ||
31 | + return repository.ModelToDomainModel(m) | ||
32 | + | ||
33 | +} | ||
34 | + | ||
35 | +func (repository *TodoRepository) Update(ctx context.Context, conn transaction.Conn, dm *domain.Todo) (*domain.Todo, error) { | ||
36 | + var ( | ||
37 | + err error | ||
38 | + m *models.Todo | ||
39 | + tx = conn.DB() | ||
40 | + ) | ||
41 | + if m, err = repository.DomainModelToModel(dm); err != nil { | ||
42 | + return nil, err | ||
43 | + } | ||
44 | + queryFunc := func() (interface{}, error) { | ||
45 | + tx = tx.Model(m).Updates(m) | ||
46 | + return nil, tx.Error | ||
47 | + } | ||
48 | + if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil { | ||
49 | + return nil, err | ||
50 | + } | ||
51 | + return repository.ModelToDomainModel(m) | ||
52 | +} | ||
53 | + | ||
54 | +func (repository *TodoRepository) UpdateWithVersion(ctx context.Context, transaction transaction.Conn, dm *domain.Todo) (*domain.Todo, error) { | ||
55 | + var ( | ||
56 | + err error | ||
57 | + m *models.Todo | ||
58 | + tx = transaction.DB() | ||
59 | + ) | ||
60 | + if m, err = repository.DomainModelToModel(dm); err != nil { | ||
61 | + return nil, err | ||
62 | + } | ||
63 | + oldVersion := dm.Version | ||
64 | + m.Version += 1 | ||
65 | + queryFunc := func() (interface{}, error) { | ||
66 | + tx = tx.Model(m).Select("*").Where("id = ?", m.Id).Where("version = ?", oldVersion).Updates(m) | ||
67 | + if tx.RowsAffected == 0 { | ||
68 | + return nil, domain.ErrUpdateFail | ||
69 | + } | ||
70 | + return nil, tx.Error | ||
71 | + } | ||
72 | + if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil { | ||
73 | + return nil, err | ||
74 | + } | ||
75 | + return repository.ModelToDomainModel(m) | ||
76 | +} | ||
77 | + | ||
78 | +func (repository *TodoRepository) Delete(ctx context.Context, conn transaction.Conn, dm *domain.Todo) (*domain.Todo, error) { | ||
79 | + var ( | ||
80 | + tx = conn.DB() | ||
81 | + m = &models.Todo{Id: dm.Identify().(int64)} | ||
82 | + ) | ||
83 | + queryFunc := func() (interface{}, error) { | ||
84 | + tx = tx.Where("id = ?", m.Id).Delete(m) | ||
85 | + return m, tx.Error | ||
86 | + } | ||
87 | + if _, err := repository.Query(queryFunc, m.CacheKeyFunc()); err != nil { | ||
88 | + return dm, err | ||
89 | + } | ||
90 | + return repository.ModelToDomainModel(m) | ||
91 | +} | ||
92 | + | ||
93 | +func (repository *TodoRepository) FindOne(ctx context.Context, conn transaction.Conn, id int64) (*domain.Todo, error) { | ||
94 | + var ( | ||
95 | + err error | ||
96 | + tx = conn.DB() | ||
97 | + m = new(models.Todo) | ||
98 | + ) | ||
99 | + queryFunc := func() (interface{}, error) { | ||
100 | + tx = tx.Model(m).Where("id = ?", id).First(m) | ||
101 | + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { | ||
102 | + return nil, domain.ErrNotFound | ||
103 | + } | ||
104 | + return m, tx.Error | ||
105 | + } | ||
106 | + cacheModel := new(models.Todo) | ||
107 | + cacheModel.Id = id | ||
108 | + if err = repository.QueryCache(cacheModel.CacheKeyFunc, m, queryFunc); err != nil { | ||
109 | + return nil, err | ||
110 | + } | ||
111 | + return repository.ModelToDomainModel(m) | ||
112 | +} | ||
113 | + | ||
114 | +func (repository *TodoRepository) Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*domain.Todo, error) { | ||
115 | + var ( | ||
116 | + tx = conn.DB() | ||
117 | + ms []*models.Todo | ||
118 | + dms = make([]*domain.Todo, 0) | ||
119 | + total int64 | ||
120 | + ) | ||
121 | + queryFunc := func() (interface{}, error) { | ||
122 | + tx = tx.Model(&ms).Order("id desc") | ||
123 | + if total, tx = transaction.PaginationAndCount(ctx, tx, queryOptions, &ms); tx.Error != nil { | ||
124 | + return dms, tx.Error | ||
125 | + } | ||
126 | + return dms, nil | ||
127 | + } | ||
128 | + | ||
129 | + if _, err := repository.Query(queryFunc); err != nil { | ||
130 | + return 0, nil, err | ||
131 | + } | ||
132 | + | ||
133 | + for _, item := range ms { | ||
134 | + if dm, err := repository.ModelToDomainModel(item); err != nil { | ||
135 | + return 0, dms, err | ||
136 | + } else { | ||
137 | + dms = append(dms, dm) | ||
138 | + } | ||
139 | + } | ||
140 | + return total, dms, nil | ||
141 | +} | ||
142 | + | ||
143 | +func (repository *TodoRepository) ModelToDomainModel(from *models.Todo) (*domain.Todo, error) { | ||
144 | + to := &domain.Todo{} | ||
145 | + err := copier.Copy(to, from) | ||
146 | + return to, err | ||
147 | +} | ||
148 | + | ||
149 | +func (repository *TodoRepository) DomainModelToModel(from *domain.Todo) (*models.Todo, error) { | ||
150 | + to := &models.Todo{} | ||
151 | + err := copier.Copy(to, from) | ||
152 | + return to, err | ||
153 | +} | ||
154 | + | ||
155 | +func NewTodoRepository(cache *cache.CachedRepository) domain.TodoRepository { | ||
156 | + return &TodoRepository{CachedRepository: cache} | ||
157 | +} |
1 | +package transaction1 |
cmd/bsi/interanl/pkg/domain/repository.go
0 → 100644
1 | +package domain | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/transaction" | ||
6 | + "reflect" | ||
7 | +) | ||
8 | + | ||
9 | +func OffsetLimit(page, size int) (offset int, limit int) { | ||
10 | + if page == 0 { | ||
11 | + page = 1 | ||
12 | + } | ||
13 | + if size == 0 { | ||
14 | + size = 20 | ||
15 | + } | ||
16 | + offset = (page - 1) * size | ||
17 | + limit = size | ||
18 | + return | ||
19 | +} | ||
20 | + | ||
21 | +type QueryOptions map[string]interface{} | ||
22 | + | ||
23 | +func NewQueryOptions() QueryOptions { | ||
24 | + options := make(map[string]interface{}) | ||
25 | + return options | ||
26 | +} | ||
27 | +func (options QueryOptions) WithOffsetLimit(page, size int) QueryOptions { | ||
28 | + offset, limit := OffsetLimit(page, size) | ||
29 | + options["offset"] = offset | ||
30 | + options["limit"] = limit | ||
31 | + return options | ||
32 | +} | ||
33 | + | ||
34 | +func (options QueryOptions) WithKV(key string, value interface{}) QueryOptions { | ||
35 | + if reflect.ValueOf(value).IsZero() { | ||
36 | + return options | ||
37 | + } | ||
38 | + options[key] = value | ||
39 | + return options | ||
40 | +} | ||
41 | + | ||
42 | +func (options QueryOptions) MustWithKV(key string, value interface{}) QueryOptions { | ||
43 | + options[key] = value | ||
44 | + return options | ||
45 | +} | ||
46 | + | ||
47 | +func (options QueryOptions) Copy() QueryOptions { | ||
48 | + newOptions := NewQueryOptions() | ||
49 | + for k, v := range options { | ||
50 | + newOptions[k] = v | ||
51 | + } | ||
52 | + return newOptions | ||
53 | +} | ||
54 | + | ||
55 | +type IndexQueryOptionFunc func() QueryOptions | ||
56 | + | ||
57 | +// 自定义的一些查询条件 | ||
58 | + | ||
59 | +func (options QueryOptions) WithCountOnly() QueryOptions { | ||
60 | + options["countOnly"] = true | ||
61 | + return options | ||
62 | +} | ||
63 | +func (options QueryOptions) WithFindOnly() QueryOptions { | ||
64 | + options["findOnly"] = true | ||
65 | + return options | ||
66 | +} | ||
67 | + | ||
68 | +func LazyLoad[K comparable, T any](source map[K]T, ctx context.Context, conn transaction.Conn, k K, load func(context.Context, transaction.Conn, K) (T, error)) (T, error) { | ||
69 | + if v, ok := source[k]; ok { | ||
70 | + return v, nil | ||
71 | + } | ||
72 | + if v, err := load(ctx, conn, k); err != nil { | ||
73 | + return v, err | ||
74 | + } else { | ||
75 | + source[k] = v | ||
76 | + return v, nil | ||
77 | + } | ||
78 | +} | ||
79 | + | ||
80 | +func Values[T any, V any](list []T, each func(item T) V) []V { | ||
81 | + var result []V | ||
82 | + for _, item := range list { | ||
83 | + value := each(item) | ||
84 | + result = append(result, value) | ||
85 | + } | ||
86 | + return result | ||
87 | +} |
cmd/bsi/interanl/pkg/domain/todo.go
0 → 100644
1 | +package domain | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/transaction" | ||
6 | +) | ||
7 | + | ||
8 | +type Todo struct { | ||
9 | + Id int64 // 唯一标识 | ||
10 | + CreatedAt int64 | ||
11 | + UpdatedAt int64 | ||
12 | + DeletedAt int64 | ||
13 | + Version int | ||
14 | +} | ||
15 | + | ||
16 | +type TodoRepository interface { | ||
17 | + Insert(ctx context.Context, conn transaction.Conn, dm *Todo) (*Todo, error) | ||
18 | + Update(ctx context.Context, conn transaction.Conn, dm *Todo) (*Todo, error) | ||
19 | + UpdateWithVersion(ctx context.Context, conn transaction.Conn, dm *Todo) (*Todo, error) | ||
20 | + Delete(ctx context.Context, conn transaction.Conn, dm *Todo) (*Todo, error) | ||
21 | + FindOne(ctx context.Context, conn transaction.Conn, id int64) (*Todo, error) | ||
22 | + Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*Todo, error) | ||
23 | +} | ||
24 | + | ||
25 | +func (m *Todo) Identify() interface{} { | ||
26 | + if m.Id == 0 { | ||
27 | + return nil | ||
28 | + } | ||
29 | + return m.Id | ||
30 | +} |
cmd/bsi/interanl/pkg/domain/vars.go
0 → 100644
go.mod
0 → 100644
1 | +module gitlab.fjmaimaimai.com/allied-creation/sumifcc | ||
2 | + | ||
3 | +go 1.19 | ||
4 | + | ||
5 | +require ( | ||
6 | + github.com/golang-jwt/jwt/v4 v4.5.0 | ||
7 | + github.com/jinzhu/copier v0.4.0 | ||
8 | + github.com/jinzhu/now v1.1.5 | ||
9 | + github.com/mozillazg/go-pinyin v0.20.0 | ||
10 | + github.com/pkg/errors v0.9.1 | ||
11 | + github.com/stretchr/testify v1.8.4 | ||
12 | + github.com/tiptok/gocomm v1.0.14 | ||
13 | + github.com/zeromicro/go-zero v1.5.5 | ||
14 | + google.golang.org/grpc v1.57.0 | ||
15 | + gorm.io/driver/mysql v1.5.1 | ||
16 | + gorm.io/driver/postgres v1.5.2 | ||
17 | + gorm.io/gorm v1.25.4 | ||
18 | + gorm.io/plugin/soft_delete v1.2.1 | ||
19 | +) | ||
20 | + | ||
21 | +require ( | ||
22 | + github.com/Shopify/sarama v1.37.2 // indirect | ||
23 | + github.com/beego/beego/v2 v2.0.1 // indirect | ||
24 | + github.com/beorn7/perks v1.0.1 // indirect | ||
25 | + github.com/cenkalti/backoff/v4 v4.2.0 // indirect | ||
26 | + github.com/cespare/xxhash/v2 v2.2.0 // indirect | ||
27 | + github.com/davecgh/go-spew v1.1.1 // indirect | ||
28 | + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect | ||
29 | + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | ||
30 | + github.com/eapache/go-resiliency v1.3.0 // indirect | ||
31 | + github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect | ||
32 | + github.com/eapache/queue v1.1.0 // indirect | ||
33 | + github.com/fatih/color v1.15.0 // indirect | ||
34 | + github.com/fsnotify/fsnotify v1.4.9 // indirect | ||
35 | + github.com/garyburd/redigo v1.6.3 // indirect | ||
36 | + github.com/gin-contrib/sse v0.1.0 // indirect | ||
37 | + github.com/gin-gonic/gin v1.5.0 // indirect | ||
38 | + github.com/go-logr/logr v1.2.3 // indirect | ||
39 | + github.com/go-logr/stdr v1.2.2 // indirect | ||
40 | + github.com/go-playground/locales v0.12.1 // indirect | ||
41 | + github.com/go-playground/universal-translator v0.16.0 // indirect | ||
42 | + github.com/go-redis/redis/v8 v8.11.5 // indirect | ||
43 | + github.com/go-sql-driver/mysql v1.7.1 // indirect | ||
44 | + github.com/golang/protobuf v1.5.3 // indirect | ||
45 | + github.com/golang/snappy v0.0.4 // indirect | ||
46 | + github.com/google/uuid v1.3.0 // indirect | ||
47 | + github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 // indirect | ||
48 | + github.com/hashicorp/errwrap v1.1.0 // indirect | ||
49 | + github.com/hashicorp/go-multierror v1.1.1 // indirect | ||
50 | + github.com/hashicorp/go-uuid v1.0.3 // indirect | ||
51 | + github.com/hashicorp/hcl v1.0.0 // indirect | ||
52 | + github.com/jackc/pgpassfile v1.0.0 // indirect | ||
53 | + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect | ||
54 | + github.com/jackc/pgx/v5 v5.4.3 // indirect | ||
55 | + github.com/jcmturner/aescts/v2 v2.0.0 // indirect | ||
56 | + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect | ||
57 | + github.com/jcmturner/gofork v1.7.6 // indirect | ||
58 | + github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect | ||
59 | + github.com/jcmturner/rpc/v2 v2.0.3 // indirect | ||
60 | + github.com/jinzhu/inflection v1.0.0 // indirect | ||
61 | + github.com/json-iterator/go v1.1.12 // indirect | ||
62 | + github.com/klauspost/compress v1.15.15 // indirect | ||
63 | + github.com/leodido/go-urn v1.1.0 // indirect | ||
64 | + github.com/magiconair/properties v1.8.0 // indirect | ||
65 | + github.com/mattn/go-colorable v0.1.13 // indirect | ||
66 | + github.com/mattn/go-isatty v0.0.17 // indirect | ||
67 | + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect | ||
68 | + github.com/mitchellh/mapstructure v1.3.3 // indirect | ||
69 | + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||
70 | + github.com/modern-go/reflect2 v1.0.2 // indirect | ||
71 | + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect | ||
72 | + github.com/onsi/gomega v1.26.0 // indirect | ||
73 | + github.com/openzipkin/zipkin-go v0.4.1 // indirect | ||
74 | + github.com/pelletier/go-toml v1.8.1 // indirect | ||
75 | + github.com/pelletier/go-toml/v2 v2.0.9 // indirect | ||
76 | + github.com/pierrec/lz4/v4 v4.1.17 // indirect | ||
77 | + github.com/pmezard/go-difflib v1.0.0 // indirect | ||
78 | + github.com/prometheus/client_golang v1.16.0 // indirect | ||
79 | + github.com/prometheus/client_model v0.3.0 // indirect | ||
80 | + github.com/prometheus/common v0.42.0 // indirect | ||
81 | + github.com/prometheus/procfs v0.10.1 // indirect | ||
82 | + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect | ||
83 | + github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect | ||
84 | + github.com/spaolacci/murmur3 v1.1.0 // indirect | ||
85 | + github.com/spf13/afero v1.2.2 // indirect | ||
86 | + github.com/spf13/cast v1.4.1 // indirect | ||
87 | + github.com/spf13/jwalterweatherman v1.0.0 // indirect | ||
88 | + github.com/spf13/pflag v1.0.5 // indirect | ||
89 | + github.com/spf13/viper v1.4.0 // indirect | ||
90 | + github.com/ugorji/go/codec v1.1.7 // indirect | ||
91 | + go.opentelemetry.io/otel v1.14.0 // indirect | ||
92 | + go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect | ||
93 | + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect | ||
94 | + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect | ||
95 | + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect | ||
96 | + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0 // indirect | ||
97 | + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 // indirect | ||
98 | + go.opentelemetry.io/otel/exporters/zipkin v1.14.0 // indirect | ||
99 | + go.opentelemetry.io/otel/sdk v1.14.0 // indirect | ||
100 | + go.opentelemetry.io/otel/trace v1.14.0 // indirect | ||
101 | + go.opentelemetry.io/proto/otlp v0.19.0 // indirect | ||
102 | + go.uber.org/automaxprocs v1.5.3 // indirect | ||
103 | + golang.org/x/crypto v0.12.0 // indirect | ||
104 | + golang.org/x/net v0.14.0 // indirect | ||
105 | + golang.org/x/sys v0.11.0 // indirect | ||
106 | + golang.org/x/text v0.12.0 // indirect | ||
107 | + google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect | ||
108 | + google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect | ||
109 | + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect | ||
110 | + google.golang.org/protobuf v1.31.0 // indirect | ||
111 | + gopkg.in/go-playground/validator.v9 v9.29.1 // indirect | ||
112 | + gopkg.in/yaml.v2 v2.4.0 // indirect | ||
113 | + gopkg.in/yaml.v3 v3.0.1 // indirect | ||
114 | +) |
pkg/cache/multi_level_cache.go
0 → 100644
1 | +package cache | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/tiptok/gocomm/pkg/cache" | ||
5 | + "github.com/tiptok/gocomm/pkg/cache/gzcache" | ||
6 | + "github.com/tiptok/gocomm/pkg/log" | ||
7 | + "github.com/zeromicro/go-zero/core/logx" | ||
8 | +) | ||
9 | + | ||
10 | +func NewMultiLevelCache(hosts []string, password string) *cache.MultiLevelCache { | ||
11 | + logx.Infof("starting multi level cache...") | ||
12 | + mlCache := cache.NewMultiLevelCacheNew(cache.WithDebugLog(true, func() log.Log { | ||
13 | + return log.DefaultLog | ||
14 | + })) | ||
15 | + mlCache.RegisterCache(gzcache.NewClusterCache(hosts, password)) | ||
16 | + return mlCache | ||
17 | +} | ||
18 | + | ||
19 | +func NewCachedRepository(c *cache.MultiLevelCache, options ...cache.QueryOption) *cache.CachedRepository { | ||
20 | + return cache.NewCachedRepository(c, options...) | ||
21 | +} |
pkg/config/config.go
0 → 100644
1 | +package config | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/zeromicro/go-zero/core/stores/cache" | ||
5 | +) | ||
6 | + | ||
7 | +type Auth struct { | ||
8 | + AccessSecret string | ||
9 | + AccessExpire int64 | ||
10 | +} | ||
11 | +type Config struct { | ||
12 | + DB struct { | ||
13 | + DataSource string `json:",env=DataSource"` | ||
14 | + } `json:",optional"` | ||
15 | + Cache cache.CacheConf `json:",optional"` | ||
16 | + DTM DTM `json:",optional"` | ||
17 | + Sms Sms `json:",optional"` | ||
18 | + Oss Oss `json:",optional"` | ||
19 | + Wechat Wechat `json:",optional"` | ||
20 | +} | ||
21 | + | ||
22 | +type DTM struct { | ||
23 | + Server Server `json:",optional"` | ||
24 | +} | ||
25 | + | ||
26 | +type Server struct { | ||
27 | + Name string `json:",optional"` | ||
28 | + Host string `json:",optional"` | ||
29 | + GRPC GRPC `json:",optional"` | ||
30 | + HTTP HTTP `json:",optional"` | ||
31 | + Metrics Metrics `json:",optional"` | ||
32 | +} | ||
33 | + | ||
34 | +type HTTP struct { | ||
35 | + Port string | ||
36 | +} | ||
37 | + | ||
38 | +type GRPC struct { | ||
39 | + Port string | ||
40 | +} | ||
41 | + | ||
42 | +type Metrics struct { | ||
43 | + Port string | ||
44 | +} | ||
45 | + | ||
46 | +type Sms struct { | ||
47 | + Debug bool | ||
48 | + DebugCode string | ||
49 | + Expire int `json:",default=180"` | ||
50 | + MaxSendTime int `json:",default=5"` | ||
51 | + CompanyName string | ||
52 | + SecretId string | ||
53 | + SecretKey string | ||
54 | + SmsAppId string | ||
55 | + Sign string | ||
56 | + TemplateId string | ||
57 | +} | ||
58 | + | ||
59 | +type Oss struct { | ||
60 | + OssEndPoint string | ||
61 | + AccessKeyID string | ||
62 | + AccessKeySecret string | ||
63 | + BuckName string | ||
64 | + | ||
65 | + RegionID string | ||
66 | + RoleArn string | ||
67 | + | ||
68 | + CDN CDN | ||
69 | +} | ||
70 | + | ||
71 | +type Wechat struct { | ||
72 | + AppName string `json:",optional"` | ||
73 | + AppID string | ||
74 | + AppSecret string | ||
75 | + QrcodeEnv string `json:",optional,default=release"` | ||
76 | + MsgTemplates []Template `json:",optional"` | ||
77 | +} | ||
78 | + | ||
79 | +func (wx Wechat) GetTemplate(code string) (Template, bool) { | ||
80 | + for _, temp := range wx.MsgTemplates { | ||
81 | + if temp.Code == code { | ||
82 | + return temp, true | ||
83 | + } | ||
84 | + } | ||
85 | + return Template{}, false | ||
86 | +} | ||
87 | + | ||
88 | +type CDN struct { | ||
89 | + HostPairs []string | ||
90 | +} | ||
91 | + | ||
92 | +type Template struct { | ||
93 | + ID string // 模板ID | ||
94 | + Name string // 模板名称 | ||
95 | + Code string // 模板编码 | ||
96 | +} |
pkg/contextdata/user_token.go
0 → 100644
1 | +package contextdata | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "encoding/json" | ||
6 | + "github.com/golang-jwt/jwt/v4" | ||
7 | + "github.com/zeromicro/go-zero/core/logx" | ||
8 | + "time" | ||
9 | +) | ||
10 | + | ||
11 | +var ( | ||
12 | + CtxKeyJwtUserId = "userId" | ||
13 | + CtxKeyJwtCompanyId = "companyId" | ||
14 | +) | ||
15 | + | ||
16 | +func GetInt64FromCtx(ctx context.Context, key string) int64 { | ||
17 | + var uid int64 | ||
18 | + if jsonUid, ok := ctx.Value(key).(json.Number); ok { | ||
19 | + if int64Uid, err := jsonUid.Int64(); err == nil { | ||
20 | + uid = int64Uid | ||
21 | + } else { | ||
22 | + logx.WithContext(ctx).Errorf("GetUidFromCtx err : %+v", err) | ||
23 | + } | ||
24 | + } | ||
25 | + return uid | ||
26 | +} | ||
27 | + | ||
28 | +func getStringFromCtx(ctx context.Context, key string) string { | ||
29 | + var uid string | ||
30 | + if jsonUid, ok := ctx.Value(key).(string); ok { | ||
31 | + return jsonUid | ||
32 | + } | ||
33 | + return uid | ||
34 | +} | ||
35 | + | ||
36 | +func getArrayInt64FromCtx(ctx context.Context, key string) []int64 { | ||
37 | + values := ctx.Value(key) | ||
38 | + var ids = make([]int64, 0) | ||
39 | + if values == nil { | ||
40 | + return ids | ||
41 | + } | ||
42 | + if list, ok := values.([]interface{}); ok { | ||
43 | + for _, item := range list { | ||
44 | + if jsonId, ok := item.(json.Number); ok { | ||
45 | + id, _ := jsonId.Int64() | ||
46 | + ids = append(ids, id) | ||
47 | + } | ||
48 | + } | ||
49 | + } | ||
50 | + return ids | ||
51 | +} | ||
52 | + | ||
53 | +func GetUserTokenFromCtx(ctx context.Context) UserToken { | ||
54 | + return UserToken{ | ||
55 | + UserId: GetInt64FromCtx(ctx, CtxKeyJwtUserId), | ||
56 | + CompanyId: GetInt64FromCtx(ctx, CtxKeyJwtCompanyId), | ||
57 | + } | ||
58 | +} | ||
59 | + | ||
60 | +type UserToken struct { | ||
61 | + UserId int64 | ||
62 | + CompanyId int64 | ||
63 | +} | ||
64 | + | ||
65 | +func (tk UserToken) GenerateToken(secret string, expire int64) (string, error) { | ||
66 | + claims := make(jwt.MapClaims) | ||
67 | + claims["exp"] = time.Now().Unix() + expire | ||
68 | + claims["iat"] = time.Now().Unix() | ||
69 | + claims["UserId"] = tk.UserId | ||
70 | + token := jwt.New(jwt.SigningMethodHS256) | ||
71 | + token.Claims = claims | ||
72 | + | ||
73 | + return token.SignedString([]byte(secret)) | ||
74 | +} |
pkg/database/db.go
0 → 100644
1 | +package database | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "fmt" | ||
6 | + "log" | ||
7 | + "os" | ||
8 | + "time" | ||
9 | + | ||
10 | + "github.com/zeromicro/go-zero/core/logx" | ||
11 | + "gorm.io/driver/mysql" | ||
12 | + "gorm.io/driver/postgres" | ||
13 | + "gorm.io/gorm" | ||
14 | + "gorm.io/gorm/logger" | ||
15 | +) | ||
16 | + | ||
17 | +func OpenGormDB(source string) *gorm.DB { | ||
18 | + newLogger := logger.New( | ||
19 | + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer | ||
20 | + logger.Config{ | ||
21 | + SlowThreshold: time.Second, // Slow SQL threshold | ||
22 | + LogLevel: logger.Info, // Log level | ||
23 | + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger | ||
24 | + Colorful: false, // Disable color | ||
25 | + }, | ||
26 | + ) | ||
27 | + fmt.Println("starting db...") | ||
28 | + db, err := gorm.Open(mysql.Open(source), &gorm.Config{ | ||
29 | + Logger: newLogger, | ||
30 | + }) | ||
31 | + if err != nil { | ||
32 | + panic(err) | ||
33 | + } | ||
34 | + return db | ||
35 | +} | ||
36 | + | ||
37 | +func OpenGormPGDB(source string, logMode string) *gorm.DB { | ||
38 | + logx.Infof("starting db...") | ||
39 | + db, err := gorm.Open(postgres.New(postgres.Config{ | ||
40 | + DSN: source, | ||
41 | + PreferSimpleProtocol: true, // disables implicit prepared statement usage | ||
42 | + }), &gorm.Config{ | ||
43 | + Logger: NewLogger(logMode), //newLogger, | ||
44 | + }) | ||
45 | + if err != nil { | ||
46 | + panic(err) | ||
47 | + } | ||
48 | + | ||
49 | + return db | ||
50 | +} | ||
51 | + | ||
52 | +func NewLogger(logType string) logger.Interface { | ||
53 | + if logType == "console" { | ||
54 | + return logger.New( | ||
55 | + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer | ||
56 | + logger.Config{ | ||
57 | + SlowThreshold: time.Second, // Slow SQL threshold | ||
58 | + LogLevel: logger.Info, // Log level | ||
59 | + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger | ||
60 | + Colorful: false, // Disable color | ||
61 | + }, | ||
62 | + ) | ||
63 | + } | ||
64 | + return ZeroLog{} | ||
65 | +} | ||
66 | + | ||
67 | +type ZeroLog struct { | ||
68 | +} | ||
69 | + | ||
70 | +func (l ZeroLog) LogMode(logger.LogLevel) logger.Interface { | ||
71 | + return l | ||
72 | +} | ||
73 | +func (l ZeroLog) Info(ctx context.Context, s string, values ...interface{}) { | ||
74 | + logx.Infof(s, values...) | ||
75 | +} | ||
76 | +func (l ZeroLog) Warn(ctx context.Context, s string, values ...interface{}) { | ||
77 | + logx.Errorf(s, values...) | ||
78 | +} | ||
79 | +func (l ZeroLog) Error(ctx context.Context, s string, values ...interface{}) { | ||
80 | + logx.Errorf(s, values...) | ||
81 | +} | ||
82 | +func (l ZeroLog) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { | ||
83 | + now := time.Now() | ||
84 | + sql, rows := fc() | ||
85 | + logx.Infof("[%v] [rows:%v] %s", now.Sub(begin).String(), rows, sql) | ||
86 | +} |
pkg/database/partition.go
0 → 100644
1 | +package database | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | + "github.com/jinzhu/now" | ||
6 | + "github.com/zeromicro/go-zero/core/stores/redis" | ||
7 | + "gorm.io/gorm" | ||
8 | + "gorm.io/gorm/clause" | ||
9 | + "gorm.io/gorm/migrator" | ||
10 | + "gorm.io/gorm/schema" | ||
11 | + "strings" | ||
12 | + "time" | ||
13 | +) | ||
14 | + | ||
15 | +var ( | ||
16 | + // PartitionByRangeTime 按unix时间戳分区 | ||
17 | + PartitionByRangeTime = 1 | ||
18 | + // PartitionByHash 按系统的hash值分区 | ||
19 | + PartitionByHash = 2 | ||
20 | + // PartitionByList 按List包含值分区 | ||
21 | + PartitionByList = 3 | ||
22 | +) | ||
23 | + | ||
24 | +type PartitionTable interface { | ||
25 | + TableName() string | ||
26 | +} | ||
27 | + | ||
28 | +type PartitionMigrator struct { | ||
29 | + ServiceName string | ||
30 | + DB *gorm.DB | ||
31 | + Redis *redis.Redis | ||
32 | +} | ||
33 | + | ||
34 | +func NewPartitionMigrator(serviceName string, db *gorm.DB, redis *redis.Redis) *PartitionMigrator { | ||
35 | + return &PartitionMigrator{ | ||
36 | + DB: db, | ||
37 | + ServiceName: serviceName, | ||
38 | + Redis: redis, | ||
39 | + } | ||
40 | +} | ||
41 | + | ||
42 | +func (c *PartitionMigrator) AutoMigrate(t PartitionTable, option ...PartitionOptionFunc) error { | ||
43 | + options := NewPartitionOptions() | ||
44 | + for i := range option { | ||
45 | + option[i](options) | ||
46 | + } | ||
47 | + | ||
48 | + tableName := t.TableName() | ||
49 | + if !c.DB.Migrator().HasTable(tableName) { | ||
50 | + migrator := Migrator{migrator.Migrator{ | ||
51 | + migrator.Config{ | ||
52 | + CreateIndexAfterCreateTable: true, | ||
53 | + DB: c.DB, | ||
54 | + Dialector: c.DB.Dialector, | ||
55 | + }, | ||
56 | + }} | ||
57 | + if err := migrator.CreatePartitionTable(options, t); err != nil { | ||
58 | + panic(err) | ||
59 | + } | ||
60 | + } | ||
61 | + | ||
62 | + rk := fmt.Sprintf("%s:auto-partition:%s", c.ServiceName, tableName) | ||
63 | + lock := redis.NewRedisLock(c.Redis, rk) | ||
64 | + ok, err := lock.Acquire() | ||
65 | + if !ok || err != nil { | ||
66 | + return nil | ||
67 | + } | ||
68 | + defer lock.Release() | ||
69 | + switch options.Type { | ||
70 | + case PartitionByRangeTime: | ||
71 | + begin := options.TimeBegin | ||
72 | + end := options.TimeEnd | ||
73 | + for { | ||
74 | + if begin.Unix() > end.Unix() { | ||
75 | + break | ||
76 | + } | ||
77 | + pTable := fmt.Sprintf("%s_%s", tableName, options.FormatTimeSubFunc(begin)) | ||
78 | + sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES FROM (%d) TO (%d);", | ||
79 | + pTable, tableName, begin.Unix(), begin.AddDate(0, options.TimeSpanMonth, 0).Unix()) | ||
80 | + tx := c.DB.Exec(sql) | ||
81 | + if tx.Error != nil { | ||
82 | + return tx.Error | ||
83 | + } | ||
84 | + c.log(t, pTable) | ||
85 | + begin = begin.AddDate(0, options.TimeSpanMonth, 0) | ||
86 | + } | ||
87 | + break | ||
88 | + case PartitionByHash: | ||
89 | + for i := 0; i < options.Modulus; i++ { | ||
90 | + pTable := fmt.Sprintf("%s_%d", tableName, i) | ||
91 | + sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES WITH (MODULUS %d, REMAINDER %d);", | ||
92 | + pTable, tableName, options.Modulus, i) | ||
93 | + tx := c.DB.Exec(sql) | ||
94 | + if tx.Error != nil { | ||
95 | + return tx.Error | ||
96 | + } | ||
97 | + c.log(t, pTable) | ||
98 | + } | ||
99 | + break | ||
100 | + case PartitionByList: | ||
101 | + for i := 0; i < len(options.ListRange); i++ { | ||
102 | + pTable := fmt.Sprintf("%s_%d", tableName, i) | ||
103 | + sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES IN %s;", | ||
104 | + pTable, tableName, InArgs(options.ListRange[i])) | ||
105 | + tx := c.DB.Exec(sql) | ||
106 | + if tx.Error != nil { | ||
107 | + return tx.Error | ||
108 | + } | ||
109 | + c.log(t, pTable) | ||
110 | + } | ||
111 | + break | ||
112 | + default: | ||
113 | + return nil | ||
114 | + } | ||
115 | + | ||
116 | + return nil | ||
117 | +} | ||
118 | + | ||
119 | +func (c *PartitionMigrator) log(t PartitionTable, pTable string) { | ||
120 | + fmt.Println("【自动分区】 create partition table", pTable, "on table", t.TableName()) | ||
121 | +} | ||
122 | + | ||
123 | +type PartitionOptions struct { | ||
124 | + // 分区类型 1:Hash 2:RangeTime | ||
125 | + Type int | ||
126 | + // 分区列 | ||
127 | + Column string | ||
128 | + | ||
129 | + // Hash分区 | ||
130 | + Modulus int | ||
131 | + | ||
132 | + // List 范围 | ||
133 | + ListRange []interface{} | ||
134 | + | ||
135 | + // Range时间分区 | ||
136 | + TimeBegin time.Time | ||
137 | + TimeEnd time.Time | ||
138 | + TimeSpanMonth int | ||
139 | + FormatTimeSubFunc func(time.Time) string | ||
140 | + | ||
141 | + // 禁用PrimaryKey生成 | ||
142 | + // 分区字段有函数表达式的,需要禁用掉PrimaryKey,使用自定义的唯一ID生成规则 | ||
143 | + DisablePrimaryKey bool | ||
144 | +} | ||
145 | + | ||
146 | +func NewPartitionOptions() *PartitionOptions { | ||
147 | + return &PartitionOptions{ | ||
148 | + Type: PartitionByRangeTime, | ||
149 | + FormatTimeSubFunc: func(t time.Time) string { | ||
150 | + return t.Format("200601") | ||
151 | + }, | ||
152 | + } | ||
153 | +} | ||
154 | + | ||
155 | +func (c *PartitionOptions) Sql() string { | ||
156 | + if c.Type == PartitionByHash { | ||
157 | + return fmt.Sprintf("PARTITION BY HASH(%s)", c.Column) | ||
158 | + } | ||
159 | + if c.Type == PartitionByRangeTime { | ||
160 | + return fmt.Sprintf("PARTITION BY RANGE(%s)", c.Column) | ||
161 | + } | ||
162 | + if c.Type == PartitionByList { | ||
163 | + return fmt.Sprintf("PARTITION BY LIST(%s)", c.Column) | ||
164 | + } | ||
165 | + return "" | ||
166 | +} | ||
167 | + | ||
168 | +type PartitionOptionFunc func(*PartitionOptions) | ||
169 | + | ||
170 | +func WithPartitionType(t int) PartitionOptionFunc { | ||
171 | + return func(options *PartitionOptions) { | ||
172 | + options.Type = t | ||
173 | + } | ||
174 | +} | ||
175 | + | ||
176 | +func WithPartitionColumn(c string) PartitionOptionFunc { | ||
177 | + return func(options *PartitionOptions) { | ||
178 | + options.Column = c | ||
179 | + } | ||
180 | +} | ||
181 | + | ||
182 | +func WithPartitionHash(modulus int) PartitionOptionFunc { | ||
183 | + return func(options *PartitionOptions) { | ||
184 | + options.Modulus = modulus | ||
185 | + } | ||
186 | +} | ||
187 | + | ||
188 | +func WithPartitionRangeTime(begin, end time.Time, spanMonth int) PartitionOptionFunc { | ||
189 | + return func(options *PartitionOptions) { | ||
190 | + options.TimeBegin = begin | ||
191 | + options.TimeEnd = end | ||
192 | + options.TimeSpanMonth = spanMonth | ||
193 | + } | ||
194 | +} | ||
195 | + | ||
196 | +func WithPartitionList(list ...interface{}) PartitionOptionFunc { | ||
197 | + return func(options *PartitionOptions) { | ||
198 | + options.ListRange = list | ||
199 | + } | ||
200 | +} | ||
201 | + | ||
202 | +func WithDisablePrimaryKey(disablePrimaryKey bool) PartitionOptionFunc { | ||
203 | + return func(options *PartitionOptions) { | ||
204 | + options.DisablePrimaryKey = disablePrimaryKey | ||
205 | + } | ||
206 | +} | ||
207 | + | ||
208 | +func Date(date string) time.Time { | ||
209 | + return now.MustParse(date) | ||
210 | +} | ||
211 | + | ||
212 | +type Migrator struct { | ||
213 | + migrator.Migrator | ||
214 | +} | ||
215 | + | ||
216 | +// CreatePartitionTable create table in database for values | ||
217 | +func (m Migrator) CreatePartitionTable(options *PartitionOptions, values ...interface{}) error { | ||
218 | + for _, value := range m.ReorderModels(values, false) { | ||
219 | + tx := m.DB.Session(&gorm.Session{}) | ||
220 | + if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) { | ||
221 | + var ( | ||
222 | + createTableSQL = "CREATE TABLE ? (" | ||
223 | + values = []interface{}{m.CurrentTable(stmt)} | ||
224 | + hasPrimaryKeyInDataType bool | ||
225 | + ) | ||
226 | + | ||
227 | + for _, dbName := range stmt.Schema.DBNames { | ||
228 | + field := stmt.Schema.FieldsByDBName[dbName] | ||
229 | + if !field.IgnoreMigration { | ||
230 | + createTableSQL += "? ?" | ||
231 | + hasPrimaryKeyInDataType = hasPrimaryKeyInDataType || strings.Contains(strings.ToUpper(string(field.DataType)), "PRIMARY KEY") | ||
232 | + values = append(values, clause.Column{Name: dbName}, m.DB.Migrator().FullDataTypeOf(field)) | ||
233 | + createTableSQL += "," | ||
234 | + } | ||
235 | + } | ||
236 | + | ||
237 | + if !hasPrimaryKeyInDataType && len(stmt.Schema.PrimaryFields) > 0 && !options.DisablePrimaryKey { | ||
238 | + createTableSQL += "PRIMARY KEY ?," | ||
239 | + primaryKeys := []interface{}{} | ||
240 | + for _, field := range stmt.Schema.PrimaryFields { | ||
241 | + primaryKeys = append(primaryKeys, clause.Column{Name: field.DBName}) | ||
242 | + } | ||
243 | + | ||
244 | + values = append(values, primaryKeys) | ||
245 | + } | ||
246 | + | ||
247 | + for _, idx := range stmt.Schema.ParseIndexes() { | ||
248 | + if m.CreateIndexAfterCreateTable { | ||
249 | + defer func(value interface{}, name string) { | ||
250 | + if errr == nil { | ||
251 | + errr = tx.Migrator().CreateIndex(value, name) | ||
252 | + } | ||
253 | + }(value, idx.Name) | ||
254 | + } else { | ||
255 | + if idx.Class != "" { | ||
256 | + createTableSQL += idx.Class + " " | ||
257 | + } | ||
258 | + createTableSQL += "INDEX ? ?" | ||
259 | + | ||
260 | + if idx.Comment != "" { | ||
261 | + createTableSQL += fmt.Sprintf(" COMMENT '%s'", idx.Comment) | ||
262 | + } | ||
263 | + | ||
264 | + if idx.Option != "" { | ||
265 | + createTableSQL += " " + idx.Option | ||
266 | + } | ||
267 | + | ||
268 | + createTableSQL += "," | ||
269 | + values = append(values, clause.Column{Name: idx.Name}, tx.Migrator().(migrator.BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt)) | ||
270 | + } | ||
271 | + } | ||
272 | + | ||
273 | + if !m.DB.DisableForeignKeyConstraintWhenMigrating && !m.DB.IgnoreRelationshipsWhenMigrating { | ||
274 | + for _, rel := range stmt.Schema.Relationships.Relations { | ||
275 | + if rel.Field.IgnoreMigration { | ||
276 | + continue | ||
277 | + } | ||
278 | + if constraint := rel.ParseConstraint(); constraint != nil { | ||
279 | + if constraint.Schema == stmt.Schema { | ||
280 | + sql, vars := buildConstraint(constraint) | ||
281 | + createTableSQL += sql + "," | ||
282 | + values = append(values, vars...) | ||
283 | + } | ||
284 | + } | ||
285 | + } | ||
286 | + } | ||
287 | + | ||
288 | + for _, chk := range stmt.Schema.ParseCheckConstraints() { | ||
289 | + createTableSQL += "CONSTRAINT ? CHECK (?)," | ||
290 | + values = append(values, clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint}) | ||
291 | + } | ||
292 | + | ||
293 | + createTableSQL = strings.TrimSuffix(createTableSQL, ",") | ||
294 | + | ||
295 | + createTableSQL += ")" | ||
296 | + | ||
297 | + if options != nil { | ||
298 | + createTableSQL += options.Sql() | ||
299 | + } | ||
300 | + | ||
301 | + if tableOption, ok := m.DB.Get("gorm:table_options"); ok { | ||
302 | + createTableSQL += fmt.Sprint(tableOption) | ||
303 | + } | ||
304 | + | ||
305 | + errr = tx.Exec(createTableSQL, values...).Error | ||
306 | + return errr | ||
307 | + }); err != nil { | ||
308 | + return err | ||
309 | + } | ||
310 | + } | ||
311 | + return nil | ||
312 | +} | ||
313 | + | ||
314 | +func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) { | ||
315 | + sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??" | ||
316 | + if constraint.OnDelete != "" { | ||
317 | + sql += " ON DELETE " + constraint.OnDelete | ||
318 | + } | ||
319 | + | ||
320 | + if constraint.OnUpdate != "" { | ||
321 | + sql += " ON UPDATE " + constraint.OnUpdate | ||
322 | + } | ||
323 | + | ||
324 | + var foreignKeys, references []interface{} | ||
325 | + for _, field := range constraint.ForeignKeys { | ||
326 | + foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName}) | ||
327 | + } | ||
328 | + | ||
329 | + for _, field := range constraint.References { | ||
330 | + references = append(references, clause.Column{Name: field.DBName}) | ||
331 | + } | ||
332 | + results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references) | ||
333 | + return | ||
334 | +} |
pkg/database/query.go
0 → 100644
1 | +package database | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/zeromicro/go-zero/core/mapping" | ||
5 | + "reflect" | ||
6 | +) | ||
7 | + | ||
8 | +func InArgs(args interface{}) string { | ||
9 | + bytes := make([]byte, 0) | ||
10 | + bytes = appendIn(bytes, reflect.ValueOf(args)) | ||
11 | + return string(bytes) | ||
12 | +} | ||
13 | + | ||
14 | +func Arg(args interface{}) string { | ||
15 | + bytes := make([]byte, 0) | ||
16 | + v := reflect.ValueOf(args) | ||
17 | + bytes = appendValue(bytes, v) | ||
18 | + return string(bytes) | ||
19 | +} | ||
20 | + | ||
21 | +func appendIn(b []byte, slice reflect.Value) []byte { | ||
22 | + sliceLen := slice.Len() | ||
23 | + b = append(b, '(') | ||
24 | + for i := 0; i < sliceLen; i++ { | ||
25 | + if i > 0 { | ||
26 | + b = append(b, ',') | ||
27 | + } | ||
28 | + | ||
29 | + elem := slice.Index(i) | ||
30 | + if elem.Kind() == reflect.Interface { | ||
31 | + elem = elem.Elem() | ||
32 | + } | ||
33 | + if elem.Kind() == reflect.Slice { | ||
34 | + //b = appendIn(b, elem) | ||
35 | + } else { | ||
36 | + b = appendValue(b, elem) | ||
37 | + } | ||
38 | + } | ||
39 | + b = append(b, ')') | ||
40 | + return b | ||
41 | +} | ||
42 | + | ||
43 | +func appendValue(b []byte, v reflect.Value) []byte { | ||
44 | + if v.Kind() == reflect.Ptr && v.IsNil() { | ||
45 | + | ||
46 | + return append(b, "NULL"...) | ||
47 | + } | ||
48 | + if v.Kind() == reflect.Int || v.Kind() == reflect.Int64 || v.Kind() == reflect.Float64 { | ||
49 | + return append(b, []byte(mapping.Repr(v.Interface()))...) | ||
50 | + } | ||
51 | + b = append(b, []byte("'")...) | ||
52 | + b = append(b, []byte(mapping.Repr(v.Interface()))...) | ||
53 | + b = append(b, []byte("'")...) | ||
54 | + return b | ||
55 | +} |
pkg/database/shardingsphere.go
0 → 100644
1 | +package database |
pkg/gateway/authlib/api_auth_lib.go
0 → 100644
1 | +package authlib | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway" | ||
6 | + "net/http" | ||
7 | +) | ||
8 | + | ||
9 | +type ApiAuthService struct { | ||
10 | + gateway.Service | ||
11 | +} | ||
12 | + | ||
13 | +func (svc *ApiAuthService) MeInfo(ctx context.Context, request RequestUserMeQuery) (*DataUserMe, error) { | ||
14 | + var result DataUserMe | ||
15 | + if err := svc.Do(ctx, "/v1/user/me", http.MethodGet, request, &result); err != nil { | ||
16 | + return nil, err | ||
17 | + } | ||
18 | + return &result, nil | ||
19 | +} | ||
20 | + | ||
21 | +func (svc *ApiAuthService) MeAppInfo(ctx context.Context, request RequestUserMeQuery) (*DataUserAppInfo, error) { | ||
22 | + var result DataUserAppInfo | ||
23 | + if err := svc.Do(ctx, "/v1/user/me-app-info", http.MethodGet, request, &result); err != nil { | ||
24 | + return nil, err | ||
25 | + } | ||
26 | + return &result, nil | ||
27 | +} | ||
28 | + | ||
29 | +func (svc *ApiAuthService) LoginCheck(ctx context.Context, request RequestLoginCheck) (*DataLoginCheck, error) { | ||
30 | + var ( | ||
31 | + result DataLoginCheck | ||
32 | + err error | ||
33 | + ) | ||
34 | + if err = svc.Do(ctx, "/v1/login/check?token="+request.Token, http.MethodGet, request, &result); err != nil { | ||
35 | + return nil, err | ||
36 | + } | ||
37 | + if errCodeMsg, ok := err.(gateway.HttpError); ok { | ||
38 | + return &DataLoginCheck{ | ||
39 | + Code: errCodeMsg.Base.Code, | ||
40 | + Msg: errCodeMsg.Base.Msg, | ||
41 | + }, nil | ||
42 | + } | ||
43 | + return &result, nil | ||
44 | +} | ||
45 | + | ||
46 | +func (svc *ApiAuthService) AppLogin(ctx context.Context, request RequestAppLogin) (*DataAppLogin, error) { | ||
47 | + var result DataAppLogin | ||
48 | + if err := svc.Do(ctx, "/v1/login/check?token="+request.Token, http.MethodGet, request, &result); err != nil { | ||
49 | + return nil, err | ||
50 | + } | ||
51 | + return &result, nil | ||
52 | +} |
pkg/gateway/authlib/types.go
0 → 100644
1 | +package authlib | ||
2 | + | ||
3 | +type RequestUserMeQuery struct { | ||
4 | + Token string `header:"x-mmm-accesstoken"` | ||
5 | + //UserId int | ||
6 | + //CompanyId int | ||
7 | +} | ||
8 | + | ||
9 | +type DataUserMe struct { | ||
10 | + User *struct { | ||
11 | + ID string `json:"id"` | ||
12 | + Phone string `json:"phone"` | ||
13 | + NickName string `json:"nickName"` | ||
14 | + Avatar string `json:"avatar"` | ||
15 | + } `json:"user,optional"` | ||
16 | + CompanyList []*struct { | ||
17 | + ID string `json:"id"` | ||
18 | + Name string `json:"name"` | ||
19 | + Logo string `json:"logo"` | ||
20 | + DefaultLogin int `json:"defaultLogin"` | ||
21 | + Types int `json:"types"` | ||
22 | + } `json:"companyList,optional"` | ||
23 | + CurrentCompany *struct { | ||
24 | + ID string `json:"id"` | ||
25 | + Name string `json:"name"` | ||
26 | + Logo string `json:"logo"` | ||
27 | + DefaultLogin int `json:"defaultLogin"` | ||
28 | + Types int `json:"types"` | ||
29 | + } `json:"currentCompany,optional"` | ||
30 | + Workbench []*struct { | ||
31 | + ID int `json:"id"` | ||
32 | + Name string `json:"name"` | ||
33 | + Code string `json:"code"` | ||
34 | + CoverImage string `json:"coverImage"` | ||
35 | + URL string `json:"url"` | ||
36 | + } `json:"workbench,optional"` | ||
37 | + Menus []*struct { | ||
38 | + MenuID int `json:"menuId"` | ||
39 | + ParentID int `json:"parentId"` | ||
40 | + MenuName string `json:"menuName"` | ||
41 | + Code string `json:"code"` | ||
42 | + Types string `json:"types"` | ||
43 | + } `json:"menus,optional"` | ||
44 | +} | ||
45 | + | ||
46 | +type RequestLoginCheck struct { | ||
47 | + Token string | ||
48 | +} | ||
49 | +type DataLoginCheck struct { | ||
50 | + Code int `json:"code,optional"` | ||
51 | + Msg string `json:"msg,optional"` | ||
52 | +} | ||
53 | + | ||
54 | +type ( | ||
55 | + RequestAppLogin struct { | ||
56 | + AppKey string `json:"appKey" valid:"Required"` // 应用键值 | ||
57 | + Token string `json:"token" valid:"Required"` // 凭证 | ||
58 | + } | ||
59 | + DataAppLogin struct { | ||
60 | + AppEnabled bool `json:"appEnabled"` | ||
61 | + } | ||
62 | +) | ||
63 | + | ||
64 | +type ( | ||
65 | + DataUserAppInfo struct { | ||
66 | + Apps []AppItem `json:"apps"` | ||
67 | + } | ||
68 | + AppItem struct { | ||
69 | + AppId int64 | ||
70 | + AppKey string | ||
71 | + AppName string | ||
72 | + } | ||
73 | +) |
pkg/gateway/base.go
0 → 100644
1 | +package gateway | ||
2 | + | ||
3 | +import ( | ||
4 | + "encoding/json" | ||
5 | + "fmt" | ||
6 | +) | ||
7 | + | ||
8 | +// Response 统一消息返回格式 | ||
9 | +type Response struct { | ||
10 | + Code int `json:"code,optional"` | ||
11 | + Msg string `json:"msg,optional"` | ||
12 | + Data json.RawMessage `json:"data,optional"` | ||
13 | +} | ||
14 | + | ||
15 | +// | ||
16 | +//type Request struct { | ||
17 | +// Url string | ||
18 | +// Method string | ||
19 | +// Param interface{} | ||
20 | +//} | ||
21 | + | ||
22 | +type HttpError struct { | ||
23 | + Base Response | ||
24 | +} | ||
25 | + | ||
26 | +func (e HttpError) Error() string { | ||
27 | + if e.Base.Code > 0 && e.Base.Msg != "" { | ||
28 | + return e.Base.Msg | ||
29 | + } | ||
30 | + return fmt.Sprintf("HttpError code:%d msg:%s", e.Base.Code, e.Base.Msg) | ||
31 | +} |
pkg/gateway/base_gateway.go
0 → 100644
1 | +package gateway | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "encoding/json" | ||
6 | + "fmt" | ||
7 | + "github.com/zeromicro/go-zero/core/mapping" | ||
8 | + "github.com/zeromicro/go-zero/rest/httpc" | ||
9 | + "io/ioutil" | ||
10 | + "net/http" | ||
11 | + "strings" | ||
12 | + "time" | ||
13 | +) | ||
14 | + | ||
15 | +type Service struct { | ||
16 | + Timeout time.Duration | ||
17 | + Host string | ||
18 | + Interceptor func(msg string) | ||
19 | + ServiceName string | ||
20 | + service httpc.Service | ||
21 | +} | ||
22 | + | ||
23 | +func NewService(name string, host string, timeout time.Duration, opts ...httpc.Option) Service { | ||
24 | + client := &http.Client{} | ||
25 | + //client.Timeout = timeout | ||
26 | + | ||
27 | + service := Service{ | ||
28 | + Host: host, | ||
29 | + service: httpc.NewServiceWithClient(name, client, opts...), | ||
30 | + } | ||
31 | + return service | ||
32 | +} | ||
33 | + | ||
34 | +func (gateway Service) Do(ctx context.Context, url string, method string, val interface{}, result interface{}) error { | ||
35 | + var ( | ||
36 | + baseResponse = Response{} | ||
37 | + begin = time.Now() | ||
38 | + body []byte | ||
39 | + ) | ||
40 | + response, err := gateway.service.Do(ctx, method, gateway.Host+url, val) | ||
41 | + defer func() { | ||
42 | + jsonParam, _ := json.Marshal(val) | ||
43 | + jsonData, _ := json.Marshal(result) | ||
44 | + if err != nil { | ||
45 | + result = err.Error() | ||
46 | + } | ||
47 | + if gateway.Interceptor != nil { | ||
48 | + gateway.Interceptor(fmt.Sprintf("【网关】%v | %v%v | %v : %v \n-->> %v \n<<-- %v", time.Since(begin), gateway.Host, url, strings.ToUpper(method), | ||
49 | + result, | ||
50 | + string(jsonParam), | ||
51 | + string(jsonData), | ||
52 | + )) | ||
53 | + } | ||
54 | + }() | ||
55 | + if err != nil { | ||
56 | + return err | ||
57 | + } | ||
58 | + if response.StatusCode != http.StatusOK { | ||
59 | + return HttpError{ | ||
60 | + Base: Response{ | ||
61 | + Code: response.StatusCode, | ||
62 | + Msg: response.Status, | ||
63 | + }, | ||
64 | + } | ||
65 | + } | ||
66 | + body, err = Bytes(response) | ||
67 | + if err != nil { | ||
68 | + return err | ||
69 | + } | ||
70 | + if err = json.Unmarshal(body, &baseResponse); err != nil { | ||
71 | + return err | ||
72 | + } | ||
73 | + if baseResponse.Code != 0 { | ||
74 | + return HttpError{ | ||
75 | + Base: Response{ | ||
76 | + Code: baseResponse.Code, | ||
77 | + Msg: baseResponse.Msg, | ||
78 | + }, | ||
79 | + } | ||
80 | + } | ||
81 | + if err = mapping.UnmarshalJsonBytes(baseResponse.Data, result); err != nil { | ||
82 | + return err | ||
83 | + } | ||
84 | + return nil | ||
85 | +} | ||
86 | + | ||
87 | +func (gateway Service) HandlerResponse(ctx context.Context, request *http.Request, response *http.Response, val, result interface{}) error { | ||
88 | + var ( | ||
89 | + baseResponse = Response{} | ||
90 | + begin = time.Now() | ||
91 | + body []byte | ||
92 | + err error | ||
93 | + ) | ||
94 | + defer func() { | ||
95 | + jsonParam, _ := json.Marshal(val) | ||
96 | + jsonData, _ := json.Marshal(result) | ||
97 | + if err != nil { | ||
98 | + result = err.Error() | ||
99 | + } | ||
100 | + if gateway.Interceptor != nil { | ||
101 | + gateway.Interceptor(fmt.Sprintf("【网关】%v | %v%v | %v : %v \n-->> %v \n<<-- %v", time.Since(begin), gateway.Host, request.URL.Path, request.Method, | ||
102 | + result, | ||
103 | + string(jsonParam), | ||
104 | + string(jsonData), | ||
105 | + )) | ||
106 | + } | ||
107 | + }() | ||
108 | + if err != nil { | ||
109 | + return err | ||
110 | + } | ||
111 | + if response.StatusCode != http.StatusOK { | ||
112 | + return HttpError{ | ||
113 | + Base: Response{ | ||
114 | + Code: response.StatusCode, | ||
115 | + Msg: response.Status, | ||
116 | + }, | ||
117 | + } | ||
118 | + } | ||
119 | + body, err = Bytes(response) | ||
120 | + if err != nil { | ||
121 | + return err | ||
122 | + } | ||
123 | + if err = json.Unmarshal(body, &baseResponse); err != nil { | ||
124 | + return err | ||
125 | + } | ||
126 | + if baseResponse.Code != 0 { | ||
127 | + return HttpError{ | ||
128 | + Base: Response{ | ||
129 | + Code: baseResponse.Code, | ||
130 | + Msg: baseResponse.Msg, | ||
131 | + }, | ||
132 | + } | ||
133 | + } | ||
134 | + if err = mapping.UnmarshalJsonBytes(baseResponse.Data, result); err != nil { | ||
135 | + return err | ||
136 | + } | ||
137 | + return nil | ||
138 | +} | ||
139 | + | ||
140 | +func (gateway Service) GetService() httpc.Service { | ||
141 | + return gateway.service | ||
142 | +} | ||
143 | +func Bytes(resp *http.Response) ([]byte, error) { | ||
144 | + var body []byte | ||
145 | + if resp.Body == nil { | ||
146 | + return nil, nil | ||
147 | + } | ||
148 | + defer resp.Body.Close() | ||
149 | + | ||
150 | + body, err := ioutil.ReadAll(resp.Body) | ||
151 | + return body, err | ||
152 | +} |
pkg/gateway/openlib/open_api_lib.go
0 → 100644
1 | +package openlib | ||
2 | + | ||
3 | +import ( | ||
4 | + "bytes" | ||
5 | + "context" | ||
6 | + "encoding/json" | ||
7 | + "github.com/zeromicro/go-zero/core/mapping" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway" | ||
9 | + "io" | ||
10 | + "io/ioutil" | ||
11 | + "mime/multipart" | ||
12 | + "net/http" | ||
13 | + "net/url" | ||
14 | +) | ||
15 | + | ||
16 | +type OpenApiService struct { | ||
17 | + gateway.Service | ||
18 | +} | ||
19 | + | ||
20 | +func (svc *OpenApiService) PutFile(ctx context.Context, request RequestPutFile) (*DataPutFile, error) { | ||
21 | + var result DataPutFile | ||
22 | + r, _ := buildRequest(ctx, http.MethodPost, svc.Host+"/v1/vod/putObject", request) | ||
23 | + response, err := svc.GetService().DoRequest(r) | ||
24 | + if err != nil { | ||
25 | + return nil, err | ||
26 | + } | ||
27 | + if err := svc.HandlerResponse(ctx, r, response, nil, &result); err != nil { | ||
28 | + return nil, err | ||
29 | + } | ||
30 | + return &result, nil | ||
31 | +} | ||
32 | + | ||
33 | +func buildRequest(ctx context.Context, method, tmpUrl string, data any) (*http.Request, error) { | ||
34 | + u, err := url.Parse(tmpUrl) | ||
35 | + if err != nil { | ||
36 | + return nil, err | ||
37 | + } | ||
38 | + | ||
39 | + var val map[string]map[string]any | ||
40 | + if data != nil { | ||
41 | + val, err = mapping.Marshal(data) | ||
42 | + if err != nil { | ||
43 | + return nil, err | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | + var reader io.Reader | ||
48 | + jsonVars, hasJsonBody := val["json"] | ||
49 | + if hasJsonBody { | ||
50 | + var buf bytes.Buffer | ||
51 | + enc := json.NewEncoder(&buf) | ||
52 | + if err := enc.Encode(jsonVars); err != nil { | ||
53 | + return nil, err | ||
54 | + } | ||
55 | + | ||
56 | + reader = &buf | ||
57 | + } | ||
58 | + | ||
59 | + req, err := http.NewRequestWithContext(ctx, method, u.String(), reader) | ||
60 | + if err != nil { | ||
61 | + return nil, err | ||
62 | + } | ||
63 | + fileVars, hasFile := val["file"] | ||
64 | + if hasFile { | ||
65 | + if err = fillFile(req, fileVars); err != nil { | ||
66 | + return nil, err | ||
67 | + } | ||
68 | + } | ||
69 | + if hasJsonBody { | ||
70 | + req.Header.Set("Content-Type", "application/json; charset=utf-8") | ||
71 | + } | ||
72 | + | ||
73 | + return req, nil | ||
74 | +} | ||
75 | + | ||
76 | +func fillFile(req *http.Request, val map[string]any) error { | ||
77 | + if len(val) == 0 { | ||
78 | + return nil | ||
79 | + } | ||
80 | + pr, pw := io.Pipe() | ||
81 | + bodyWriter := multipart.NewWriter(pw) | ||
82 | + go func() { | ||
83 | + for k, v := range val { | ||
84 | + fileWriter, err := bodyWriter.CreateFormFile(k, k) | ||
85 | + if err != nil { | ||
86 | + return | ||
87 | + } | ||
88 | + //fh, err := os.Open(v.(string)) | ||
89 | + //if err != nil { | ||
90 | + // return err | ||
91 | + //} | ||
92 | + fh := bytes.NewBuffer(v.([]byte)) | ||
93 | + // iocopy | ||
94 | + _, err = io.Copy(fileWriter, fh) | ||
95 | + if err != nil { | ||
96 | + return | ||
97 | + } | ||
98 | + } | ||
99 | + bodyWriter.Close() | ||
100 | + pw.Close() | ||
101 | + }() | ||
102 | + req.Header.Set("Content-Type", bodyWriter.FormDataContentType()) | ||
103 | + req.Body = ioutil.NopCloser(pr) | ||
104 | + req.Header.Set("Transfer-Encoding", "chunked") | ||
105 | + return nil | ||
106 | +} |
pkg/gateway/openlib/types.go
0 → 100644
1 | +package openlib | ||
2 | + | ||
3 | +type ( | ||
4 | + RequestPutFile struct { | ||
5 | + File []byte `file:"file.png"` | ||
6 | + } | ||
7 | + DataPutFile []*DataUploadItem | ||
8 | + | ||
9 | + DataUploadItem struct { | ||
10 | + Host string `json:"host"` | ||
11 | + Key string `json:"key"` | ||
12 | + Path string `json:"path"` | ||
13 | + FileName string `json:"fileName"` | ||
14 | + } | ||
15 | +) |
pkg/gateway/request_option.go
0 → 100644
1 | +package gateway | ||
2 | + | ||
3 | +import "net/http" | ||
4 | + | ||
5 | +type RequestOptions struct { | ||
6 | + Header http.Header | ||
7 | + // key:form key value:path | ||
8 | + FileMap map[string]string | ||
9 | +} | ||
10 | + | ||
11 | +type Option func(o *RequestOptions) | ||
12 | + | ||
13 | +func WithHeader(header http.Header) Option { | ||
14 | + return func(o *RequestOptions) { | ||
15 | + o.Header = header | ||
16 | + } | ||
17 | +} | ||
18 | + | ||
19 | +func WithFileMap(v map[string]string) Option { | ||
20 | + return func(o *RequestOptions) { | ||
21 | + o.FileMap = v | ||
22 | + } | ||
23 | +} |
pkg/gateway/smslib/sms_lib.go
0 → 100644
1 | +package smslib | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway" | ||
6 | + "net/http" | ||
7 | +) | ||
8 | + | ||
9 | +type SMSService struct { | ||
10 | + gateway.Service | ||
11 | +} | ||
12 | + | ||
13 | +func (svc *SMSService) SendSmsCode(ctx context.Context, request RequestSendSmsCode) (*DataSendSmsCode, error) { | ||
14 | + var result DataSendSmsCode | ||
15 | + if err := svc.Do(ctx, "/service/sendSms", http.MethodPost, request, &result); err != nil { | ||
16 | + return nil, err | ||
17 | + } | ||
18 | + return &result, nil | ||
19 | +} | ||
20 | + | ||
21 | +func (svc *SMSService) CheckSmsCode(ctx context.Context, request RequestCheckSmsCode) (*DataCheckSmsCode, error) { | ||
22 | + var result DataCheckSmsCode | ||
23 | + if err := svc.Do(ctx, "/service/checkSmsCode", http.MethodPost, request, &result); err != nil { | ||
24 | + return nil, err | ||
25 | + } | ||
26 | + return &result, nil | ||
27 | +} |
pkg/gateway/smslib/types.go
0 → 100644
1 | +package smslib | ||
2 | + | ||
3 | +type ( | ||
4 | + RequestSendSmsCode struct { | ||
5 | + Phone string `json:"phone"` | ||
6 | + } | ||
7 | + DataSendSmsCode struct { | ||
8 | + } | ||
9 | +) | ||
10 | + | ||
11 | +type ( | ||
12 | + RequestCheckSmsCode struct { | ||
13 | + Phone string `json:"phone"` | ||
14 | + Code string `json:"code"` | ||
15 | + } | ||
16 | + DataCheckSmsCode struct { | ||
17 | + } | ||
18 | +) |
1 | +package middleware | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway/authlib" | ||
7 | + "net/http" | ||
8 | +) | ||
9 | + | ||
10 | +type LoginStatusCheckMiddleware struct { | ||
11 | + apiAuth authlib.ApiAuthService | ||
12 | +} | ||
13 | + | ||
14 | +func NewLoginStatusCheckMiddleware(apiAuth authlib.ApiAuthService) *LoginStatusCheckMiddleware { | ||
15 | + return &LoginStatusCheckMiddleware{ | ||
16 | + apiAuth: apiAuth, | ||
17 | + } | ||
18 | +} | ||
19 | + | ||
20 | +func (m *LoginStatusCheckMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { | ||
21 | + return func(w http.ResponseWriter, r *http.Request) { | ||
22 | + token := r.Header.Get("x-mmm-accesstoken") | ||
23 | + if len(token) > 0 { | ||
24 | + _, err := m.apiAuth.LoginCheck(r.Context(), authlib.RequestLoginCheck{ | ||
25 | + Token: token, | ||
26 | + }) | ||
27 | + if err != nil { | ||
28 | + gatewayError, ok := err.(gateway.HttpError) | ||
29 | + if ok { | ||
30 | + unAuthResponse(w, gatewayError.Base.Code, gatewayError.Base.Msg) | ||
31 | + return | ||
32 | + } | ||
33 | + httpx.ErrorCtx(r.Context(), w, err) | ||
34 | + return | ||
35 | + } | ||
36 | + } | ||
37 | + next(w, r) | ||
38 | + } | ||
39 | +} | ||
40 | + | ||
41 | +func unAuthResponse(w http.ResponseWriter, code int, msg string) { | ||
42 | + data := map[string]interface{}{ | ||
43 | + "msg": msg, | ||
44 | + "code": code, | ||
45 | + "data": struct{}{}, | ||
46 | + } | ||
47 | + httpx.WriteJson(w, http.StatusUnauthorized, data) | ||
48 | +} |
pkg/middleware/logrequest_middleware.go
0 → 100644
1 | +package middleware | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/zeromicro/go-zero/rest/handler" | ||
5 | + "net/http" | ||
6 | +) | ||
7 | + | ||
8 | +type LogRequestMiddleware struct { | ||
9 | + logRequest bool | ||
10 | +} | ||
11 | + | ||
12 | +// NewLogRequestMiddleware 记录请求 | ||
13 | +// logRequest true开启记录,false关闭记录 | ||
14 | +func NewLogRequestMiddleware(logRequest bool) *LogRequestMiddleware { | ||
15 | + return &LogRequestMiddleware{ | ||
16 | + logRequest: logRequest, | ||
17 | + } | ||
18 | +} | ||
19 | +func (m *LogRequestMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { | ||
20 | + if !m.logRequest { | ||
21 | + return func(writer http.ResponseWriter, request *http.Request) { | ||
22 | + next(writer, request) | ||
23 | + } | ||
24 | + } | ||
25 | + return handler.DetailedLogHandler(next).(http.HandlerFunc) | ||
26 | +} |
pkg/tool/encryption.go
0 → 100644
1 | +package tool | ||
2 | + | ||
3 | +import ( | ||
4 | + "crypto/md5" | ||
5 | + "fmt" | ||
6 | + "io" | ||
7 | +) | ||
8 | + | ||
9 | +/** 加密方式 **/ | ||
10 | + | ||
11 | +func Md5ByString(str string) string { | ||
12 | + m := md5.New() | ||
13 | + _, err := io.WriteString(m, str) | ||
14 | + if err != nil { | ||
15 | + panic(err) | ||
16 | + } | ||
17 | + arr := m.Sum(nil) | ||
18 | + return fmt.Sprintf("%x", arr) | ||
19 | +} | ||
20 | + | ||
21 | +func Md5ByBytes(b []byte) string { | ||
22 | + return fmt.Sprintf("%x", md5.Sum(b)) | ||
23 | +} |
pkg/tool/filetype_detect.go
0 → 100644
1 | +package tool | ||
2 | + | ||
3 | +import ( | ||
4 | + "path/filepath" | ||
5 | + "strings" | ||
6 | +) | ||
7 | + | ||
8 | +const ( | ||
9 | + Image = "image" | ||
10 | + Video = "video" | ||
11 | +) | ||
12 | + | ||
13 | +var TypeMap = map[string]string{ | ||
14 | + "jpg": Image, | ||
15 | + "png": Image, | ||
16 | + "gif": Image, | ||
17 | + "webp": Image, | ||
18 | + "cr2": Image, | ||
19 | + "tif": Image, | ||
20 | + "bmp": Image, | ||
21 | + "heif": Image, | ||
22 | + "jxr": Image, | ||
23 | + "psd": Image, | ||
24 | + "ico": Image, | ||
25 | + "dwg": Image, | ||
26 | + "avif": Image, | ||
27 | + | ||
28 | + "mp4": Video, | ||
29 | + "m4v": Video, | ||
30 | + "mkv": Video, | ||
31 | + "webm": Video, | ||
32 | + "mov": Video, | ||
33 | + "avi": Video, | ||
34 | + "wmv": Video, | ||
35 | + "mpg": Video, | ||
36 | + "flv": Video, | ||
37 | + "3gp": Video, | ||
38 | +} | ||
39 | +var DefaultFileTypeDetector = FileTypeDetector{} | ||
40 | + | ||
41 | +type FileTypeDetector struct { | ||
42 | +} | ||
43 | + | ||
44 | +func (c FileTypeDetector) Classify(medias []string, mediaType string) []string { | ||
45 | + result := make([]string, 0) | ||
46 | + for _, media := range medias { | ||
47 | + v, ok := TypeMap[strings.Trim(filepath.Ext(media), ".")] | ||
48 | + if !ok { | ||
49 | + continue | ||
50 | + } | ||
51 | + if v == mediaType { | ||
52 | + result = append(result, media) | ||
53 | + } | ||
54 | + } | ||
55 | + return result | ||
56 | +} |
pkg/tool/jwt.go
0 → 100644
1 | +package tool | ||
2 | + | ||
3 | +import ( | ||
4 | + jwt "github.com/golang-jwt/jwt/v4" | ||
5 | + "time" | ||
6 | +) | ||
7 | + | ||
8 | +type UserToken struct { | ||
9 | + UserId int64 `json:"userId"` | ||
10 | + AdminId int64 `json:"adminId"` | ||
11 | + CompanyId int64 `json:"companyId"` | ||
12 | + ClientType string `json:"clientType"` | ||
13 | +} | ||
14 | + | ||
15 | +func (tk UserToken) GenerateToken(secret string, expire int64) (string, error) { | ||
16 | + claims := make(jwt.MapClaims) | ||
17 | + claims["exp"] = time.Now().Unix() + expire | ||
18 | + claims["iat"] = time.Now().Unix() | ||
19 | + claims["userId"] = tk.UserId | ||
20 | + claims["adminId"] = tk.AdminId | ||
21 | + claims["companyId"] = tk.CompanyId | ||
22 | + claims["clientType"] = tk.ClientType | ||
23 | + token := jwt.New(jwt.SigningMethodHS256) | ||
24 | + token.Claims = claims | ||
25 | + | ||
26 | + return token.SignedString([]byte(secret)) | ||
27 | +} |
pkg/tool/krand.go
0 → 100644
1 | +package tool | ||
2 | + | ||
3 | +import ( | ||
4 | + "math/rand" | ||
5 | + "time" | ||
6 | +) | ||
7 | + | ||
8 | +const ( | ||
9 | + KC_RAND_KIND_NUM = 0 // 纯数字 | ||
10 | + KC_RAND_KIND_LOWER = 1 // 小写字母 | ||
11 | + KC_RAND_KIND_UPPER = 2 // 大写字母 | ||
12 | + KC_RAND_KIND_ALL = 3 // 数字、大小写字母 | ||
13 | +) | ||
14 | + | ||
15 | +// 随机字符串 | ||
16 | +func Krand(size int, kind int) string { | ||
17 | + ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size) | ||
18 | + is_all := kind > 2 || kind < 0 | ||
19 | + rand.Seed(time.Now().UnixNano()) | ||
20 | + for i := 0; i < size; i++ { | ||
21 | + if is_all { // random ikind | ||
22 | + ikind = rand.Intn(3) | ||
23 | + } | ||
24 | + scope, base := kinds[ikind][0], kinds[ikind][1] | ||
25 | + result[i] = uint8(base + rand.Intn(scope)) | ||
26 | + } | ||
27 | + return string(result) | ||
28 | +} |
pkg/tool/oss/ali_oss.go
0 → 100644
1 | +package oss | ||
2 | + | ||
3 | +import ( | ||
4 | + "encoding/json" | ||
5 | + "fmt" | ||
6 | + "image" | ||
7 | + _ "image/jpeg" | ||
8 | + "net/http" | ||
9 | + "strings" | ||
10 | + "time" | ||
11 | +) | ||
12 | + | ||
13 | +type FileInfo struct { | ||
14 | + FileSize struct { | ||
15 | + Value string | ||
16 | + } | ||
17 | + Format struct { | ||
18 | + Value string | ||
19 | + } | ||
20 | + ImageHeight struct { | ||
21 | + Value string | ||
22 | + } | ||
23 | + ImageWidth struct { | ||
24 | + Value string | ||
25 | + } | ||
26 | +} | ||
27 | + | ||
28 | +func GetImageInfo(url string) (info FileInfo, err error) { | ||
29 | + //ok := strings.HasPrefix(url, "https://timeless-world.oss-cn-shenzhen.aliyuncs.com") | ||
30 | + ok := strings.HasPrefix(url, "http") | ||
31 | + if !ok { | ||
32 | + return | ||
33 | + } | ||
34 | + ok = strings.Contains(url, "aliyuncs") | ||
35 | + if !ok { | ||
36 | + return | ||
37 | + } | ||
38 | + apiUrl := url + `?x-oss-process=image/info` | ||
39 | + req, err := http.NewRequest(http.MethodGet, apiUrl, nil) | ||
40 | + if err != nil { | ||
41 | + return info, err | ||
42 | + } | ||
43 | + httpclient := http.Client{ | ||
44 | + Timeout: 5 * time.Second, | ||
45 | + } | ||
46 | + resp, err := httpclient.Do(req) | ||
47 | + if err != nil { | ||
48 | + return info, err | ||
49 | + } | ||
50 | + defer resp.Body.Close() | ||
51 | + if resp.StatusCode != http.StatusOK { | ||
52 | + return | ||
53 | + } | ||
54 | + jDecoder := json.NewDecoder(resp.Body) | ||
55 | + err = jDecoder.Decode(&info) | ||
56 | + if err != nil { | ||
57 | + return info, err | ||
58 | + } | ||
59 | + return info, nil | ||
60 | +} | ||
61 | + | ||
62 | +// 获取视频封面图 | ||
63 | +func GetVideoCover(videoUrl string) (coverUrl string, w int, h int, err error) { | ||
64 | + ok := strings.HasPrefix(videoUrl, "http") | ||
65 | + if !ok { | ||
66 | + return | ||
67 | + } | ||
68 | + ok = strings.Contains(videoUrl, "aliyuncs") | ||
69 | + if !ok { | ||
70 | + return | ||
71 | + } | ||
72 | + videoUrl = videoUrl + "?x-oss-process=video/snapshot,t_100,f_jpg,m_fast" | ||
73 | + httpclient := http.Client{ | ||
74 | + Timeout: 5 * time.Second, | ||
75 | + } | ||
76 | + res, err := httpclient.Get(videoUrl) | ||
77 | + if err != nil || res.StatusCode != http.StatusOK { | ||
78 | + return videoUrl, 600, 600, fmt.Errorf("获取图片失败:%s", err) | ||
79 | + } | ||
80 | + defer res.Body.Close() | ||
81 | + m, _, err := image.Decode(res.Body) | ||
82 | + if err != nil { | ||
83 | + return videoUrl, 600, 600, fmt.Errorf("获取图片失败:%s", err) | ||
84 | + } | ||
85 | + return videoUrl, m.Bounds().Dx(), m.Bounds().Dy(), nil | ||
86 | +} |
pkg/tool/oss/ali_oss_test.go
0 → 100644
1 | +package oss | ||
2 | + | ||
3 | +import "testing" | ||
4 | + | ||
5 | +func TestGetVideoCover(t *testing.T) { | ||
6 | + | ||
7 | + cover, w, h, err := GetVideoCover("https://timeless-world.oss-cn-shenzhen.aliyuncs.com/open-api/dev_online/20230913/object/1694587897_yYfG6TYTsGMCKETxdnTEhAQjXpYGD3MB.mp4") | ||
8 | + t.Logf("cover=%v, w=%v, h=%v, err=%v", cover, w, h, err) | ||
9 | +} |
pkg/tool/pinyin.go
0 → 100644
1 | +package tool | ||
2 | + | ||
3 | +import ( | ||
4 | + "strings" | ||
5 | + | ||
6 | + "github.com/mozillazg/go-pinyin" | ||
7 | +) | ||
8 | + | ||
9 | +func ToPinYin(hans string, sep string) string { | ||
10 | + a := pinyin.NewArgs() | ||
11 | + tmp := pinyin.Pinyin(hans, a) | ||
12 | + result := make([]string, 0) | ||
13 | + for i := range tmp { | ||
14 | + result = append(result, tmp[i]...) | ||
15 | + } | ||
16 | + py := strings.Join(result, sep) | ||
17 | + if len(py) == 0 { | ||
18 | + return strings.ToLower(hans) | ||
19 | + } | ||
20 | + return py | ||
21 | +} |
pkg/transaction/transaction.go
0 → 100644
1 | +package transaction | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "fmt" | ||
6 | + "gorm.io/gorm" | ||
7 | + "sync" | ||
8 | +) | ||
9 | + | ||
10 | +type Context struct { | ||
11 | + //启用事务标识 | ||
12 | + beginTransFlag bool | ||
13 | + db *gorm.DB | ||
14 | + session *gorm.DB | ||
15 | + lock sync.Mutex | ||
16 | +} | ||
17 | + | ||
18 | +func (transactionContext *Context) Begin() error { | ||
19 | + transactionContext.lock.Lock() | ||
20 | + defer transactionContext.lock.Unlock() | ||
21 | + transactionContext.beginTransFlag = true | ||
22 | + tx := transactionContext.db.Begin() | ||
23 | + transactionContext.session = tx | ||
24 | + return nil | ||
25 | +} | ||
26 | + | ||
27 | +func (transactionContext *Context) Commit() error { | ||
28 | + transactionContext.lock.Lock() | ||
29 | + defer transactionContext.lock.Unlock() | ||
30 | + if !transactionContext.beginTransFlag { | ||
31 | + return nil | ||
32 | + } | ||
33 | + tx := transactionContext.session.Commit() | ||
34 | + return tx.Error | ||
35 | +} | ||
36 | + | ||
37 | +func (transactionContext *Context) Rollback() error { | ||
38 | + transactionContext.lock.Lock() | ||
39 | + defer transactionContext.lock.Unlock() | ||
40 | + if !transactionContext.beginTransFlag { | ||
41 | + return nil | ||
42 | + } | ||
43 | + tx := transactionContext.session.Rollback() | ||
44 | + return tx.Error | ||
45 | +} | ||
46 | + | ||
47 | +func (transactionContext *Context) DB() *gorm.DB { | ||
48 | + if transactionContext.beginTransFlag && transactionContext.session != nil { | ||
49 | + return transactionContext.session | ||
50 | + } | ||
51 | + return transactionContext.db | ||
52 | +} | ||
53 | + | ||
54 | +func NewTransactionContext(db *gorm.DB) *Context { | ||
55 | + return &Context{ | ||
56 | + db: db, | ||
57 | + } | ||
58 | +} | ||
59 | + | ||
60 | +type Conn interface { | ||
61 | + Begin() error | ||
62 | + Commit() error | ||
63 | + Rollback() error | ||
64 | + DB() *gorm.DB | ||
65 | +} | ||
66 | + | ||
67 | +// UseTrans when beginTrans is true , it will begin a new transaction | ||
68 | +// to execute the function, recover when panic happen | ||
69 | +func UseTrans(ctx context.Context, | ||
70 | + db *gorm.DB, | ||
71 | + fn func(context.Context, Conn) error, beginTrans bool) (err error) { | ||
72 | + var tx Conn | ||
73 | + tx = NewTransactionContext(db) | ||
74 | + if beginTrans { | ||
75 | + if err = tx.Begin(); err != nil { | ||
76 | + return | ||
77 | + } | ||
78 | + } | ||
79 | + defer func() { | ||
80 | + if p := recover(); p != nil { | ||
81 | + if e := tx.Rollback(); e != nil { | ||
82 | + err = fmt.Errorf("recover from %#v, rollback failed: %w", p, e) | ||
83 | + } else { | ||
84 | + err = fmt.Errorf("recoveer from %#v", p) | ||
85 | + } | ||
86 | + } else if err != nil { | ||
87 | + if e := tx.Rollback(); e != nil { | ||
88 | + err = fmt.Errorf("transaction failed: %s, rollback failed: %w", err, e) | ||
89 | + } | ||
90 | + } else { | ||
91 | + err = tx.Commit() | ||
92 | + } | ||
93 | + }() | ||
94 | + | ||
95 | + return fn(ctx, tx) | ||
96 | +} | ||
97 | + | ||
98 | +func PaginationAndCount(ctx context.Context, tx *gorm.DB, params map[string]interface{}, dst interface{}) (int64, *gorm.DB) { | ||
99 | + var total int64 | ||
100 | + // 只返回数量 | ||
101 | + if v, ok := params["countOnly"]; ok && v.(bool) { | ||
102 | + tx = tx.Count(&total) | ||
103 | + return total, tx | ||
104 | + } | ||
105 | + // 只返回记录 | ||
106 | + if v, ok := params["findOnly"]; ok && v.(bool) { | ||
107 | + if v, ok := params["offset"]; ok { | ||
108 | + tx.Offset(v.(int)) | ||
109 | + } | ||
110 | + if v, ok := params["limit"]; ok { | ||
111 | + tx.Limit(v.(int)) | ||
112 | + } | ||
113 | + if tx = tx.Find(dst); tx.Error != nil { | ||
114 | + return 0, tx | ||
115 | + } | ||
116 | + return total, tx | ||
117 | + } | ||
118 | + // 数量跟记录都返回 | ||
119 | + tx = tx.Count(&total) | ||
120 | + if tx.Error != nil { | ||
121 | + return total, tx | ||
122 | + } | ||
123 | + if v, ok := params["offset"]; ok { | ||
124 | + tx.Offset(v.(int)) | ||
125 | + } | ||
126 | + if v, ok := params["limit"]; ok { | ||
127 | + tx.Limit(v.(int)) | ||
128 | + } | ||
129 | + if tx = tx.Find(dst); tx.Error != nil { | ||
130 | + return 0, tx | ||
131 | + } | ||
132 | + return total, tx | ||
133 | +} |
pkg/xcollection/tree.go
0 → 100644
1 | +package xcollection | ||
2 | + | ||
3 | +type TreeNode interface { | ||
4 | + PID() string | ||
5 | + ID() string | ||
6 | +} | ||
7 | + | ||
8 | +type Tree struct { | ||
9 | + Node TreeNode `json:"chart"` | ||
10 | + Nodes []*Tree `json:"charts"` | ||
11 | +} | ||
12 | + | ||
13 | +func NewTree(nodes []TreeNode) *Tree { | ||
14 | + var tree = &Tree{ | ||
15 | + Node: nil, | ||
16 | + Nodes: make([]*Tree, 0), | ||
17 | + } | ||
18 | + for i := range nodes { | ||
19 | + match := traverseAdd(tree, nodes[i]) | ||
20 | + if !match { | ||
21 | + tree.Nodes = append(tree.Nodes, newTree(nodes[i])) | ||
22 | + } | ||
23 | + } | ||
24 | + return tree | ||
25 | +} | ||
26 | + | ||
27 | +func newTree(node TreeNode) *Tree { | ||
28 | + return &Tree{ | ||
29 | + Node: node, | ||
30 | + Nodes: make([]*Tree, 0), | ||
31 | + } | ||
32 | +} | ||
33 | + | ||
34 | +func (tree *Tree) Root() TreeNode { | ||
35 | + if tree.Node != nil { | ||
36 | + return tree.Node | ||
37 | + } | ||
38 | + if len(tree.Nodes) > 0 { | ||
39 | + return tree.Nodes[0].Node | ||
40 | + } | ||
41 | + return nil | ||
42 | +} | ||
43 | + | ||
44 | +// TreeNodePaths returns all the parents of the current node 1->5->7 , use time n*O(n)(need performance optimization) | ||
45 | +func (tree *Tree) TreeNodePaths(node TreeNode) []TreeNode { | ||
46 | + treeNode := node | ||
47 | + result := make([]TreeNode, 0) | ||
48 | + for { | ||
49 | + if treeNode == nil { | ||
50 | + break | ||
51 | + } | ||
52 | + tmp := tree.find(treeNode, func(a, b TreeNode) bool { | ||
53 | + if a.ID() == b.PID() { | ||
54 | + return true | ||
55 | + } | ||
56 | + return false | ||
57 | + }) | ||
58 | + result = append(result, treeNode) | ||
59 | + if tmp == nil { | ||
60 | + break | ||
61 | + } | ||
62 | + treeNode = tmp.Node | ||
63 | + } | ||
64 | + reserveResult := make([]TreeNode, 0) | ||
65 | + for i := len(result) - 1; i >= 0; i-- { | ||
66 | + reserveResult = append(reserveResult, result[i]) | ||
67 | + } | ||
68 | + return reserveResult | ||
69 | +} | ||
70 | + | ||
71 | +// Add adds a node to the first matching parent tree if add success it return true | ||
72 | +func (tree *Tree) Add(node TreeNode) bool { | ||
73 | + return traverseAdd(tree, node) | ||
74 | +} | ||
75 | + | ||
76 | +// AllChildNode returns all child nodes under Node, including itself | ||
77 | +func (tree *Tree) AllChildNode(node TreeNode) []TreeNode { | ||
78 | + treeNode := tree.find(node, nil) | ||
79 | + if treeNode == nil { | ||
80 | + return []TreeNode{} | ||
81 | + } | ||
82 | + return tree.allChildNode(treeNode, nil) | ||
83 | +} | ||
84 | + | ||
85 | +// AllLeafNode returns all leaf node under Node ,if node is nil returns all leaf node under tree | ||
86 | +func (tree *Tree) AllLeafNode(node TreeNode) []TreeNode { | ||
87 | + treeNode := tree | ||
88 | + if node != nil { | ||
89 | + treeNode = tree.find(node, nil) | ||
90 | + } | ||
91 | + if treeNode == nil { | ||
92 | + return []TreeNode{} | ||
93 | + } | ||
94 | + return tree.allChildNode(treeNode, func(node *Tree) bool { | ||
95 | + if len(node.Nodes) == 0 { | ||
96 | + return true | ||
97 | + } | ||
98 | + return false | ||
99 | + }) | ||
100 | +} | ||
101 | + | ||
102 | +// Depth returns all child nodes under depth depth=[1:n] | ||
103 | +func (tree *Tree) Depth(depth int) []TreeNode { | ||
104 | + treeNode := tree.find(tree.Root(), nil) | ||
105 | + if treeNode == nil { | ||
106 | + return []TreeNode{} | ||
107 | + } | ||
108 | + return tree.allChildByDepth(treeNode, depth) | ||
109 | +} | ||
110 | + | ||
111 | +// AllChildNodeByDepth returns all child nodes under depth Node | ||
112 | +func (tree *Tree) AllChildNodeByDepth(node TreeNode, depth int) []TreeNode { | ||
113 | + treeNode := tree.find(node, nil) | ||
114 | + if treeNode == nil { | ||
115 | + return []TreeNode{} | ||
116 | + } | ||
117 | + return tree.allChildByDepth(treeNode, depth) | ||
118 | +} | ||
119 | + | ||
120 | +// Find query the node in this tree | ||
121 | +func (tree *Tree) Find(node TreeNode, compared func(a, b TreeNode) bool) *Tree { | ||
122 | + return tree.find(node, compared) | ||
123 | +} | ||
124 | + | ||
125 | +// find query the node in this tree | ||
126 | +func (tree *Tree) find(node TreeNode, compared func(a, b TreeNode) bool) *Tree { | ||
127 | + var stack []*Tree | ||
128 | + stack = append(stack, tree) | ||
129 | + var find *Tree | ||
130 | + for { | ||
131 | + if len(stack) == 0 { | ||
132 | + break | ||
133 | + } | ||
134 | + pop := stack[0] | ||
135 | + stack = stack[1:] | ||
136 | + stack = append(stack, pop.Nodes...) | ||
137 | + if pop == nil || pop.Node == nil { | ||
138 | + continue | ||
139 | + } | ||
140 | + if compared != nil { | ||
141 | + if compared(pop.Node, node) { | ||
142 | + find = pop | ||
143 | + break | ||
144 | + } | ||
145 | + continue | ||
146 | + } | ||
147 | + if pop.Node.ID() == node.ID() { | ||
148 | + find = pop | ||
149 | + break | ||
150 | + } | ||
151 | + } | ||
152 | + return find | ||
153 | +} | ||
154 | + | ||
155 | +// allChildNode 返回treeNode下所有子节点 | ||
156 | +func (tree *Tree) allChildNode(treeNode *Tree, filter func(node *Tree) bool) []TreeNode { | ||
157 | + var stack []*Tree | ||
158 | + stack = append(stack, treeNode) | ||
159 | + var res []TreeNode | ||
160 | + for { | ||
161 | + if len(stack) == 0 { | ||
162 | + break | ||
163 | + } | ||
164 | + pop := stack[0] | ||
165 | + stack = stack[1:] | ||
166 | + stack = append(stack, pop.Nodes...) | ||
167 | + if filter != nil && !filter(pop) { | ||
168 | + continue | ||
169 | + } | ||
170 | + res = append(res, pop.Node) | ||
171 | + } | ||
172 | + return res | ||
173 | +} | ||
174 | + | ||
175 | +// traverseAdd 递归添加 | ||
176 | +// | ||
177 | +// tree 当前树 | ||
178 | +// node 判断的节点 | ||
179 | +func traverseAdd(tree *Tree, node TreeNode) bool { | ||
180 | + list := tree.Nodes | ||
181 | + var match bool = false | ||
182 | + for i := range list { | ||
183 | + id, pid := list[i].Node.ID(), node.PID() | ||
184 | + if pid == id { | ||
185 | + list[i].Nodes = append(list[i].Nodes, newTree(node)) | ||
186 | + return true | ||
187 | + } | ||
188 | + if match || traverseAdd(list[i], node) { | ||
189 | + match = true | ||
190 | + break | ||
191 | + } | ||
192 | + } | ||
193 | + return match | ||
194 | +} | ||
195 | + | ||
196 | +// allChildByDepth 返回treeNode下指定深度的所有子节点 depth=[1:n] | ||
197 | +func (tree *Tree) allChildByDepth(treeNode *Tree, depth int) []TreeNode { | ||
198 | + var stack []*Tree | ||
199 | + stack = append(stack, treeNode) | ||
200 | + var res []TreeNode | ||
201 | + if depth <= 0 { | ||
202 | + return res | ||
203 | + } | ||
204 | + if treeNode.Root() != nil && depth == 1 { | ||
205 | + return []TreeNode{treeNode.Root()} | ||
206 | + } | ||
207 | + curDepth := 1 | ||
208 | + var depthStack []*Tree | ||
209 | + for { | ||
210 | + if len(stack) == 0 { | ||
211 | + break | ||
212 | + } | ||
213 | + pop := stack[0] | ||
214 | + stack = stack[1:] | ||
215 | + depthStack = append(depthStack, pop.Nodes...) | ||
216 | + if len(stack) == 0 { | ||
217 | + curDepth++ | ||
218 | + stack = depthStack[:] | ||
219 | + depthStack = []*Tree{} | ||
220 | + if curDepth == depth { | ||
221 | + for i := range stack { | ||
222 | + res = append(res, stack[i].Node) | ||
223 | + } | ||
224 | + break | ||
225 | + } | ||
226 | + } | ||
227 | + } | ||
228 | + return res | ||
229 | +} |
pkg/xcollection/tree_test.go
0 → 100644
1 | +package xcollection | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/stretchr/testify/assert" | ||
5 | + "strconv" | ||
6 | + "testing" | ||
7 | +) | ||
8 | + | ||
9 | +func prepare() []struct { | ||
10 | + Input []TreeNode | ||
11 | + Text string | ||
12 | + Except []string | ||
13 | + Except2 []string | ||
14 | +} { | ||
15 | + return []struct { | ||
16 | + Input []TreeNode | ||
17 | + Text string | ||
18 | + Except []string | ||
19 | + Except2 []string | ||
20 | + }{ | ||
21 | + { | ||
22 | + Input: []TreeNode{ | ||
23 | + &st{Id: 1, Pid: 0}, | ||
24 | + &st{Id: 2, Pid: 1}, &st{Id: 3, Pid: 1}, &st{Id: 4, Pid: 1}, | ||
25 | + &st{Id: 5, Pid: 3}, | ||
26 | + &st{Id: 6, Pid: 5}, &st{Id: 7, Pid: 5}}, | ||
27 | + Text: ` | ||
28 | +树形结构: | ||
29 | + 1 | ||
30 | +2 3 4 | ||
31 | + 5 | ||
32 | + 6 7 | ||
33 | +`, | ||
34 | + Except: []string{"5", "6", "7"}, | ||
35 | + Except2: []string{"2", "4", "6", "7"}, | ||
36 | + }, | ||
37 | + } | ||
38 | +} | ||
39 | + | ||
40 | +func Test_Tree(t *testing.T) { | ||
41 | + table := prepare() | ||
42 | + for i := range table { | ||
43 | + tree := NewTree(table[i].Input) | ||
44 | + out := tree.AllChildNode(&st{Id: 5, Pid: 3}) | ||
45 | + var res []string = treeNodeResults(out) | ||
46 | + assert.Equal(t, res, table[i].Except) | ||
47 | + | ||
48 | + out = tree.AllLeafNode(nil) //tree.Root() | ||
49 | + res = treeNodeResults(out) | ||
50 | + assert.Equal(t, res, table[i].Except2) | ||
51 | + | ||
52 | + root := tree.Root() | ||
53 | + assert.Equal(t, root.ID(), "1") | ||
54 | + | ||
55 | + //tree.Add(&st{Id:10,Pid: 7}) | ||
56 | + // | ||
57 | + //out = tree.AllLeafNode(tree.Root()) | ||
58 | + //res = treeNodeResults(out) | ||
59 | + //assert.Equal(t, res, []string{"2", "4", "6", "10"}) | ||
60 | + | ||
61 | + out = tree.TreeNodePaths(&st{Id: 7, Pid: 5}) | ||
62 | + res = treeNodeResults(out) | ||
63 | + assert.Equal(t, res, []string{"1", "3", "5", "7"}) | ||
64 | + | ||
65 | + } | ||
66 | +} | ||
67 | + | ||
68 | +func Test_TreeNodeByDepth(t *testing.T) { | ||
69 | + input := []TreeNode{ | ||
70 | + &st{Id: 1, Pid: 0}, | ||
71 | + &st{Id: 2, Pid: 1}, &st{Id: 3, Pid: 1}, &st{Id: 4, Pid: 1}, | ||
72 | + &st{Id: 5, Pid: 3}, | ||
73 | + &st{Id: 6, Pid: 5}, &st{Id: 7, Pid: 5}, | ||
74 | + &st{Id: 8, Pid: 6}, &st{Id: 9, Pid: 6}, &st{Id: 10, Pid: 6}, &st{Id: 11, Pid: 7}, &st{Id: 12, Pid: 7}, | ||
75 | + } | ||
76 | + | ||
77 | + tree := NewTree(input) | ||
78 | + /* | ||
79 | + 树形结构: | ||
80 | + 1 | ||
81 | + 2 3 4 | ||
82 | + 5 | ||
83 | + 6 7 | ||
84 | + 8 9 10 11 12 | ||
85 | + */ | ||
86 | + var out []TreeNode | ||
87 | + var res []string | ||
88 | + out = tree.AllChildNodeByDepth(&st{Id: 5, Pid: 3}, 2) | ||
89 | + res = treeNodeResults(out) | ||
90 | + assert.Equal(t, []string{"6", "7"}, res) | ||
91 | + out = tree.AllChildNodeByDepth(tree.Root(), 1) | ||
92 | + res = treeNodeResults(out) | ||
93 | + assert.Equal(t, []string{"1"}, res) | ||
94 | + out = tree.AllChildNodeByDepth(tree.Root(), 2) | ||
95 | + res = treeNodeResults(out) | ||
96 | + assert.Equal(t, []string{"2", "3", "4"}, res) | ||
97 | + out = tree.AllChildNodeByDepth(tree.Root(), 3) | ||
98 | + res = treeNodeResults(out) | ||
99 | + assert.Equal(t, []string{"5"}, res) | ||
100 | + out = tree.AllChildNodeByDepth(tree.Root(), 4) | ||
101 | + res = treeNodeResults(out) | ||
102 | + assert.Equal(t, []string{"6", "7"}, res) | ||
103 | + out = tree.AllChildNodeByDepth(tree.Root(), 5) | ||
104 | + res = treeNodeResults(out) | ||
105 | + assert.Equal(t, []string{"8", "9", "10", "11", "12"}, res) | ||
106 | +} | ||
107 | + | ||
108 | +type st struct { | ||
109 | + Id int | ||
110 | + Pid int | ||
111 | +} | ||
112 | + | ||
113 | +func (t *st) PID() string { | ||
114 | + return strconv.Itoa(t.Pid) | ||
115 | +} | ||
116 | +func (t *st) ID() string { | ||
117 | + return strconv.Itoa(t.Id) | ||
118 | +} | ||
119 | + | ||
120 | +func treeNodeResults(nodes []TreeNode) []string { | ||
121 | + var res []string | ||
122 | + for i := range nodes { | ||
123 | + res = append(res, nodes[i].ID()) | ||
124 | + } | ||
125 | + return res | ||
126 | +} |
pkg/xerr/err.go
0 → 100644
1 | +package xerr | ||
2 | + | ||
3 | +/**默认的服务错误**/ | ||
4 | + | ||
5 | +func NewErr(err error) *CodeError { | ||
6 | + return &CodeError{errCode: ServerCommonError, InternalError: err} | ||
7 | +} | ||
8 | + | ||
9 | +func NewErrMsg(errMsg string) *CodeError { | ||
10 | + return &CodeError{errCode: ServerCommonError, errMsg: errMsg} | ||
11 | +} | ||
12 | + | ||
13 | +func NewErrMsgErr(errMsg string, internalError error) *CodeError { | ||
14 | + return &CodeError{errCode: ServerCommonError, errMsg: errMsg, InternalError: internalError} | ||
15 | +} | ||
16 | + | ||
17 | +/**指定错误码的错误**/ | ||
18 | + | ||
19 | +func NewCodeErr(errCode int, err error) *CodeError { | ||
20 | + return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode), InternalError: err} | ||
21 | +} | ||
22 | + | ||
23 | +func NewCodeErrMsg(errCode int, err error, msg string) *CodeError { | ||
24 | + return &CodeError{errCode: errCode, errMsg: msg, InternalError: err} | ||
25 | +} |
pkg/xerr/err_code.go
0 → 100644
1 | +package xerr | ||
2 | + | ||
3 | +import "fmt" | ||
4 | + | ||
5 | +const ( | ||
6 | + // OK 成功返回 | ||
7 | + OK int = 200 | ||
8 | +) | ||
9 | + | ||
10 | +// 全局错误码 | ||
11 | +// 系统错误前3位代表业务,后三位代表具体功能 | ||
12 | +const ( | ||
13 | + ServerCommonError int = 100001 // 系统错误 | ||
14 | + RequestParamError int = 100002 // 参数请求错误 | ||
15 | + TokenExpireError int = 100003 // token失效 | ||
16 | + TokenGenerateError int = 100004 // 生成token失败 | ||
17 | + DbError int = 100005 // 数据库错误 | ||
18 | + DbUpdateAffectedZeroError int = 100006 // 数据库更新错误 | ||
19 | +) | ||
20 | + | ||
21 | +/**微信模块**/ | ||
22 | +const ( | ||
23 | + ErrWxMiniAuthFailError int = 500001 | ||
24 | + ErrUserNoAuth int = 500002 | ||
25 | +) | ||
26 | + | ||
27 | +type CodeError struct { | ||
28 | + errCode int | ||
29 | + errMsg string | ||
30 | + InternalError error | ||
31 | +} | ||
32 | + | ||
33 | +// GetErrCode 返回给前端的错误码 | ||
34 | +func (e *CodeError) GetErrCode() int { | ||
35 | + return e.errCode | ||
36 | +} | ||
37 | + | ||
38 | +// GetErrMsg 返回给前端显示端错误信息 | ||
39 | +func (e *CodeError) GetErrMsg() string { | ||
40 | + if e.errMsg == "" { | ||
41 | + return MapErrMsg(e.errCode) | ||
42 | + } | ||
43 | + return e.errMsg | ||
44 | +} | ||
45 | + | ||
46 | +func (e *CodeError) Error() string { | ||
47 | + if e.InternalError != nil { | ||
48 | + return fmt.Sprintf("code error: code=%d msg:%s error:%s", e.errCode, e.errMsg, e.InternalError.Error()) | ||
49 | + } | ||
50 | + return fmt.Sprintf("code error: code=%d msg:%s", e.errCode, e.errMsg) | ||
51 | +} |
pkg/xerr/err_handler.go
0 → 100644
1 | +package xerr | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "github.com/zeromicro/go-zero/core/logx" | ||
6 | + "google.golang.org/grpc/status" | ||
7 | + "net/http" | ||
8 | +) | ||
9 | + | ||
10 | +func ErrorHandlerCtx(ctx context.Context, err error) (int, any) { | ||
11 | + //错误返回 | ||
12 | + var ( | ||
13 | + errCode = ServerCommonError | ||
14 | + ) | ||
15 | + // 自定义错误类型 | ||
16 | + if codeError, ok := err.(*CodeError); ok { | ||
17 | + errCode = codeError.GetErrCode() | ||
18 | + } else { | ||
19 | + // grpc err错误 | ||
20 | + var grpcStatus *status.Status | ||
21 | + if grpcStatus, ok = status.FromError(err); ok { | ||
22 | + grpcCode := int(grpcStatus.Code()) | ||
23 | + if IsCodeErr(grpcCode) { | ||
24 | + errCode = grpcCode | ||
25 | + } | ||
26 | + } | ||
27 | + } | ||
28 | + logx.WithContext(ctx).Errorf("【ERROR-HANDLER】 : %+v ", err) | ||
29 | + response := Error(errCode, MapErrMsg(errCode)) | ||
30 | + response.Error = err.Error() | ||
31 | + return http.StatusOK, response | ||
32 | +} | ||
33 | + | ||
34 | +func OkHandlerCtx(ctx context.Context, a any) any { | ||
35 | + return Success(a) | ||
36 | +} | ||
37 | + | ||
38 | +func Success(data interface{}) *ResponseSuccessBean { | ||
39 | + return &ResponseSuccessBean{Code: 0, Msg: "OK", Data: data} | ||
40 | +} | ||
41 | + | ||
42 | +type ResponseSuccessBean struct { | ||
43 | + Code uint32 `json:"code"` | ||
44 | + Msg string `json:"msg"` | ||
45 | + Data interface{} `json:"data"` | ||
46 | +} | ||
47 | + | ||
48 | +type ResponseErrorBean struct { | ||
49 | + Code int `json:"code"` | ||
50 | + Msg string `json:"msg"` | ||
51 | + Error string `json:"err"` | ||
52 | +} | ||
53 | + | ||
54 | +func Error(errCode int, errMsg string) *ResponseErrorBean { | ||
55 | + return &ResponseErrorBean{Code: errCode, Msg: errMsg} | ||
56 | +} |
pkg/xerr/err_msg.go
0 → 100644
1 | +package xerr | ||
2 | + | ||
3 | +var message map[int]string | ||
4 | + | ||
5 | +func init() { | ||
6 | + message = make(map[int]string) | ||
7 | + message[OK] = "SUCCESS" | ||
8 | + message[ServerCommonError] = "服务器开小差啦,稍后再来试一试" | ||
9 | + message[RequestParamError] = "参数错误" | ||
10 | + message[TokenExpireError] = "token失效,请重新登陆" | ||
11 | + message[TokenGenerateError] = "生成token失败" | ||
12 | + message[DbError] = "数据库繁忙,请稍后再试" | ||
13 | + message[DbUpdateAffectedZeroError] = "更新数据影响行数为0" | ||
14 | + message[ErrUserNoAuth] = "无权限" | ||
15 | + message[ErrWxMiniAuthFailError] = "微信授权失败" | ||
16 | +} | ||
17 | + | ||
18 | +func MapErrMsg(errCode int) string { | ||
19 | + if msg, ok := message[errCode]; ok { | ||
20 | + return msg | ||
21 | + } else { | ||
22 | + return "服务器开小差啦,稍后再来试一试" | ||
23 | + } | ||
24 | +} | ||
25 | + | ||
26 | +func IsCodeErr(errCode int) bool { | ||
27 | + if _, ok := message[errCode]; ok { | ||
28 | + return true | ||
29 | + } else { | ||
30 | + return false | ||
31 | + } | ||
32 | +} |
-
请 注册 或 登录 后发表评论