正在显示
76 个修改的文件
包含
4664 行增加
和
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 | + | ||
28 | +app.log | ||
29 | +go.sum | ||
30 | +lastupdate.tmp | ||
31 | +*.log | ||
32 | + | ||
33 | +public/* | ||
34 | +logs/ |
Dockerfile
0 → 100644
1 | +FROM golang:1.19-alpine as builder | ||
2 | + | ||
3 | +# Define the project name | 定义项目名称 | ||
4 | +ARG PROJECT=core | ||
5 | + | ||
6 | +WORKDIR /build | ||
7 | +COPY . . | ||
8 | + | ||
9 | +RUN go env -w GO111MODULE=on \ | ||
10 | + && go env -w GOPROXY=https://goproxy.cn,direct \ | ||
11 | + && go env -w CGO_ENABLED=0 \ | ||
12 | + && go env \ | ||
13 | + && go mod tidy \ | ||
14 | + && cd cmd/chart-server/api \ | ||
15 | + && go build -ldflags="-s -w" -o /build/api/${PROJECT} ${PROJECT}.go | ||
16 | + | ||
17 | +FROM alpine:latest | ||
18 | + | ||
19 | +# Define the project name | 定义项目名称 | ||
20 | +ARG PROJECT=core | ||
21 | +# Define the config file name | 定义配置文件名 | ||
22 | +ARG CONFIG_FILE=core.yaml | ||
23 | +# Define the author | 定义作者 | ||
24 | +ARG AUTHOR=785409885@qq.com | ||
25 | + | ||
26 | +LABEL org.opencontainers.image.authors=${AUTHOR} | ||
27 | + | ||
28 | +WORKDIR /app | ||
29 | +ENV PROJECT=${PROJECT} | ||
30 | +ENV CONFIG_FILE=${CONFIG_FILE} | ||
31 | + | ||
32 | +COPY --from=builder /build/api/${PROJECT} ./ | ||
33 | +COPY --from=builder /build/cmd/chart-server/api/etc/${CONFIG_FILE} ./etc/ | ||
34 | + | ||
35 | +EXPOSE 8080 | ||
36 | +ENTRYPOINT ./${PROJECT} -f etc/${CONFIG_FILE} |
cmd/chart-server/api/core.go
0 → 100644
1 | +package main | ||
2 | + | ||
3 | +import ( | ||
4 | + "flag" | ||
5 | + "fmt" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
8 | + "net/http" | ||
9 | + "strings" | ||
10 | + | ||
11 | + "github.com/golang-jwt/jwt/v4/request" | ||
12 | + | ||
13 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/config" | ||
14 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/handler" | ||
15 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
16 | + | ||
17 | + "github.com/zeromicro/go-zero/core/conf" | ||
18 | + "github.com/zeromicro/go-zero/rest" | ||
19 | +) | ||
20 | + | ||
21 | +var configFile = flag.String("f", "etc/core.yaml", "the config file") | ||
22 | + | ||
23 | +func main() { | ||
24 | + flag.Parse() | ||
25 | + | ||
26 | + var c config.Config | ||
27 | + conf.MustLoad(*configFile, &c) | ||
28 | + | ||
29 | + // 默认的token头 Authorization 修改未 x-token | ||
30 | + request.AuthorizationHeaderExtractor = &request.PostExtractionFilter{ | ||
31 | + request.HeaderExtractor{"x-mmm-accesstoken"}, func(tok string) (string, error) { | ||
32 | + // Should be a bearer token | ||
33 | + if len(tok) > 6 && strings.ToUpper(tok[0:7]) == "BEARER " { | ||
34 | + return tok[7:], nil | ||
35 | + } | ||
36 | + return tok, nil | ||
37 | + }, | ||
38 | + } | ||
39 | + | ||
40 | + // 初始化Domain里面的配置 | ||
41 | + domain.ProjectName = c.Name | ||
42 | + | ||
43 | + opts := make([]rest.RunOption, 0) | ||
44 | + // cors | ||
45 | + opt := rest.WithCustomCors(func(header http.Header) { | ||
46 | + header.Set("Access-Control-Allow-Headers", "*") | ||
47 | + }, func(writer http.ResponseWriter) { | ||
48 | + | ||
49 | + }) | ||
50 | + opts = append(opts, opt) | ||
51 | + | ||
52 | + server := rest.MustNewServer(c.RestConf, opts...) | ||
53 | + defer server.Stop() | ||
54 | + | ||
55 | + ctx := svc.NewServiceContext(c) | ||
56 | + handler.RegisterHandlers(server, ctx) | ||
57 | + | ||
58 | + db.Migrate(ctx.DB) | ||
59 | + | ||
60 | + fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) | ||
61 | + server.Start() | ||
62 | +} |
cmd/chart-server/api/etc/core.yaml
0 → 100644
1 | +Name: Core | ||
2 | +Host: 0.0.0.0 | ||
3 | +Port: 8080 | ||
4 | + | ||
5 | +Verbose: false | ||
6 | + | ||
7 | +JwtAuth: | ||
8 | + AccessSecret: digital-platform | ||
9 | + Expire: 360000 | ||
10 | + | ||
11 | +Redis: | ||
12 | + Host: 127.0.0.1:6379 | ||
13 | + Type: node | ||
14 | + Pass: | ||
15 | +DB: | ||
16 | + DataSource: host=106.52.103.187 user=postgres password=UYXN134KUm8TeE7 dbname=skateboard-test port=25431 sslmode=disable TimeZone=Asia/Shanghai |
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-bchart/pkg/config" | ||
7 | +) | ||
8 | + | ||
9 | +type Config struct { | ||
10 | + rest.RestConf | ||
11 | + config.Config | ||
12 | + Redis redis.RedisConf `json:",optional"` | ||
13 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result" | ||
5 | + "net/http" | ||
6 | + | ||
7 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
11 | +) | ||
12 | + | ||
13 | +func DeleteChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
14 | + return func(w http.ResponseWriter, r *http.Request) { | ||
15 | + var req types.ChartDeleteRequest | ||
16 | + if err := httpx.Parse(r, &req); err != nil { | ||
17 | + httpx.ErrorCtx(r.Context(), w, err) | ||
18 | + return | ||
19 | + } | ||
20 | + | ||
21 | + l := chart.NewDeleteChartLogic(r.Context(), svcCtx) | ||
22 | + resp, err := l.DeleteChart(&req) | ||
23 | + result.HttpResult(r, w, resp, err) | ||
24 | + } | ||
25 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result" | ||
5 | + "net/http" | ||
6 | + | ||
7 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
11 | +) | ||
12 | + | ||
13 | +func GetChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
14 | + return func(w http.ResponseWriter, r *http.Request) { | ||
15 | + var req types.ChartGetRequest | ||
16 | + if err := httpx.Parse(r, &req); err != nil { | ||
17 | + httpx.ErrorCtx(r.Context(), w, err) | ||
18 | + return | ||
19 | + } | ||
20 | + | ||
21 | + l := chart.NewGetChartLogic(r.Context(), svcCtx) | ||
22 | + resp, err := l.GetChart(&req) | ||
23 | + result.HttpResult(r, w, resp, err) | ||
24 | + } | ||
25 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result" | ||
5 | + "net/http" | ||
6 | + | ||
7 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
11 | +) | ||
12 | + | ||
13 | +func RenameChartSortHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
14 | + return func(w http.ResponseWriter, r *http.Request) { | ||
15 | + var req types.ChartRenameRequest | ||
16 | + if err := httpx.Parse(r, &req); err != nil { | ||
17 | + httpx.ErrorCtx(r.Context(), w, err) | ||
18 | + return | ||
19 | + } | ||
20 | + | ||
21 | + l := chart.NewRenameChartSortLogic(r.Context(), svcCtx) | ||
22 | + resp, err := l.RenameChartSort(&req) | ||
23 | + result.HttpResult(r, w, resp, err) | ||
24 | + } | ||
25 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result" | ||
5 | + "net/http" | ||
6 | + | ||
7 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
11 | +) | ||
12 | + | ||
13 | +func SaveChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
14 | + return func(w http.ResponseWriter, r *http.Request) { | ||
15 | + var req types.ChartSaveRequest | ||
16 | + if err := httpx.Parse(r, &req); err != nil { | ||
17 | + httpx.ErrorCtx(r.Context(), w, err) | ||
18 | + return | ||
19 | + } | ||
20 | + | ||
21 | + l := chart.NewSaveChartLogic(r.Context(), svcCtx) | ||
22 | + resp, err := l.SaveChart(&req) | ||
23 | + result.HttpResult(r, w, resp, err) | ||
24 | + } | ||
25 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result" | ||
5 | + "net/http" | ||
6 | + | ||
7 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
11 | +) | ||
12 | + | ||
13 | +func SearchChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
14 | + return func(w http.ResponseWriter, r *http.Request) { | ||
15 | + var req types.ChartSearchRequest | ||
16 | + if err := httpx.Parse(r, &req); err != nil { | ||
17 | + httpx.ErrorCtx(r.Context(), w, err) | ||
18 | + return | ||
19 | + } | ||
20 | + | ||
21 | + l := chart.NewSearchChartLogic(r.Context(), svcCtx) | ||
22 | + resp, err := l.SearchChart(&req) | ||
23 | + result.HttpResult(r, w, resp, err) | ||
24 | + } | ||
25 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result" | ||
5 | + "net/http" | ||
6 | + | ||
7 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
11 | +) | ||
12 | + | ||
13 | +func UpdateChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
14 | + return func(w http.ResponseWriter, r *http.Request) { | ||
15 | + var req types.ChartUpdateRequest | ||
16 | + if err := httpx.Parse(r, &req); err != nil { | ||
17 | + httpx.ErrorCtx(r.Context(), w, err) | ||
18 | + return | ||
19 | + } | ||
20 | + | ||
21 | + l := chart.NewUpdateChartLogic(r.Context(), svcCtx) | ||
22 | + resp, err := l.UpdateChart(&req) | ||
23 | + result.HttpResult(r, w, resp, err) | ||
24 | + } | ||
25 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result" | ||
5 | + "net/http" | ||
6 | + | ||
7 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
11 | +) | ||
12 | + | ||
13 | +func UpdateChartSortHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||
14 | + return func(w http.ResponseWriter, r *http.Request) { | ||
15 | + var req types.ChartUpdateSortRequest | ||
16 | + if err := httpx.Parse(r, &req); err != nil { | ||
17 | + httpx.ErrorCtx(r.Context(), w, err) | ||
18 | + return | ||
19 | + } | ||
20 | + | ||
21 | + l := chart.NewUpdateChartSortLogic(r.Context(), svcCtx) | ||
22 | + resp, err := l.UpdateChartSort(&req) | ||
23 | + result.HttpResult(r, w, resp, err) | ||
24 | + } | ||
25 | +} |
1 | +// Code generated by goctl. DO NOT EDIT. | ||
2 | +package handler | ||
3 | + | ||
4 | +import ( | ||
5 | + "net/http" | ||
6 | + | ||
7 | + chart "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/handler/chart" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
9 | + | ||
10 | + "github.com/zeromicro/go-zero/rest" | ||
11 | +) | ||
12 | + | ||
13 | +func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { | ||
14 | + server.AddRoutes( | ||
15 | + []rest.Route{ | ||
16 | + { | ||
17 | + Method: http.MethodGet, | ||
18 | + Path: "/chart/:id", | ||
19 | + Handler: chart.GetChartHandler(serverCtx), | ||
20 | + }, | ||
21 | + { | ||
22 | + Method: http.MethodPost, | ||
23 | + Path: "/chart", | ||
24 | + Handler: chart.SaveChartHandler(serverCtx), | ||
25 | + }, | ||
26 | + { | ||
27 | + Method: http.MethodDelete, | ||
28 | + Path: "/chart/:id", | ||
29 | + Handler: chart.DeleteChartHandler(serverCtx), | ||
30 | + }, | ||
31 | + { | ||
32 | + Method: http.MethodPut, | ||
33 | + Path: "/chart/:id", | ||
34 | + Handler: chart.UpdateChartHandler(serverCtx), | ||
35 | + }, | ||
36 | + { | ||
37 | + Method: http.MethodPost, | ||
38 | + Path: "/chart/search", | ||
39 | + Handler: chart.SearchChartHandler(serverCtx), | ||
40 | + }, | ||
41 | + { | ||
42 | + Method: http.MethodPost, | ||
43 | + Path: "/chart/move", | ||
44 | + Handler: chart.UpdateChartSortHandler(serverCtx), | ||
45 | + }, | ||
46 | + { | ||
47 | + Method: http.MethodPost, | ||
48 | + Path: "/chart/rename", | ||
49 | + Handler: chart.RenameChartSortHandler(serverCtx), | ||
50 | + }, | ||
51 | + }, | ||
52 | + rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret), | ||
53 | + rest.WithPrefix("/v1"), | ||
54 | + ) | ||
55 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr" | ||
8 | + | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
11 | + | ||
12 | + "github.com/zeromicro/go-zero/core/logx" | ||
13 | +) | ||
14 | + | ||
15 | +type DeleteChartLogic struct { | ||
16 | + logx.Logger | ||
17 | + ctx context.Context | ||
18 | + svcCtx *svc.ServiceContext | ||
19 | +} | ||
20 | + | ||
21 | +func NewDeleteChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteChartLogic { | ||
22 | + return &DeleteChartLogic{ | ||
23 | + Logger: logx.WithContext(ctx), | ||
24 | + ctx: ctx, | ||
25 | + svcCtx: svcCtx, | ||
26 | + } | ||
27 | +} | ||
28 | + | ||
29 | +func (l *DeleteChartLogic) DeleteChart(req *types.ChartDeleteRequest) (resp *types.ChartDeleteResponse, err error) { | ||
30 | + var ( | ||
31 | + conn = l.svcCtx.DefaultDBConn() | ||
32 | + chart *domain.Chart | ||
33 | + tenantId = contextdata.GetTenantFromCtx(l.ctx) | ||
34 | + ) | ||
35 | + resp = &types.ChartDeleteResponse{} | ||
36 | + if chart, err = l.svcCtx.ChartRepository.FindOne(l.ctx, conn, req.Id); err != nil { | ||
37 | + return nil, xerr.NewErrMsgErr("不存在", err) | ||
38 | + } | ||
39 | + if chart.TenantId != tenantId { | ||
40 | + return nil, xerr.NewErrMsgErr("无权限", nil) | ||
41 | + } | ||
42 | + // TODO:图表被引用不能删除 | ||
43 | + // TODO:删除下级图表 | ||
44 | + if chart, err = l.svcCtx.ChartRepository.Delete(l.ctx, conn, chart); err != nil { | ||
45 | + return nil, xerr.NewErrMsgErr("删除失败", err) | ||
46 | + } | ||
47 | + | ||
48 | + resp = &types.ChartDeleteResponse{} | ||
49 | + return | ||
50 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
8 | + | ||
9 | + "github.com/zeromicro/go-zero/core/logx" | ||
10 | +) | ||
11 | + | ||
12 | +type GetChartLogic struct { | ||
13 | + logx.Logger | ||
14 | + ctx context.Context | ||
15 | + svcCtx *svc.ServiceContext | ||
16 | +} | ||
17 | + | ||
18 | +func NewGetChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetChartLogic { | ||
19 | + return &GetChartLogic{ | ||
20 | + Logger: logx.WithContext(ctx), | ||
21 | + ctx: ctx, | ||
22 | + svcCtx: svcCtx, | ||
23 | + } | ||
24 | +} | ||
25 | + | ||
26 | +func (l *GetChartLogic) GetChart(req *types.ChartGetRequest) (resp *types.ChartGetResponse, err error) { | ||
27 | + // todo: add your logic here and delete this line | ||
28 | + | ||
29 | + return | ||
30 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr" | ||
8 | + | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
11 | + | ||
12 | + "github.com/zeromicro/go-zero/core/logx" | ||
13 | +) | ||
14 | + | ||
15 | +type RenameChartSortLogic struct { | ||
16 | + logx.Logger | ||
17 | + ctx context.Context | ||
18 | + svcCtx *svc.ServiceContext | ||
19 | +} | ||
20 | + | ||
21 | +func NewRenameChartSortLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RenameChartSortLogic { | ||
22 | + return &RenameChartSortLogic{ | ||
23 | + Logger: logx.WithContext(ctx), | ||
24 | + ctx: ctx, | ||
25 | + svcCtx: svcCtx, | ||
26 | + } | ||
27 | +} | ||
28 | + | ||
29 | +func (l *RenameChartSortLogic) RenameChartSort(req *types.ChartRenameRequest) (resp *types.ChartRenameResponse, err error) { | ||
30 | + var ( | ||
31 | + conn = l.svcCtx.DefaultDBConn() | ||
32 | + chart *domain.Chart | ||
33 | + tenantId = contextdata.GetTenantFromCtx(l.ctx) | ||
34 | + charts []*domain.Chart | ||
35 | + ) | ||
36 | + resp = &types.ChartRenameResponse{} | ||
37 | + if chart, err = l.svcCtx.ChartRepository.FindOne(l.ctx, conn, req.Id); err != nil { | ||
38 | + return nil, xerr.NewErrMsgErr("不存在", err) | ||
39 | + } | ||
40 | + if chart.Name == req.Name { | ||
41 | + return | ||
42 | + } | ||
43 | + if chart.TenantId != tenantId { | ||
44 | + return nil, xerr.NewErrMsgErr("无权限", nil) | ||
45 | + } | ||
46 | + _, charts, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexChartPid(tenantId, chart.Pid)()) | ||
47 | + if err != nil { | ||
48 | + return nil, xerr.NewErr(err) | ||
49 | + } | ||
50 | + for i := range charts { | ||
51 | + if charts[i].Name == req.Name { | ||
52 | + return nil, xerr.NewErrMsgErr("名称已存在", nil) | ||
53 | + } | ||
54 | + } | ||
55 | + chart.Name = req.Name | ||
56 | + chart, err = l.svcCtx.ChartRepository.UpdateWithVersion(l.ctx, conn, chart) | ||
57 | + if err != nil { | ||
58 | + return nil, xerr.NewErr(err) | ||
59 | + } | ||
60 | + return | ||
61 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/tool" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr" | ||
10 | + | ||
11 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
12 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
13 | + | ||
14 | + "github.com/zeromicro/go-zero/core/logx" | ||
15 | +) | ||
16 | + | ||
17 | +type SaveChartLogic struct { | ||
18 | + logx.Logger | ||
19 | + ctx context.Context | ||
20 | + svcCtx *svc.ServiceContext | ||
21 | +} | ||
22 | + | ||
23 | +func NewSaveChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SaveChartLogic { | ||
24 | + return &SaveChartLogic{ | ||
25 | + Logger: logx.WithContext(ctx), | ||
26 | + ctx: ctx, | ||
27 | + svcCtx: svcCtx, | ||
28 | + } | ||
29 | +} | ||
30 | + | ||
31 | +func (l *SaveChartLogic) SaveChart(req *types.ChartSaveRequest) (resp *types.ChartSaveResponse, err error) { | ||
32 | + var ( | ||
33 | + conn = l.svcCtx.DefaultDBConn() | ||
34 | + parentChart *domain.Chart | ||
35 | + tenantId = contextdata.GetTenantFromCtx(l.ctx) | ||
36 | + ) | ||
37 | + if !domain.ChartTypeContain(req.Type) { | ||
38 | + return nil, xerr.NewErrMsgErr("未知类型:"+req.Type, err) | ||
39 | + } | ||
40 | + chart := &domain.Chart{ | ||
41 | + Name: req.Name, | ||
42 | + Type: req.Type, | ||
43 | + Pid: req.Pid, | ||
44 | + Sort: 1, | ||
45 | + Group: tool.Krand(10, tool.KC_RAND_KIND_UPPER), | ||
46 | + TenantId: tenantId, | ||
47 | + } | ||
48 | + if chart.Name == "" { | ||
49 | + chart.Name = chart.RandName() | ||
50 | + } | ||
51 | + if req.Pid > 0 { | ||
52 | + if parentChart, err = l.svcCtx.ChartRepository.FindOne(l.ctx, conn, req.Pid); err != nil { | ||
53 | + return nil, xerr.NewErrMsgErr("父级不存在", err) | ||
54 | + } | ||
55 | + chart.Group = parentChart.Group | ||
56 | + } | ||
57 | + // current sort | ||
58 | + if currentSortChart, _ := l.svcCtx.ChartRepository.FindOneByGroup(l.ctx, conn, tenantId, req.Pid); currentSortChart != nil { | ||
59 | + chart.Sort = currentSortChart.Sort + 1 | ||
60 | + } | ||
61 | + if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error { | ||
62 | + if chart, err = l.svcCtx.ChartRepository.Insert(ctx, conn, chart); err != nil { | ||
63 | + return err | ||
64 | + } | ||
65 | + return nil | ||
66 | + }, true); err != nil { | ||
67 | + return nil, xerr.NewErrMsgErr("创建失败", err) | ||
68 | + } | ||
69 | + resp = &types.ChartSaveResponse{ | ||
70 | + Chart: types.NewChartItem(chart), | ||
71 | + } | ||
72 | + return | ||
73 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "github.com/samber/lo" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xcollection" | ||
9 | + | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
11 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
12 | + | ||
13 | + "github.com/zeromicro/go-zero/core/logx" | ||
14 | +) | ||
15 | + | ||
16 | +type SearchChartLogic struct { | ||
17 | + logx.Logger | ||
18 | + ctx context.Context | ||
19 | + svcCtx *svc.ServiceContext | ||
20 | +} | ||
21 | + | ||
22 | +func NewSearchChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SearchChartLogic { | ||
23 | + return &SearchChartLogic{ | ||
24 | + Logger: logx.WithContext(ctx), | ||
25 | + ctx: ctx, | ||
26 | + svcCtx: svcCtx, | ||
27 | + } | ||
28 | +} | ||
29 | + | ||
30 | +func (l *SearchChartLogic) SearchChart(req *types.ChartSearchRequest) (resp interface{}, err error) { | ||
31 | + var ( | ||
32 | + conn = l.svcCtx.DefaultDBConn() | ||
33 | + tenantId = contextdata.GetTenantFromCtx(l.ctx) | ||
34 | + charts []*domain.Chart | ||
35 | + ) | ||
36 | + _, charts, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexTenantId(tenantId)()) | ||
37 | + var chartItems = make([]xcollection.TreeNode, 0) | ||
38 | + lo.EveryBy(charts, func(chart *domain.Chart) bool { | ||
39 | + // 按类型过滤 | ||
40 | + if len(req.IncludeTypes) > 0 { | ||
41 | + if !lo.Contains(req.IncludeTypes, chart.Type) { | ||
42 | + return true | ||
43 | + } | ||
44 | + } | ||
45 | + chartItems = append(chartItems, types.NewChartItem(chart)) | ||
46 | + return true | ||
47 | + }) | ||
48 | + if req.DataStyle == "tree" { | ||
49 | + resp = xcollection.NewTree(chartItems).Nodes | ||
50 | + } else if req.DataStyle == "flat" { | ||
51 | + resp = chartItems | ||
52 | + } else { | ||
53 | + resp = chartItems | ||
54 | + } | ||
55 | + return map[string]interface{}{ | ||
56 | + "list": resp, | ||
57 | + }, nil | ||
58 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
8 | + | ||
9 | + "github.com/zeromicro/go-zero/core/logx" | ||
10 | +) | ||
11 | + | ||
12 | +type UpdateChartLogic struct { | ||
13 | + logx.Logger | ||
14 | + ctx context.Context | ||
15 | + svcCtx *svc.ServiceContext | ||
16 | +} | ||
17 | + | ||
18 | +func NewUpdateChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateChartLogic { | ||
19 | + return &UpdateChartLogic{ | ||
20 | + Logger: logx.WithContext(ctx), | ||
21 | + ctx: ctx, | ||
22 | + svcCtx: svcCtx, | ||
23 | + } | ||
24 | +} | ||
25 | + | ||
26 | +func (l *UpdateChartLogic) UpdateChart(req *types.ChartUpdateRequest) (resp *types.ChartUpdateResponse, err error) { | ||
27 | + // todo: add your logic here and delete this line | ||
28 | + | ||
29 | + return | ||
30 | +} |
1 | +package chart | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "github.com/zeromicro/go-zero/core/logx" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata" | ||
11 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr" | ||
12 | +) | ||
13 | + | ||
14 | +type UpdateChartSortLogic struct { | ||
15 | + logx.Logger | ||
16 | + ctx context.Context | ||
17 | + svcCtx *svc.ServiceContext | ||
18 | +} | ||
19 | + | ||
20 | +func NewUpdateChartSortLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateChartSortLogic { | ||
21 | + return &UpdateChartSortLogic{ | ||
22 | + Logger: logx.WithContext(ctx), | ||
23 | + ctx: ctx, | ||
24 | + svcCtx: svcCtx, | ||
25 | + } | ||
26 | +} | ||
27 | + | ||
28 | +func (l *UpdateChartSortLogic) UpdateChartSort(req *types.ChartUpdateSortRequest) (resp *types.ChartUpdateSortResponse, err error) { | ||
29 | + var ( | ||
30 | + conn = l.svcCtx.DefaultDBConn() | ||
31 | + chart *domain.Chart | ||
32 | + chartGroupBefore []*domain.Chart | ||
33 | + chartGroupAfter []*domain.Chart | ||
34 | + tenantId = contextdata.GetTenantFromCtx(l.ctx) | ||
35 | + ) | ||
36 | + if chart, err = l.svcCtx.ChartRepository.FindOne(l.ctx, conn, req.Id); err != nil { | ||
37 | + return nil, xerr.NewErrMsgErr("不存在", err) | ||
38 | + } | ||
39 | + var ( | ||
40 | + oldPid = chart.Pid | ||
41 | + inSameGroup = req.Pid == chart.Pid // 同一分组 | ||
42 | + ) | ||
43 | + chart.Sort = req.Index + 1 | ||
44 | + chart.Pid = req.Pid | ||
45 | + resp = &types.ChartUpdateSortResponse{} | ||
46 | + // TODO:图表被引用不能移动 | ||
47 | + | ||
48 | + // 移动到同一个分组某个位置 | ||
49 | + if inSameGroup { | ||
50 | + if _, chartGroupBefore, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexChartPid(tenantId, req.Pid)()); err != nil { | ||
51 | + return nil, xerr.NewErr(err) | ||
52 | + } | ||
53 | + if err = l.updateSort(domain.Reorder(chartGroupBefore, chart, true)); err != nil { | ||
54 | + return nil, xerr.NewErrMsgErr("更新失败", err) | ||
55 | + } | ||
56 | + return | ||
57 | + } | ||
58 | + // 移动到不同分组的某个位置 | ||
59 | + if _, chartGroupBefore, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexChartPid(tenantId, oldPid)()); err != nil { | ||
60 | + return nil, xerr.NewErr(err) | ||
61 | + } | ||
62 | + if _, chartGroupAfter, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexChartPid(tenantId, req.Pid)()); err != nil { | ||
63 | + return nil, xerr.NewErr(err) | ||
64 | + } | ||
65 | + list := domain.Reorder(chartGroupBefore, chart, false) | ||
66 | + list = append(list, domain.Reorder(chartGroupAfter, chart, true)...) | ||
67 | + if err = l.updateSort(list); err != nil { | ||
68 | + return nil, xerr.NewErrMsgErr("更新失败", err) | ||
69 | + } | ||
70 | + return | ||
71 | +} | ||
72 | + | ||
73 | +func (l *UpdateChartSortLogic) updateSort(charts []*domain.Chart) error { | ||
74 | + if err := transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error { | ||
75 | + for i := range charts { | ||
76 | + if charts[i].Id == 0 { | ||
77 | + continue | ||
78 | + } | ||
79 | + if _, err := l.svcCtx.ChartRepository.Update(l.ctx, conn, charts[i]); err != nil { | ||
80 | + return err | ||
81 | + } | ||
82 | + } | ||
83 | + return nil | ||
84 | + }, true); err != nil { | ||
85 | + return err | ||
86 | + } | ||
87 | + return nil | ||
88 | +} |
1 | +package svc | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/tiptok/gocomm/pkg/cache/gzcache" | ||
5 | + "github.com/zeromicro/go-zero/core/stores/redis" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/config" | ||
7 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/repository" | ||
8 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/cache" | ||
11 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/database" | ||
12 | + "gorm.io/gorm" | ||
13 | +) | ||
14 | + | ||
15 | +type ServiceContext struct { | ||
16 | + Config config.Config | ||
17 | + DB *gorm.DB | ||
18 | + Redis *redis.Redis | ||
19 | + RedisCache gzcache.GZCache | ||
20 | + ChartRepository domain.ChartRepository | ||
21 | +} | ||
22 | + | ||
23 | +func NewServiceContext(c config.Config) *ServiceContext { | ||
24 | + db := database.OpenGormPGDB(c.DB.DataSource) | ||
25 | + mlCache := cache.NewMultiLevelCache([]string{c.Redis.Host}, c.Redis.Pass) | ||
26 | + redisCache := gzcache.NewClusterCache([]string{c.Redis.Host}, c.Redis.Pass) | ||
27 | + redis, _ := redis.NewRedis(redis.RedisConf{Host: c.Redis.Host, Pass: c.Redis.Pass, Type: "node"}) | ||
28 | + | ||
29 | + return &ServiceContext{ | ||
30 | + Config: c, | ||
31 | + DB: db, | ||
32 | + RedisCache: redisCache, | ||
33 | + Redis: redis, | ||
34 | + ChartRepository: repository.NewChartRepository(cache.NewCachedRepository(mlCache)), | ||
35 | + } | ||
36 | +} | ||
37 | + | ||
38 | +func (svc *ServiceContext) DefaultDBConn() transaction.Conn { | ||
39 | + return transaction.NewTransactionContext(svc.DB) | ||
40 | +} |
cmd/chart-server/api/internal/types/chart.go
0 → 100644
cmd/chart-server/api/internal/types/new.go
0 → 100644
1 | +package types | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/jinzhu/copier" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
6 | +) | ||
7 | + | ||
8 | +func NewChartItem(chart *domain.Chart) ChartItem { | ||
9 | + item := ChartItem{} | ||
10 | + copier.Copy(&item, chart) | ||
11 | + //item.Charts = make([]ChartItem, 0) | ||
12 | + return item | ||
13 | +} |
cmd/chart-server/api/internal/types/types.go
0 → 100644
1 | +// Code generated by goctl. DO NOT EDIT. | ||
2 | +package types | ||
3 | + | ||
4 | +type ChartGetRequest struct { | ||
5 | + Id int64 `path:"id"` | ||
6 | +} | ||
7 | + | ||
8 | +type ChartGetResponse struct { | ||
9 | + Chart ChartItem `json:"chart"` | ||
10 | +} | ||
11 | + | ||
12 | +type ChartSaveRequest struct { | ||
13 | + Pid int64 `json:"pid,optional"` // 父级ID | ||
14 | + Type string `json:"type"` // 类型 report:报表 group:分组 chart:图表 | ||
15 | + Name string `json:"name,optional"` // 名称 | ||
16 | +} | ||
17 | + | ||
18 | +type ChartSaveResponse struct { | ||
19 | + Chart ChartItem `json:"chart"` | ||
20 | +} | ||
21 | + | ||
22 | +type ChartDeleteRequest struct { | ||
23 | + Id int64 `path:"id"` | ||
24 | +} | ||
25 | + | ||
26 | +type ChartDeleteResponse struct { | ||
27 | +} | ||
28 | + | ||
29 | +type ChartUpdateRequest struct { | ||
30 | + Id int64 `path:"id"` | ||
31 | +} | ||
32 | + | ||
33 | +type ChartUpdateResponse struct { | ||
34 | +} | ||
35 | + | ||
36 | +type ChartSearchRequest struct { | ||
37 | + IncludeTypes []string `json:"includeTypes,optional"` //包含的类型: 类型 report:报表 group:分组 chart:图表(未指定返回所有) | ||
38 | + DataStyle string `json:"dataStyle,optional"` // 数据样式 tree:树形 flat:平铺 | ||
39 | +} | ||
40 | + | ||
41 | +type ChartSearchResponse struct { | ||
42 | + List []ChartItem `json:"list"` | ||
43 | + Total int64 `json:"total"` | ||
44 | +} | ||
45 | + | ||
46 | +type ChartUpdateSortRequest struct { | ||
47 | + Id int64 `json:"id"` | ||
48 | + Pid int64 `json:"pid"` | ||
49 | + Index int `json:"index"` // 元素下标,从0开始 | ||
50 | +} | ||
51 | + | ||
52 | +type ChartUpdateSortResponse struct { | ||
53 | +} | ||
54 | + | ||
55 | +type ChartRenameRequest struct { | ||
56 | + Id int64 `json:"id"` | ||
57 | + Name string `json:"name"` // 名称 | ||
58 | +} | ||
59 | + | ||
60 | +type ChartRenameResponse struct { | ||
61 | +} | ||
62 | + | ||
63 | +type ChartItem struct { | ||
64 | + Id int64 `json:"id,optional"` // ID | ||
65 | + Pid int64 `json:"pid,optional"` // 父级ID | ||
66 | + Type string `json:"type,optional"` // 类型 report:报表 group:分组 chart:图表 | ||
67 | + Sort int64 `json:"sort,optional"` // 排序 | ||
68 | + Name string `json:"name,optional"` // 名称 | ||
69 | + //Charts []ChartItem `json:"charts,optional"` | ||
70 | +} |
cmd/chart-server/doc/dsl/api/chart.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: v1 | ||
14 | + group: chart | ||
15 | + jwt: JwtAuth | ||
16 | +) | ||
17 | +service Core { | ||
18 | + @handler getChart | ||
19 | + post /chart/:id (ChartGetRequest) returns (ChartGetResponse) | ||
20 | + @handler saveChart | ||
21 | + post /chart (ChartSaveRequest) returns (ChartSaveResponse) | ||
22 | + @handler deleteChart | ||
23 | + delete /chart/:id (ChartDeleteRequest) returns (ChartDeleteResponse) | ||
24 | + @handler updateChart | ||
25 | + put /chart/:id (ChartUpdateRequest) returns (ChartUpdateResponse) | ||
26 | + @handler searchChart | ||
27 | + post /chart/search (ChartSearchRequest) returns (ChartSearchResponse) | ||
28 | +} | ||
29 | + | ||
30 | +type ( | ||
31 | + ChartGetRequest { | ||
32 | + Id int64 `path:"id"` | ||
33 | + } | ||
34 | + ChartGetResponse struct{ | ||
35 | + Chart ChartItem `json:"chart"` | ||
36 | + } | ||
37 | + | ||
38 | + ChartSaveRequest struct{ | ||
39 | + Chart ChartItem `json:"chart"` | ||
40 | + } | ||
41 | + ChartSaveResponse struct{} | ||
42 | + | ||
43 | + ChartDeleteRequest struct{ | ||
44 | + Id int64 `path:"id"` | ||
45 | + } | ||
46 | + ChartDeleteResponse struct{} | ||
47 | + | ||
48 | + ChartUpdateRequest struct{ | ||
49 | + Id int64 `path:"id"` | ||
50 | + Chart ChartItem `json:"chart"` | ||
51 | + } | ||
52 | + ChartUpdateResponse struct{} | ||
53 | + | ||
54 | + ChartSearchRequest struct{ | ||
55 | + Page int `json:"page"` | ||
56 | + Size int `json:"size"` | ||
57 | + } | ||
58 | + ChartSearchResponse{ | ||
59 | + List []ChartItem `json:"list"` | ||
60 | + Total int64 `json:"total"` | ||
61 | + } | ||
62 | + ChartItem struct{ | ||
63 | + | ||
64 | + } | ||
65 | +) |
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: v1 | ||
14 | + group: chart-setting | ||
15 | + jwt: JwtAuth | ||
16 | +) | ||
17 | +service Core { | ||
18 | + @handler getChartSetting | ||
19 | + post /chart-setting/:id (ChartSettingGetRequest) returns (ChartSettingGetResponse) | ||
20 | + @handler saveChartSetting | ||
21 | + post /chart-setting (ChartSettingSaveRequest) returns (ChartSettingSaveResponse) | ||
22 | + @handler deleteChartSetting | ||
23 | + delete /chart-setting/:id (ChartSettingDeleteRequest) returns (ChartSettingDeleteResponse) | ||
24 | + @handler updateChartSetting | ||
25 | + put /chart-setting/:id (ChartSettingUpdateRequest) returns (ChartSettingUpdateResponse) | ||
26 | + @handler searchChartSetting | ||
27 | + post /chart-setting/search (ChartSettingSearchRequest) returns (ChartSettingSearchResponse) | ||
28 | +} | ||
29 | + | ||
30 | +type ( | ||
31 | + ChartSettingGetRequest { | ||
32 | + Id int64 `path:"id"` | ||
33 | + } | ||
34 | + ChartSettingGetResponse struct{ | ||
35 | + ChartSetting ChartSettingItem `json:"chartSetting"` | ||
36 | + } | ||
37 | + | ||
38 | + ChartSettingSaveRequest struct{ | ||
39 | + ChartSetting ChartSettingItem `json:"chartSetting"` | ||
40 | + } | ||
41 | + ChartSettingSaveResponse struct{} | ||
42 | + | ||
43 | + ChartSettingDeleteRequest struct{ | ||
44 | + Id int64 `path:"id"` | ||
45 | + } | ||
46 | + ChartSettingDeleteResponse struct{} | ||
47 | + | ||
48 | + ChartSettingUpdateRequest struct{ | ||
49 | + Id int64 `path:"id"` | ||
50 | + ChartSetting ChartSettingItem `json:"chartSetting"` | ||
51 | + } | ||
52 | + ChartSettingUpdateResponse struct{} | ||
53 | + | ||
54 | + ChartSettingSearchRequest struct{ | ||
55 | + Page int `json:"page"` | ||
56 | + Size int `json:"size"` | ||
57 | + } | ||
58 | + ChartSettingSearchResponse{ | ||
59 | + List []ChartSettingItem `json:"list"` | ||
60 | + Total int64 `json:"total"` | ||
61 | + } | ||
62 | + ChartSettingItem struct{ | ||
63 | + | ||
64 | + } | ||
65 | +) |
cmd/chart-server/doc/dsl/rpc/chart.proto
0 → 100644
1 | + | ||
2 | +syntax = "proto3"; | ||
3 | + | ||
4 | +option go_package ="./pb"; | ||
5 | + | ||
6 | +package pb; | ||
7 | + | ||
8 | +message ChartGetReq { | ||
9 | + int64 Id = 1; | ||
10 | +} | ||
11 | +message ChartGetResp{ | ||
12 | + ChartItem User = 1; | ||
13 | +} | ||
14 | + | ||
15 | +message ChartSaveReq { | ||
16 | + | ||
17 | +} | ||
18 | +message ChartSaveResp{ | ||
19 | + | ||
20 | +} | ||
21 | + | ||
22 | +message ChartDeleteReq { | ||
23 | + int64 Id = 1; | ||
24 | +} | ||
25 | +message ChartDeleteResp{ | ||
26 | + | ||
27 | +} | ||
28 | + | ||
29 | +message ChartUpdateReq { | ||
30 | + int64 Id = 1; | ||
31 | +} | ||
32 | +message ChartUpdateResp{ | ||
33 | + | ||
34 | +} | ||
35 | + | ||
36 | +message ChartSearchReq { | ||
37 | + int64 PageNumber = 1; | ||
38 | + int64 PageSize = 2; | ||
39 | +} | ||
40 | +message ChartSearchResp{ | ||
41 | + repeated ChartItem List =1; | ||
42 | + int64 Total =2; | ||
43 | +} | ||
44 | +message ChartItem { | ||
45 | + | ||
46 | +} | ||
47 | + | ||
48 | +service ChartService { | ||
49 | + rpc ChartGet(ChartGetReq) returns(ChartGetResp); | ||
50 | + rpc ChartSave(ChartSaveReq) returns(ChartSaveResp); | ||
51 | + rpc ChartDelete(ChartDeleteReq) returns(ChartDeleteResp); | ||
52 | + rpc ChartUpdate(ChartUpdateReq) returns(ChartUpdateResp); | ||
53 | + rpc ChartSearch(ChartSearchReq) returns(ChartSearchResp); | ||
54 | +} |
1 | + | ||
2 | +syntax = "proto3"; | ||
3 | + | ||
4 | +option go_package ="./pb"; | ||
5 | + | ||
6 | +package pb; | ||
7 | + | ||
8 | +message ChartSettingGetReq { | ||
9 | + int64 Id = 1; | ||
10 | +} | ||
11 | +message ChartSettingGetResp{ | ||
12 | + ChartSettingItem User = 1; | ||
13 | +} | ||
14 | + | ||
15 | +message ChartSettingSaveReq { | ||
16 | + | ||
17 | +} | ||
18 | +message ChartSettingSaveResp{ | ||
19 | + | ||
20 | +} | ||
21 | + | ||
22 | +message ChartSettingDeleteReq { | ||
23 | + int64 Id = 1; | ||
24 | +} | ||
25 | +message ChartSettingDeleteResp{ | ||
26 | + | ||
27 | +} | ||
28 | + | ||
29 | +message ChartSettingUpdateReq { | ||
30 | + int64 Id = 1; | ||
31 | +} | ||
32 | +message ChartSettingUpdateResp{ | ||
33 | + | ||
34 | +} | ||
35 | + | ||
36 | +message ChartSettingSearchReq { | ||
37 | + int64 PageNumber = 1; | ||
38 | + int64 PageSize = 2; | ||
39 | +} | ||
40 | +message ChartSettingSearchResp{ | ||
41 | + repeated ChartSettingItem List =1; | ||
42 | + int64 Total =2; | ||
43 | +} | ||
44 | +message ChartSettingItem { | ||
45 | + | ||
46 | +} | ||
47 | + | ||
48 | +service ChartSettingService { | ||
49 | + rpc ChartSettingGet(ChartSettingGetReq) returns(ChartSettingGetResp); | ||
50 | + rpc ChartSettingSave(ChartSettingSaveReq) returns(ChartSettingSaveResp); | ||
51 | + rpc ChartSettingDelete(ChartSettingDeleteReq) returns(ChartSettingDeleteResp); | ||
52 | + rpc ChartSettingUpdate(ChartSettingUpdateReq) returns(ChartSettingUpdateResp); | ||
53 | + rpc ChartSettingSearch(ChartSettingSearchReq) returns(ChartSettingSearchResp); | ||
54 | +} |
cmd/chart-server/interanl/pkg/db/migrate.go
0 → 100644
1 | +package models | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
6 | + "gorm.io/gorm" | ||
7 | + "gorm.io/plugin/soft_delete" | ||
8 | +) | ||
9 | + | ||
10 | +type Chart struct { | ||
11 | + Id int64 // ID | ||
12 | + Pid int64 `gorm:"index:idx_chart_t_pid_sort"` // 父级ID | ||
13 | + Type string // 类型 | ||
14 | + Sort int `gorm:"index:idx_chart_t_pid_sort"` // 排序 | ||
15 | + Name string // 名称 | ||
16 | + Group string `gorm:"index:idx_chart_group"` // 分组 | ||
17 | + TenantId int64 `gorm:"index:idx_chart_t_pid_sort"` // 租户ID | ||
18 | + | ||
19 | + CreatedAt int64 `json:",omitempty"` | ||
20 | + UpdatedAt int64 `json:",omitempty"` | ||
21 | + DeletedAt int64 `json:",omitempty"` | ||
22 | + Version int `json:",omitempty"` | ||
23 | + IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"` | ||
24 | +} | ||
25 | + | ||
26 | +func (m *Chart) TableName() string { | ||
27 | + return "chart" | ||
28 | +} | ||
29 | + | ||
30 | +func (m *Chart) BeforeCreate(tx *gorm.DB) (err error) { | ||
31 | + // m.CreatedAt = time.Now().Unix() | ||
32 | + // m.UpdatedAt = time.Now().Unix() | ||
33 | + return | ||
34 | +} | ||
35 | + | ||
36 | +func (m *Chart) BeforeUpdate(tx *gorm.DB) (err error) { | ||
37 | + // m.UpdatedAt = time.Now().Unix() | ||
38 | + return | ||
39 | +} | ||
40 | + | ||
41 | +func (m *Chart) CacheKeyFunc() string { | ||
42 | + if m.Id == 0 { | ||
43 | + return "" | ||
44 | + } | ||
45 | + return fmt.Sprintf("%v:cache:%v:id:%v", domain.ProjectName, m.TableName(), m.Id) | ||
46 | +} | ||
47 | + | ||
48 | +func (m *Chart) CacheKeyFuncByObject(obj interface{}) string { | ||
49 | + if v, ok := obj.(*Chart); ok { | ||
50 | + return v.CacheKeyFunc() | ||
51 | + } | ||
52 | + return "" | ||
53 | +} | ||
54 | + | ||
55 | +func (m *Chart) CachePrimaryKeyFunc() string { | ||
56 | + if len("") == 0 { | ||
57 | + return "" | ||
58 | + } | ||
59 | + return fmt.Sprintf("%v:cache:%v:primarykey:%v", domain.ProjectName, m.TableName(), "key") | ||
60 | +} |
1 | +package models | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
6 | + "gorm.io/gorm" | ||
7 | +) | ||
8 | + | ||
9 | +type ChartSetting struct { | ||
10 | + Id int64 // ID | ||
11 | + ChartId int64 // 图表ID | ||
12 | + Property string // 属性 | ||
13 | + Style string // 样式 | ||
14 | + Series string // 系列值-数据绑定 | ||
15 | + | ||
16 | + CreatedAt int64 `json:",omitempty"` | ||
17 | + UpdatedAt int64 `json:",omitempty"` | ||
18 | + DeletedAt int64 `json:",omitempty"` | ||
19 | + Version int `json:",omitempty"` | ||
20 | +} | ||
21 | + | ||
22 | +func (m *ChartSetting) TableName() string { | ||
23 | + return "chart_setting" | ||
24 | +} | ||
25 | + | ||
26 | +func (m *ChartSetting) BeforeCreate(tx *gorm.DB) (err error) { | ||
27 | + // m.CreatedAt = time.Now().Unix() | ||
28 | + // m.UpdatedAt = time.Now().Unix() | ||
29 | + return | ||
30 | +} | ||
31 | + | ||
32 | +func (m *ChartSetting) BeforeUpdate(tx *gorm.DB) (err error) { | ||
33 | + // m.UpdatedAt = time.Now().Unix() | ||
34 | + return | ||
35 | +} | ||
36 | + | ||
37 | +func (m *ChartSetting) CacheKeyFunc() string { | ||
38 | + if m.Id == 0 { | ||
39 | + return "" | ||
40 | + } | ||
41 | + return fmt.Sprintf("%v:cache:%v:id:%v", domain.ProjectName, m.TableName(), m.Id) | ||
42 | +} | ||
43 | + | ||
44 | +func (m *ChartSetting) CacheKeyFuncByObject(obj interface{}) string { | ||
45 | + if v, ok := obj.(*ChartSetting); ok { | ||
46 | + return v.CacheKeyFunc() | ||
47 | + } | ||
48 | + return "" | ||
49 | +} | ||
50 | + | ||
51 | +func (m *ChartSetting) CachePrimaryKeyFunc() string { | ||
52 | + if len("") == 0 { | ||
53 | + return "" | ||
54 | + } | ||
55 | + return fmt.Sprintf("%v:cache:%v:primarykey:%v", domain.ProjectName, m.TableName(), "key") | ||
56 | +} |
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-bchart/cmd/chart-server/interanl/pkg/db/models" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
11 | + "gorm.io/gorm" | ||
12 | +) | ||
13 | + | ||
14 | +type ChartRepository struct { | ||
15 | + *cache.CachedRepository | ||
16 | +} | ||
17 | + | ||
18 | +func (repository *ChartRepository) Insert(ctx context.Context, conn transaction.Conn, dm *domain.Chart) (*domain.Chart, error) { | ||
19 | + var ( | ||
20 | + err error | ||
21 | + m = &models.Chart{} | ||
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 *ChartRepository) Update(ctx context.Context, conn transaction.Conn, dm *domain.Chart) (*domain.Chart, error) { | ||
36 | + var ( | ||
37 | + err error | ||
38 | + m *models.Chart | ||
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 *ChartRepository) UpdateWithVersion(ctx context.Context, transaction transaction.Conn, dm *domain.Chart) (*domain.Chart, error) { | ||
55 | + var ( | ||
56 | + err error | ||
57 | + m *models.Chart | ||
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 *ChartRepository) Delete(ctx context.Context, conn transaction.Conn, dm *domain.Chart) (*domain.Chart, error) { | ||
79 | + var ( | ||
80 | + tx = conn.DB() | ||
81 | + m = &models.Chart{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 *ChartRepository) FindOne(ctx context.Context, conn transaction.Conn, id int64) (*domain.Chart, error) { | ||
94 | + var ( | ||
95 | + err error | ||
96 | + tx = conn.DB() | ||
97 | + m = new(models.Chart) | ||
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.Chart) | ||
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 *ChartRepository) Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*domain.Chart, error) { | ||
115 | + var ( | ||
116 | + tx = conn.DB() | ||
117 | + ms []*models.Chart | ||
118 | + dms = make([]*domain.Chart, 0) | ||
119 | + total int64 | ||
120 | + ) | ||
121 | + queryFunc := func() (interface{}, error) { | ||
122 | + tx = tx.Model(&ms).Order("pid asc").Order("sort asc") | ||
123 | + if v, ok := queryOptions["tenantId"]; ok { | ||
124 | + tx.Where("tenant_id = ?", v) | ||
125 | + } | ||
126 | + if v, ok := queryOptions["pid"]; ok { | ||
127 | + tx.Where("pid = ?", v) | ||
128 | + } | ||
129 | + if total, tx = transaction.PaginationAndCount(ctx, tx, queryOptions, &ms); tx.Error != nil { | ||
130 | + return dms, tx.Error | ||
131 | + } | ||
132 | + return dms, nil | ||
133 | + } | ||
134 | + | ||
135 | + if _, err := repository.Query(queryFunc); err != nil { | ||
136 | + return 0, nil, err | ||
137 | + } | ||
138 | + | ||
139 | + for _, item := range ms { | ||
140 | + if dm, err := repository.ModelToDomainModel(item); err != nil { | ||
141 | + return 0, dms, err | ||
142 | + } else { | ||
143 | + dms = append(dms, dm) | ||
144 | + } | ||
145 | + } | ||
146 | + return total, dms, nil | ||
147 | +} | ||
148 | + | ||
149 | +func (repository *ChartRepository) FindOneByGroup(ctx context.Context, conn transaction.Conn, tenantId, pid int64) (*domain.Chart, error) { | ||
150 | + var ( | ||
151 | + err error | ||
152 | + tx = conn.DB() | ||
153 | + m = new(models.Chart) | ||
154 | + ) | ||
155 | + queryFunc := func() (interface{}, error) { | ||
156 | + tx = tx.Model(m).Where("tenant_id = ?", tenantId).Where("pid = ?", pid).Order("sort desc").Limit(1).First(m) | ||
157 | + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { | ||
158 | + return nil, domain.ErrNotFound | ||
159 | + } | ||
160 | + return m, tx.Error | ||
161 | + } | ||
162 | + if _, err = repository.Query(queryFunc); err != nil { | ||
163 | + return nil, err | ||
164 | + } | ||
165 | + return repository.ModelToDomainModel(m) | ||
166 | +} | ||
167 | + | ||
168 | +func (repository *ChartRepository) ModelToDomainModel(from *models.Chart) (*domain.Chart, error) { | ||
169 | + to := &domain.Chart{} | ||
170 | + err := copier.Copy(to, from) | ||
171 | + return to, err | ||
172 | +} | ||
173 | + | ||
174 | +func (repository *ChartRepository) DomainModelToModel(from *domain.Chart) (*models.Chart, error) { | ||
175 | + to := &models.Chart{} | ||
176 | + err := copier.Copy(to, from) | ||
177 | + return to, err | ||
178 | +} | ||
179 | + | ||
180 | +func NewChartRepository(cache *cache.CachedRepository) domain.ChartRepository { | ||
181 | + return &ChartRepository{CachedRepository: cache} | ||
182 | +} |
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-bchart/cmd/chart-server/interanl/pkg/db/models" | ||
9 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction" | ||
10 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain" | ||
11 | + "gorm.io/gorm" | ||
12 | +) | ||
13 | + | ||
14 | +type ChartSettingRepository struct { | ||
15 | + *cache.CachedRepository | ||
16 | +} | ||
17 | + | ||
18 | +func (repository *ChartSettingRepository) Insert(ctx context.Context, conn transaction.Conn, dm *domain.ChartSetting) (*domain.ChartSetting, error) { | ||
19 | + var ( | ||
20 | + err error | ||
21 | + m = &models.ChartSetting{} | ||
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 *ChartSettingRepository) Update(ctx context.Context, conn transaction.Conn, dm *domain.ChartSetting) (*domain.ChartSetting, error) { | ||
36 | + var ( | ||
37 | + err error | ||
38 | + m *models.ChartSetting | ||
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 *ChartSettingRepository) UpdateWithVersion(ctx context.Context, transaction transaction.Conn, dm *domain.ChartSetting) (*domain.ChartSetting, error) { | ||
55 | + var ( | ||
56 | + err error | ||
57 | + m *models.ChartSetting | ||
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 *ChartSettingRepository) Delete(ctx context.Context, conn transaction.Conn, dm *domain.ChartSetting) (*domain.ChartSetting, error) { | ||
79 | + var ( | ||
80 | + tx = conn.DB() | ||
81 | + m = &models.ChartSetting{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 *ChartSettingRepository) FindOne(ctx context.Context, conn transaction.Conn, id int64) (*domain.ChartSetting, error) { | ||
94 | + var ( | ||
95 | + err error | ||
96 | + tx = conn.DB() | ||
97 | + m = new(models.ChartSetting) | ||
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.ChartSetting) | ||
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 *ChartSettingRepository) Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*domain.ChartSetting, error) { | ||
115 | + var ( | ||
116 | + tx = conn.DB() | ||
117 | + ms []*models.ChartSetting | ||
118 | + dms = make([]*domain.ChartSetting, 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 *ChartSettingRepository) ModelToDomainModel(from *models.ChartSetting) (*domain.ChartSetting, error) { | ||
144 | + to := &domain.ChartSetting{} | ||
145 | + err := copier.Copy(to, from) | ||
146 | + return to, err | ||
147 | +} | ||
148 | + | ||
149 | +func (repository *ChartSettingRepository) DomainModelToModel(from *domain.ChartSetting) (*models.ChartSetting, error) { | ||
150 | + to := &models.ChartSetting{} | ||
151 | + err := copier.Copy(to, from) | ||
152 | + return to, err | ||
153 | +} | ||
154 | + | ||
155 | +func NewChartSettingRepository(cache *cache.CachedRepository) domain.ChartSettingRepository { | ||
156 | + return &ChartSettingRepository{CachedRepository: cache} | ||
157 | +} |
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{}, v interface{}) (int64, *gorm.DB) { | ||
99 | + var total int64 | ||
100 | + var enableCounter bool = false | ||
101 | + if v, ok := params["enableCounter"]; ok { | ||
102 | + enableCounter = v.(bool) | ||
103 | + } | ||
104 | + if enableCounter { | ||
105 | + tx = tx.Count(&total) | ||
106 | + return total, tx | ||
107 | + } | ||
108 | + tx = tx.Count(&total) | ||
109 | + if tx.Error != nil { | ||
110 | + return total, tx | ||
111 | + } | ||
112 | + if v, ok := params["offset"]; ok { | ||
113 | + tx.Offset(v.(int)) | ||
114 | + } | ||
115 | + if v, ok := params["limit"]; ok { | ||
116 | + tx.Limit(v.(int)) | ||
117 | + } | ||
118 | + if tx = tx.Find(v); tx.Error != nil { | ||
119 | + return 0, tx | ||
120 | + } | ||
121 | + return total, tx | ||
122 | +} |
1 | +package domain | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction" | ||
6 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/tool" | ||
7 | + "golang.org/x/exp/slices" | ||
8 | + "sort" | ||
9 | + "strings" | ||
10 | +) | ||
11 | + | ||
12 | +type Chart struct { | ||
13 | + Id int64 // ID | ||
14 | + Pid int64 // 父级ID | ||
15 | + Type string // 类型 | ||
16 | + Sort int // 排序 | ||
17 | + Name string // 名称 | ||
18 | + Group string // 分组 | ||
19 | + TenantId int64 // 租户ID | ||
20 | + | ||
21 | + CreatedAt int64 `json:",omitempty"` | ||
22 | + UpdatedAt int64 `json:",omitempty"` | ||
23 | + DeletedAt int64 `json:",omitempty"` | ||
24 | + Version int `json:",omitempty"` | ||
25 | +} | ||
26 | + | ||
27 | +type ChartRepository interface { | ||
28 | + Insert(ctx context.Context, conn transaction.Conn, dm *Chart) (*Chart, error) | ||
29 | + Update(ctx context.Context, conn transaction.Conn, dm *Chart) (*Chart, error) | ||
30 | + UpdateWithVersion(ctx context.Context, conn transaction.Conn, dm *Chart) (*Chart, error) | ||
31 | + Delete(ctx context.Context, conn transaction.Conn, dm *Chart) (*Chart, error) | ||
32 | + FindOne(ctx context.Context, conn transaction.Conn, id int64) (*Chart, error) | ||
33 | + Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*Chart, error) | ||
34 | + FindOneByGroup(ctx context.Context, conn transaction.Conn, tenantId, pid int64) (*Chart, error) | ||
35 | +} | ||
36 | + | ||
37 | +/*************** 索引函数 开始****************/ | ||
38 | + | ||
39 | +func IndexTenantId(tenantId int64) IndexQueryOptionFunc { | ||
40 | + return func() QueryOptions { | ||
41 | + return NewQueryOptions().WithKV("tenantId", tenantId) | ||
42 | + } | ||
43 | +} | ||
44 | + | ||
45 | +func IndexChartPid(tenantId, pid int64) IndexQueryOptionFunc { | ||
46 | + return func() QueryOptions { | ||
47 | + return NewQueryOptions().WithKV("tenantId", tenantId).WithKV("pid", pid) | ||
48 | + } | ||
49 | +} | ||
50 | + | ||
51 | +func (m *Chart) Identify() interface{} { | ||
52 | + if m.Id == 0 { | ||
53 | + return nil | ||
54 | + } | ||
55 | + return m.Id | ||
56 | +} | ||
57 | + | ||
58 | +func (m *Chart) RandName() string { | ||
59 | + randNumber := tool.Krand(6, tool.KC_RAND_KIND_NUM) | ||
60 | + var result strings.Builder | ||
61 | + switch m.Type { | ||
62 | + case ChartTypeReport: | ||
63 | + result.WriteString("未命名报表") | ||
64 | + case ChartTypeGroup: | ||
65 | + result.WriteString("未命名分组") | ||
66 | + case ChartTypeChart: | ||
67 | + result.WriteString("未命名图表") | ||
68 | + } | ||
69 | + result.WriteString(randNumber) | ||
70 | + return result.String() | ||
71 | +} | ||
72 | + | ||
73 | +// Reorder 重新排序charts | ||
74 | +// appendFlag:true 追加 target 到 charts | ||
75 | +// appendFlag:false 不追加 | ||
76 | +func Reorder(charts []*Chart, target *Chart, appendFlag bool) []*Chart { | ||
77 | + // 重排序 | ||
78 | + var result = make([]*Chart, 0) | ||
79 | + sort.SliceStable(charts, func(i, j int) bool { | ||
80 | + return charts[i].Sort < charts[j].Sort | ||
81 | + }) | ||
82 | + for index, c := range charts { | ||
83 | + c.Sort = index + 1 | ||
84 | + if c.Id == target.Id { | ||
85 | + result = append(result, &Chart{Sort: c.Sort}) // 旧节点继续追击啊,ID设置为0,后面跳过更新 | ||
86 | + continue | ||
87 | + } | ||
88 | + result = append(result, c) | ||
89 | + } | ||
90 | + // 插入新的节点 | ||
91 | + if appendFlag { | ||
92 | + index := slices.IndexFunc(result, func(c *Chart) bool { | ||
93 | + if float64(c.Sort) > float64(target.Sort)-0.5 { | ||
94 | + return true | ||
95 | + } | ||
96 | + return false | ||
97 | + }) | ||
98 | + if index == -1 { | ||
99 | + index = len(result) | ||
100 | + } | ||
101 | + result = slices.Insert(result, index, target) | ||
102 | + } | ||
103 | + // 重排序 | ||
104 | + var index = 1 | ||
105 | + for i := range result { | ||
106 | + if result[i].Id == 0 { | ||
107 | + continue | ||
108 | + } | ||
109 | + result[i].Sort = index | ||
110 | + index++ | ||
111 | + } | ||
112 | + return result | ||
113 | +} |
1 | +package domain | ||
2 | + | ||
3 | +type ChartProperty struct { | ||
4 | + Title *Title `json:"title,optional"` // 标题 | ||
5 | + FilterRule *FilterRule `json:"filterRule,optional"` // 过滤规则 | ||
6 | + Series []*Series `json:"series,optional"` // 系列 | ||
7 | + Cover string `json:"cover,optional"` // 封面 | ||
8 | + | ||
9 | + //XAxis interface{} `json:"xAxis"` // X轴 | ||
10 | + //YAxis interface{} `json:"yAxis"` // Y轴 | ||
11 | +} | ||
12 | + | ||
13 | +type Series struct { | ||
14 | + Type string `json:"type"` // 图表类型 (记录型表格:RecordTable-1 总体指标:MetricsCard-1 容器卡片:ContainerCard-1 四分图:QuarterChart-1) | ||
15 | + Name string `json:"name"` // 名称 | ||
16 | + Data interface{} `json:"data"` // 保存配置的时候置空 | ||
17 | + Config *DataConfig `json:"config"` // 配置 | ||
18 | +} | ||
19 | + | ||
20 | +type Title struct { | ||
21 | + MainTitle *TitleInfo `json:"main,optional"` // 主标题 | ||
22 | + SubTitle *TitleInfo `json:"sub,optional"` // 副标题 | ||
23 | +} | ||
24 | + | ||
25 | +type TitleInfo struct { | ||
26 | + Content string `json:"content,optional"` // 标题内容 | ||
27 | + Description *Description `json:"description,optional"` // 描述 | ||
28 | +} | ||
29 | + | ||
30 | +type Description struct { | ||
31 | + Type string `json:"type"` // text:文字 attachment:附件 | ||
32 | + Remark string `json:"remark,optional"` // 备注说明 | ||
33 | + Attachment *File `json:"attachment,optional"` // 附件 | ||
34 | +} | ||
35 | + | ||
36 | +type File struct { | ||
37 | + FileName string `json:"name"` | ||
38 | + Url string `json:"url"` | ||
39 | +} | ||
40 | + | ||
41 | +type FilterRule struct { | ||
42 | + FilterItems []*FilterItem `json:"items"` | ||
43 | +} | ||
44 | + | ||
45 | +type FilterItem struct { | ||
46 | + FieldItem | ||
47 | +} | ||
48 | + | ||
49 | +// FieldItem 字段项 | ||
50 | +type FieldItem struct { | ||
51 | + Source | ||
52 | + Field string `json:"field"` | ||
53 | +} | ||
54 | + | ||
55 | +type Source struct { | ||
56 | + From string `json:"from"` // 数据源类型 ByteBank:字库 User:用户自定义 | ||
57 | + SourceId int64 `json:"id"` // 数据源ID(from值为ByteBank时有值) | ||
58 | + CustomData interface{} `json:"customData"` // 自定义数据(from值为User时有值) | ||
59 | +} | ||
60 | + | ||
61 | +type DataConfig struct { | ||
62 | + Source Source `json:"source"` | ||
63 | +} |
1 | +package domain | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction" | ||
6 | +) | ||
7 | + | ||
8 | +type ChartSetting struct { | ||
9 | + Id int64 // ID | ||
10 | + ChartId int64 // 图表ID | ||
11 | + Property string // 属性 | ||
12 | + Style string // 样式 | ||
13 | + Series string // 系列值-数据绑定 | ||
14 | + | ||
15 | + CreatedAt int64 `json:",omitempty"` | ||
16 | + UpdatedAt int64 `json:",omitempty"` | ||
17 | + DeletedAt int64 `json:",omitempty"` | ||
18 | + Version int `json:",omitempty"` | ||
19 | +} | ||
20 | + | ||
21 | +type ChartSettingRepository interface { | ||
22 | + Insert(ctx context.Context, conn transaction.Conn, dm *ChartSetting) (*ChartSetting, error) | ||
23 | + Update(ctx context.Context, conn transaction.Conn, dm *ChartSetting) (*ChartSetting, error) | ||
24 | + UpdateWithVersion(ctx context.Context, conn transaction.Conn, dm *ChartSetting) (*ChartSetting, error) | ||
25 | + Delete(ctx context.Context, conn transaction.Conn, dm *ChartSetting) (*ChartSetting, error) | ||
26 | + FindOne(ctx context.Context, conn transaction.Conn, id int64) (*ChartSetting, error) | ||
27 | + Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*ChartSetting, error) | ||
28 | +} | ||
29 | + | ||
30 | +func (m *ChartSetting) Identify() interface{} { | ||
31 | + if m.Id == 0 { | ||
32 | + return nil | ||
33 | + } | ||
34 | + return m.Id | ||
35 | +} |
1 | +package domain | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/magiconair/properties/assert" | ||
5 | + "testing" | ||
6 | +) | ||
7 | + | ||
8 | +func TestReorder(t *testing.T) { | ||
9 | + //chartsAfter: | ||
10 | + //target := &Chart{Id: 3, Pid: 0, Sort: 3} | ||
11 | + inputs := []struct { | ||
12 | + charts []*Chart | ||
13 | + targetId int64 | ||
14 | + sortIndex int | ||
15 | + pid int64 | ||
16 | + appendFlag bool | ||
17 | + want []int64 | ||
18 | + }{ | ||
19 | + {createChartsBefore(), 3, 1, 0, true, []int64{3, 1, 2}}, | ||
20 | + {createChartsBefore(), 3, 2, 0, true, []int64{1, 3, 2}}, | ||
21 | + {createChartsBefore(), 3, 3, 0, true, []int64{1, 2, 3}}, | ||
22 | + {createChartsBefore(), 3, 4, 0, true, []int64{1, 2, 3}}, | ||
23 | + {createChartsBefore(), 3, 1, 1, false, []int64{1, 2}}, | ||
24 | + } | ||
25 | + for _, input := range inputs { | ||
26 | + assert.Equal(t, chartIds(Reorder(input.charts, &Chart{Id: input.targetId, Pid: input.pid, Sort: input.sortIndex}, input.appendFlag)), input.want) | ||
27 | + } | ||
28 | +} | ||
29 | +func chartIds(charts []*Chart) []int64 { | ||
30 | + var idList = make([]int64, 0) | ||
31 | + for _, c := range charts { | ||
32 | + if c.Id == 0 { | ||
33 | + continue | ||
34 | + } | ||
35 | + idList = append(idList, c.Id) | ||
36 | + } | ||
37 | + return idList | ||
38 | +} | ||
39 | + | ||
40 | +func createChartsBefore() []*Chart { | ||
41 | + return []*Chart{ | ||
42 | + {Id: 1, Pid: 0, Sort: 1}, | ||
43 | + {Id: 2, Pid: 0, Sort: 2}, | ||
44 | + {Id: 3, Pid: 0, Sort: 3}, | ||
45 | + } | ||
46 | +} | ||
47 | + | ||
48 | +func createChartsAfter() []*Chart { | ||
49 | + return []*Chart{ | ||
50 | + {Id: 5, Pid: 1, Sort: 1}, | ||
51 | + {Id: 6, Pid: 1, Sort: 2}, | ||
52 | + {Id: 7, Pid: 1, Sort: 3}, | ||
53 | + } | ||
54 | +} |
1 | +package domain | ||
2 | + | ||
3 | +import "github.com/samber/lo" | ||
4 | + | ||
5 | +const ( | ||
6 | + ChartTypeReport = "report" // 报表 | ||
7 | + ChartTypeGroup = "group" // 分组 | ||
8 | + ChartTypeChart = "chart" // 图表 | ||
9 | +) | ||
10 | + | ||
11 | +func ChartTypeContain(t string) bool { | ||
12 | + return lo.Contains([]string{ChartTypeReport, ChartTypeGroup, ChartTypeChart}, t) | ||
13 | +} |
1 | +package domain | ||
2 | + | ||
3 | +import "reflect" | ||
4 | + | ||
5 | +func OffsetLimit(page, size int) (offset int, limit int) { | ||
6 | + if page == 0 { | ||
7 | + page = 1 | ||
8 | + } | ||
9 | + if size == 0 { | ||
10 | + size = 20 | ||
11 | + } | ||
12 | + offset = (page - 1) * size | ||
13 | + limit = size | ||
14 | + return | ||
15 | +} | ||
16 | + | ||
17 | +type QueryOptions map[string]interface{} | ||
18 | + | ||
19 | +func NewQueryOptions() QueryOptions { | ||
20 | + options := make(map[string]interface{}) | ||
21 | + return options | ||
22 | +} | ||
23 | +func (options QueryOptions) WithOffsetLimit(page, size int) QueryOptions { | ||
24 | + offset, limit := OffsetLimit(page, size) | ||
25 | + options["offset"] = offset | ||
26 | + options["limit"] = limit | ||
27 | + return options | ||
28 | +} | ||
29 | + | ||
30 | +func (options QueryOptions) WithKV(key string, value interface{}) QueryOptions { | ||
31 | + if reflect.ValueOf(value).IsZero() { | ||
32 | + return options | ||
33 | + } | ||
34 | + options[key] = value | ||
35 | + return options | ||
36 | +} | ||
37 | + | ||
38 | +func (options QueryOptions) EnableCounter() QueryOptions { | ||
39 | + return options.WithKV("enableCounter", true) | ||
40 | +} | ||
41 | + | ||
42 | +func (options QueryOptions) Copy() QueryOptions { | ||
43 | + newOptions := NewQueryOptions() | ||
44 | + for k, v := range options { | ||
45 | + newOptions[k] = v | ||
46 | + } | ||
47 | + return newOptions | ||
48 | +} | ||
49 | + | ||
50 | +type IndexQueryOptionFunc func() QueryOptions |
cmd/chart-server/interanl/pkg/domain/vars.go
0 → 100644
1 | +package gateway | ||
2 | + | ||
3 | +import ( | ||
4 | + "encoding/json" | ||
5 | + "fmt" | ||
6 | +) | ||
7 | + | ||
8 | +type MessageCode struct { | ||
9 | + Code int `json:"code"` | ||
10 | + Msg string `json:"msg"` | ||
11 | +} | ||
12 | + | ||
13 | +// Response 统一消息返回格式 | ||
14 | +type Response struct { | ||
15 | + MessageCode | ||
16 | + Data json.RawMessage `json:"data"` | ||
17 | +} | ||
18 | + | ||
19 | +// | ||
20 | +//type Request struct { | ||
21 | +// Url string | ||
22 | +// Method string | ||
23 | +// Param interface{} | ||
24 | +//} | ||
25 | + | ||
26 | +type HttpError struct { | ||
27 | + Base Response | ||
28 | +} | ||
29 | + | ||
30 | +func (e HttpError) Error() string { | ||
31 | + return fmt.Sprintf("HttpError code:%d msg:%s", e.Base.Code, e.Base.Msg) | ||
32 | +} |
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 | + "net/http" | ||
10 | + "strings" | ||
11 | + "time" | ||
12 | +) | ||
13 | + | ||
14 | +type Service struct { | ||
15 | + Timeout time.Duration | ||
16 | + host string | ||
17 | + Interceptor func(msg string) | ||
18 | + ServiceName string | ||
19 | + service httpc.Service | ||
20 | +} | ||
21 | + | ||
22 | +func NewService(name string, host string, timeout time.Duration, opts ...httpc.Option) Service { | ||
23 | + client := &http.Client{} | ||
24 | + client.Timeout = timeout | ||
25 | + | ||
26 | + service := Service{ | ||
27 | + service: httpc.NewServiceWithClient(name, client, opts...), | ||
28 | + } | ||
29 | + return service | ||
30 | +} | ||
31 | + | ||
32 | +func (gateway Service) Do(ctx context.Context, url string, method string, val interface{}, result interface{}) error { | ||
33 | + var ( | ||
34 | + baseResponse = &Response{} | ||
35 | + begin = time.Now() | ||
36 | + ) | ||
37 | + response, err := gateway.service.Do(ctx, gateway.host+url, method, val) | ||
38 | + defer func() { | ||
39 | + jsonParam, _ := json.Marshal(val) | ||
40 | + jsonData, _ := json.Marshal(result) | ||
41 | + if err != nil { | ||
42 | + result = err.Error() | ||
43 | + } | ||
44 | + if gateway.Interceptor != nil { | ||
45 | + gateway.Interceptor(fmt.Sprintf("【网关】%v | %v%v | %v : %v \n-->> %v \n<<-- %v", time.Since(begin), gateway.host, url, strings.ToUpper(method), | ||
46 | + result, | ||
47 | + string(jsonParam), | ||
48 | + string(jsonData), | ||
49 | + )) | ||
50 | + } | ||
51 | + }() | ||
52 | + if err != nil { | ||
53 | + return err | ||
54 | + } | ||
55 | + if response.StatusCode != http.StatusOK { | ||
56 | + return HttpError{ | ||
57 | + Base: Response{ | ||
58 | + MessageCode: MessageCode{ | ||
59 | + Code: response.StatusCode, | ||
60 | + Msg: response.Status, | ||
61 | + }, | ||
62 | + }, | ||
63 | + } | ||
64 | + } | ||
65 | + if err = httpc.ParseJsonBody(response, baseResponse); err != nil { | ||
66 | + return err | ||
67 | + } | ||
68 | + if err = mapping.UnmarshalJsonBytes(baseResponse.Data, result); err != nil { | ||
69 | + return err | ||
70 | + } | ||
71 | + | ||
72 | + return nil | ||
73 | +} |
1 | +package bytelib | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "net/http" | ||
6 | +) | ||
7 | + | ||
8 | +func (gateway *ByteMetadataService) TableDataPreview(ctx context.Context, request *TableDataPreviewRequest) (TablePreviewResponse, error) { | ||
9 | + var result TablePreviewResponse | ||
10 | + if err := gateway.Do(ctx, "/data/table-preview", http.MethodPost, request, &result); err != nil { | ||
11 | + return result, err | ||
12 | + } | ||
13 | + return result, nil | ||
14 | +} | ||
15 | + | ||
16 | +type TableDataPreviewRequest struct { | ||
17 | + ObjectId int64 `json:"objectId"` //表ID | ||
18 | + ObjectType string `json:"objectType"` //表类型 File:文件 MetaTable:元表 DBTable:数据库表 ;当对象类型为DBTable时 objectId 1:表操作日志 2.拆解模块日志 | ||
19 | + Where *TableQueryWhere `json:"where"` //查询条件 | ||
20 | + UseCache bool `json:"useCache"` | ||
21 | + HiddenData bool `json:"hiddenData"` // 隐藏数据,只返回结构 | ||
22 | +} | ||
23 | + | ||
24 | +type TableQueryWhere struct { | ||
25 | + PageNumber int64 `json:"pageNumber"` | ||
26 | + PageSize int64 `json:"pageSize"` | ||
27 | + Conditions []*TableQueryCondition `json:"conditions"` | ||
28 | +} | ||
29 | + | ||
30 | +type TableQueryCondition struct { | ||
31 | + Field *Field `json:"field"` //字段 | ||
32 | + Like string `json:"like"` //模糊匹配 | ||
33 | + In []string `json:"in"` //包含项 | ||
34 | + Ex []string `json:"ex"` //排除项 | ||
35 | + Order string `json:"order"` //排序 ASC正序 DESC降序 | ||
36 | +} | ||
37 | + | ||
38 | +type TablePreviewResponse TableData | ||
39 | + | ||
40 | +type TablePreview struct { | ||
41 | + Code int `json:"code"` | ||
42 | + Msg string `json:"msg"` | ||
43 | + Data *TableData `json:"data"` | ||
44 | +} | ||
45 | + | ||
46 | +type TableData struct { | ||
47 | + //表ID | ||
48 | + ObjectId int64 `json:"objectId"` | ||
49 | + //表名 | ||
50 | + Name string `json:"name"` | ||
51 | + //数据 | ||
52 | + Grid *TableDataGrid `json:"grid"` | ||
53 | + //字段 | ||
54 | + Fields []*Field `json:"fields"` | ||
55 | +} | ||
56 | + | ||
57 | +type TableDataGrid struct { | ||
58 | + //数据列表 | ||
59 | + List []interface{} `json:"list"` | ||
60 | + //总记录数 | ||
61 | + Total int64 `json:"total"` | ||
62 | +} |
1 | +package bytelib | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "net/http" | ||
6 | +) | ||
7 | + | ||
8 | +func (gateway *ByteMetadataService) TableInfo(ctx context.Context, request *TableInfoRequest) (TableInfoResponse, error) { | ||
9 | + var result TableInfoResponse | ||
10 | + if err := gateway.Do(ctx, "/tables/:tableId", http.MethodGet, request, &result); err != nil { | ||
11 | + return result, err | ||
12 | + } | ||
13 | + return result, nil | ||
14 | +} | ||
15 | + | ||
16 | +type TableInfoRequest struct { | ||
17 | + TableId int `path:"tableId"` | ||
18 | +} | ||
19 | +type TableInfoResponse struct { | ||
20 | + // 表Id | ||
21 | + TableId int `json:"tableId"` | ||
22 | + // 表类型 MainTable:主表 SideTable:副表 SubTable:分表 ExcelTable:Excel表 | ||
23 | + TableType string `json:"tableType"` | ||
24 | + // 名称 | ||
25 | + Name string `json:"name"` | ||
26 | + // 父级ID | ||
27 | + ParentId int `json:"parentId"` | ||
28 | + // 主表字段 | ||
29 | + //MainTableFields []*Field `json:"mainTableFields"` | ||
30 | + // 手动添加字段 | ||
31 | + //ManualFields []*Field `json:"manualFields"` | ||
32 | + // 数据列 | ||
33 | + Fields []*Field `json:"fields"` | ||
34 | + // 模块 应用于模块 1:数控中心 2:拆解模块 4:计算模块 | ||
35 | + Module int `json:"module"` | ||
36 | +} |
1 | +package bytelib | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "net/http" | ||
6 | +) | ||
7 | + | ||
8 | +func (gateway *ByteMetadataService) ObjectTableSearch(ctx context.Context, request ObjectTableSearchRequest) (ObjectTableSearchResponse, error) { | ||
9 | + result := ObjectTableSearchResponse{} | ||
10 | + if err := gateway.Do(ctx, "/data/table-object-search", http.MethodPost, request, &result); err != nil { | ||
11 | + return result, err | ||
12 | + } | ||
13 | + return result, nil | ||
14 | +} | ||
15 | + | ||
16 | +type ObjectTableSearchRequest struct { | ||
17 | + // 表名称 | ||
18 | + Name string `cname:"表名称" json:"name"` | ||
19 | + //ViewType string `cname:"视图类型 full:完整 main:主表关系" json:"viewType"` | ||
20 | + // 表类型 MainTable:主表 SideTable:副表 SubTable:分表 ExcelTable:Excel表 SubProcess:子过程 Schema:方案 | ||
21 | + TableTypes []string `cname:"表类型 MainTable:主表 SideTable:副表 SubTable:分表 ExcelTable:Excel表" json:"tableTypes" valid:"Required"` | ||
22 | + // 父级ID | ||
23 | + ParentId int `cname:"父级ID" json:"parentId"` | ||
24 | + // 模块 应用于模块 1:数控中心 2:拆解模块 4:计算模块 | ||
25 | + Module int `json:"module"` | ||
26 | + // 父级ID | ||
27 | + ParentTableId int `cname:"父级ID" json:"parentTableId"` | ||
28 | + // 返回结构信息 | ||
29 | + ReturnDetailStructInfo bool `cname:"返回具体的结构信息 默认不返回" json:"returnDetailStructInfo"` | ||
30 | + // 排除分组项,只返回一级列表;默认 false 不排除,连分组也返回 | ||
31 | + ReturnGroupItem bool `cname:"排除分组" json:"returnGroupItem"` | ||
32 | + // 排除指定表 | ||
33 | + ExcludeTables []int `cname:"排除指定表" json:"excludeTables"` | ||
34 | + FilterRules []*FilterRule `json:"filterRules"` | ||
35 | + TableId int `cname:"ID" json:"tableId"` | ||
36 | +} | ||
37 | + | ||
38 | +type ObjectTableSearchResponse struct { | ||
39 | + Count int `json:"count"` | ||
40 | + List []*Table `json:"list"` | ||
41 | +} | ||
42 | + | ||
43 | +type FilterRule struct { | ||
44 | + // *:匹配所有 | ||
45 | + TableType string `json:"tableType"` | ||
46 | + Status int `json:"status"` | ||
47 | +} | ||
48 | + | ||
49 | +type Table struct { | ||
50 | + // 序号 | ||
51 | + Id int `json:"id"` | ||
52 | + // 表Id | ||
53 | + TableId int `json:"tableId"` | ||
54 | + // 表类型 MainTable:主表 SideTable:副表 SubTable:分表 ExcelTable:Excel表 | ||
55 | + TableType string `json:"tableType"` | ||
56 | + // 名称 | ||
57 | + Name string `json:"name"` | ||
58 | + // 对应数据库名称 | ||
59 | + SQLName string `json:"sqlName,omitempty"` | ||
60 | + // 父级ID | ||
61 | + ParentId int `json:"parentId"` | ||
62 | + // 模块 应用于模块 1:数控中心 2:拆解模块 4:计算模块 | ||
63 | + Module int `json:"module"` | ||
64 | + // 标识 | ||
65 | + Flag string `json:"flag,omitempty"` | ||
66 | + // 启用状态 | ||
67 | + Status int `json:"status"` | ||
68 | + // 冲突状态 | ||
69 | + InConflict bool `json:"inConflict"` | ||
70 | + // 表字段 | ||
71 | + Fields []*Field `json:"fields"` | ||
72 | +} | ||
73 | + | ||
74 | +// Field 字段 | ||
75 | +type Field struct { | ||
76 | + // 名称 | ||
77 | + Name string `json:"name"` | ||
78 | + // 对应数据库名称 | ||
79 | + SQLName string `json:"sqlName"` | ||
80 | + // 对应数据库类型 | ||
81 | + SQLType string `json:"sqlType"` | ||
82 | + // 标识 1.主键 2:主表字段 3:手动添加 | ||
83 | + Flag int `json:"flag"` | ||
84 | +} |
1 | +package bytelib | ||
2 | + | ||
3 | +var ( | ||
4 | + MainTable TableType = "MainTable" // 主表 | ||
5 | + SideTable TableType = "SideTable" // 副表 | ||
6 | + SubTable TableType = "SubTable" // 分表 | ||
7 | + SchemaTable TableType = "Schema" // 方案 | ||
8 | + SubProcessTable TableType = "SubProcess" // 子过程 | ||
9 | + CalculateItem TableType = "CalculateItem" // 计算项 | ||
10 | + CalculateTable TableType = "CalculateTable" // 计算表 | ||
11 | + CalculateSet TableType = "CalculateSet" // 计算集 | ||
12 | +) | ||
13 | + | ||
14 | +type TableType string | ||
15 | + | ||
16 | +func (t TableType) ToString() string { | ||
17 | + return string(t) | ||
18 | +} |
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 | +} |
doc/cmd/command.md
0 → 100644
1 | +## 生成模型 | ||
2 | + | ||
3 | +``` | ||
4 | +goctl model mysql ddl -s .\doc\dsl\model\table.sql -d cmd/chart-server | ||
5 | +``` | ||
6 | + | ||
7 | +## api生成 | ||
8 | + | ||
9 | +``` | ||
10 | +goctl api go -api .\doc\dsl\api\core.api -dir cmd/chart-server/api -style go_zero | ||
11 | +``` | ||
12 | + | ||
13 | +## swagger 生成 | ||
14 | + | ||
15 | +``` | ||
16 | +goctl api plugin -plugin goctl-swagger="swagger -filename core.json" -api .\doc\dsl\api\core.api -dir .\doc\dsl\api | ||
17 | +``` | ||
18 | + | ||
19 | +## 镜像生成 | ||
20 | + | ||
21 | +``` | ||
22 | +docker build -f Dockerfile -t tiptok/sumifcc-bchart:1.0.0 . | ||
23 | +``` |
doc/dsl/api/core.api
0 → 100644
doc/dsl/api/core.json
0 → 100644
1 | +{ | ||
2 | + "swagger": "2.0", | ||
3 | + "info": { | ||
4 | + "title": "", | ||
5 | + "version": "" | ||
6 | + }, | ||
7 | + "schemes": [ | ||
8 | + "http", | ||
9 | + "https" | ||
10 | + ], | ||
11 | + "consumes": [ | ||
12 | + "application/json" | ||
13 | + ], | ||
14 | + "produces": [ | ||
15 | + "application/json" | ||
16 | + ], | ||
17 | + "paths": { | ||
18 | + "v1/chart": { | ||
19 | + "post": { | ||
20 | + "summary": "保存图表", | ||
21 | + "operationId": "saveChart", | ||
22 | + "responses": { | ||
23 | + "200": { | ||
24 | + "description": "A successful response.", | ||
25 | + "schema": { | ||
26 | + "$ref": "#/definitions/ChartSaveResponse" | ||
27 | + } | ||
28 | + } | ||
29 | + }, | ||
30 | + "parameters": [ | ||
31 | + { | ||
32 | + "name": "body", | ||
33 | + "in": "body", | ||
34 | + "required": true, | ||
35 | + "schema": { | ||
36 | + "$ref": "#/definitions/ChartSaveRequest" | ||
37 | + } | ||
38 | + } | ||
39 | + ], | ||
40 | + "requestBody": {}, | ||
41 | + "tags": [ | ||
42 | + "chart" | ||
43 | + ] | ||
44 | + } | ||
45 | + }, | ||
46 | + "v1/chart/rename": { | ||
47 | + "post": { | ||
48 | + "summary": "更新图表排序", | ||
49 | + "operationId": "renameChartSort", | ||
50 | + "responses": { | ||
51 | + "200": { | ||
52 | + "description": "A successful response.", | ||
53 | + "schema": { | ||
54 | + "$ref": "#/definitions/ChartUpdateSortResponse" | ||
55 | + } | ||
56 | + } | ||
57 | + }, | ||
58 | + "parameters": [ | ||
59 | + { | ||
60 | + "name": "body", | ||
61 | + "in": "body", | ||
62 | + "required": true, | ||
63 | + "schema": { | ||
64 | + "$ref": "#/definitions/ChartUpdateSortRequest" | ||
65 | + } | ||
66 | + } | ||
67 | + ], | ||
68 | + "requestBody": {}, | ||
69 | + "tags": [ | ||
70 | + "chart" | ||
71 | + ] | ||
72 | + } | ||
73 | + }, | ||
74 | + "v1/chart/search": { | ||
75 | + "post": { | ||
76 | + "summary": "搜索图表", | ||
77 | + "operationId": "searchChart", | ||
78 | + "responses": { | ||
79 | + "200": { | ||
80 | + "description": "A successful response.", | ||
81 | + "schema": { | ||
82 | + "$ref": "#/definitions/ChartSearchResponse" | ||
83 | + } | ||
84 | + } | ||
85 | + }, | ||
86 | + "parameters": [ | ||
87 | + { | ||
88 | + "name": "body", | ||
89 | + "in": "body", | ||
90 | + "required": true, | ||
91 | + "schema": { | ||
92 | + "$ref": "#/definitions/ChartSearchRequest" | ||
93 | + } | ||
94 | + } | ||
95 | + ], | ||
96 | + "requestBody": {}, | ||
97 | + "tags": [ | ||
98 | + "chart" | ||
99 | + ] | ||
100 | + } | ||
101 | + }, | ||
102 | + "v1/chart/sort": { | ||
103 | + "post": { | ||
104 | + "summary": "更新图表排序", | ||
105 | + "operationId": "updateChartSort", | ||
106 | + "responses": { | ||
107 | + "200": { | ||
108 | + "description": "A successful response.", | ||
109 | + "schema": { | ||
110 | + "$ref": "#/definitions/ChartUpdateSortResponse" | ||
111 | + } | ||
112 | + } | ||
113 | + }, | ||
114 | + "parameters": [ | ||
115 | + { | ||
116 | + "name": "body", | ||
117 | + "in": "body", | ||
118 | + "required": true, | ||
119 | + "schema": { | ||
120 | + "$ref": "#/definitions/ChartUpdateSortRequest" | ||
121 | + } | ||
122 | + } | ||
123 | + ], | ||
124 | + "requestBody": {}, | ||
125 | + "tags": [ | ||
126 | + "chart" | ||
127 | + ] | ||
128 | + } | ||
129 | + }, | ||
130 | + "v1/chart/{id}": { | ||
131 | + "get": { | ||
132 | + "summary": "获取图表详情", | ||
133 | + "operationId": "getChart", | ||
134 | + "responses": { | ||
135 | + "200": { | ||
136 | + "description": "A successful response.", | ||
137 | + "schema": { | ||
138 | + "$ref": "#/definitions/ChartGetResponse" | ||
139 | + } | ||
140 | + } | ||
141 | + }, | ||
142 | + "parameters": [ | ||
143 | + { | ||
144 | + "name": "id", | ||
145 | + "in": "path", | ||
146 | + "required": true, | ||
147 | + "type": "string" | ||
148 | + } | ||
149 | + ], | ||
150 | + "requestBody": {}, | ||
151 | + "tags": [ | ||
152 | + "chart" | ||
153 | + ] | ||
154 | + }, | ||
155 | + "delete": { | ||
156 | + "summary": "删除图表", | ||
157 | + "operationId": "deleteChart", | ||
158 | + "responses": { | ||
159 | + "200": { | ||
160 | + "description": "A successful response.", | ||
161 | + "schema": { | ||
162 | + "$ref": "#/definitions/ChartDeleteResponse" | ||
163 | + } | ||
164 | + } | ||
165 | + }, | ||
166 | + "parameters": [ | ||
167 | + { | ||
168 | + "name": "id", | ||
169 | + "in": "path", | ||
170 | + "required": true, | ||
171 | + "type": "string" | ||
172 | + }, | ||
173 | + { | ||
174 | + "name": "body", | ||
175 | + "in": "body", | ||
176 | + "required": true, | ||
177 | + "schema": { | ||
178 | + "$ref": "#/definitions/ChartDeleteRequest" | ||
179 | + } | ||
180 | + } | ||
181 | + ], | ||
182 | + "requestBody": {}, | ||
183 | + "tags": [ | ||
184 | + "chart" | ||
185 | + ] | ||
186 | + }, | ||
187 | + "put": { | ||
188 | + "summary": "更新图表(配置)", | ||
189 | + "operationId": "updateChart", | ||
190 | + "responses": { | ||
191 | + "200": { | ||
192 | + "description": "A successful response.", | ||
193 | + "schema": { | ||
194 | + "$ref": "#/definitions/ChartUpdateResponse" | ||
195 | + } | ||
196 | + } | ||
197 | + }, | ||
198 | + "parameters": [ | ||
199 | + { | ||
200 | + "name": "id", | ||
201 | + "in": "path", | ||
202 | + "required": true, | ||
203 | + "type": "string" | ||
204 | + }, | ||
205 | + { | ||
206 | + "name": "body", | ||
207 | + "in": "body", | ||
208 | + "required": true, | ||
209 | + "schema": { | ||
210 | + "$ref": "#/definitions/ChartUpdateRequest" | ||
211 | + } | ||
212 | + } | ||
213 | + ], | ||
214 | + "requestBody": {}, | ||
215 | + "tags": [ | ||
216 | + "chart" | ||
217 | + ] | ||
218 | + } | ||
219 | + } | ||
220 | + }, | ||
221 | + "definitions": { | ||
222 | + "ChartDeleteRequest": { | ||
223 | + "type": "object", | ||
224 | + "properties": { | ||
225 | + "id": { | ||
226 | + "type": "integer", | ||
227 | + "format": "int64" | ||
228 | + } | ||
229 | + }, | ||
230 | + "title": "ChartDeleteRequest", | ||
231 | + "required": [ | ||
232 | + "id" | ||
233 | + ] | ||
234 | + }, | ||
235 | + "ChartDeleteResponse": { | ||
236 | + "type": "object", | ||
237 | + "title": "ChartDeleteResponse" | ||
238 | + }, | ||
239 | + "ChartGetRequest": { | ||
240 | + "type": "object", | ||
241 | + "properties": { | ||
242 | + "id": { | ||
243 | + "type": "integer", | ||
244 | + "format": "int64" | ||
245 | + } | ||
246 | + }, | ||
247 | + "title": "ChartGetRequest", | ||
248 | + "required": [ | ||
249 | + "id" | ||
250 | + ] | ||
251 | + }, | ||
252 | + "ChartGetResponse": { | ||
253 | + "type": "object", | ||
254 | + "properties": { | ||
255 | + "chart": { | ||
256 | + "$ref": "#/definitions/ChartItem" | ||
257 | + } | ||
258 | + }, | ||
259 | + "title": "ChartGetResponse", | ||
260 | + "required": [ | ||
261 | + "chart" | ||
262 | + ] | ||
263 | + }, | ||
264 | + "ChartItem": { | ||
265 | + "type": "object", | ||
266 | + "properties": { | ||
267 | + "id": { | ||
268 | + "type": "integer", | ||
269 | + "format": "int64", | ||
270 | + "description": " ID" | ||
271 | + }, | ||
272 | + "pid": { | ||
273 | + "type": "integer", | ||
274 | + "format": "int64", | ||
275 | + "description": " 父级ID" | ||
276 | + }, | ||
277 | + "type": { | ||
278 | + "type": "string", | ||
279 | + "description": " 类型 report:报表 group:分组 chart:图表" | ||
280 | + }, | ||
281 | + "sort": { | ||
282 | + "type": "integer", | ||
283 | + "format": "int64", | ||
284 | + "description": " 排序" | ||
285 | + }, | ||
286 | + "name": { | ||
287 | + "type": "string", | ||
288 | + "description": " 名称" | ||
289 | + }, | ||
290 | + "charts": { | ||
291 | + "type": "array", | ||
292 | + "items": { | ||
293 | + "$ref": "#/definitions/ChartItem" | ||
294 | + } | ||
295 | + } | ||
296 | + }, | ||
297 | + "title": "ChartItem" | ||
298 | + }, | ||
299 | + "ChartRenameRequest": { | ||
300 | + "type": "object", | ||
301 | + "properties": { | ||
302 | + "id": { | ||
303 | + "type": "integer", | ||
304 | + "format": "int64" | ||
305 | + }, | ||
306 | + "name": { | ||
307 | + "type": "string", | ||
308 | + "description": " 名称" | ||
309 | + } | ||
310 | + }, | ||
311 | + "title": "ChartRenameRequest", | ||
312 | + "required": [ | ||
313 | + "id", | ||
314 | + "name" | ||
315 | + ] | ||
316 | + }, | ||
317 | + "ChartRenameResponse": { | ||
318 | + "type": "object", | ||
319 | + "title": "ChartRenameResponse" | ||
320 | + }, | ||
321 | + "ChartSaveRequest": { | ||
322 | + "type": "object", | ||
323 | + "properties": { | ||
324 | + "pid": { | ||
325 | + "type": "integer", | ||
326 | + "format": "int64", | ||
327 | + "description": " 父级ID" | ||
328 | + }, | ||
329 | + "type": { | ||
330 | + "type": "string", | ||
331 | + "description": " 类型 report:报表 group:分组 chart:图表" | ||
332 | + }, | ||
333 | + "name": { | ||
334 | + "type": "string", | ||
335 | + "description": " 名称" | ||
336 | + } | ||
337 | + }, | ||
338 | + "title": "ChartSaveRequest", | ||
339 | + "required": [ | ||
340 | + "type" | ||
341 | + ] | ||
342 | + }, | ||
343 | + "ChartSaveResponse": { | ||
344 | + "type": "object", | ||
345 | + "properties": { | ||
346 | + "chart": { | ||
347 | + "$ref": "#/definitions/ChartItem" | ||
348 | + } | ||
349 | + }, | ||
350 | + "title": "ChartSaveResponse", | ||
351 | + "required": [ | ||
352 | + "chart" | ||
353 | + ] | ||
354 | + }, | ||
355 | + "ChartSearchRequest": { | ||
356 | + "type": "object", | ||
357 | + "properties": { | ||
358 | + "page": { | ||
359 | + "type": "integer", | ||
360 | + "format": "int32" | ||
361 | + }, | ||
362 | + "size": { | ||
363 | + "type": "integer", | ||
364 | + "format": "int32" | ||
365 | + } | ||
366 | + }, | ||
367 | + "title": "ChartSearchRequest", | ||
368 | + "required": [ | ||
369 | + "page", | ||
370 | + "size" | ||
371 | + ] | ||
372 | + }, | ||
373 | + "ChartSearchResponse": { | ||
374 | + "type": "object", | ||
375 | + "properties": { | ||
376 | + "list": { | ||
377 | + "type": "array", | ||
378 | + "items": { | ||
379 | + "$ref": "#/definitions/ChartItem" | ||
380 | + } | ||
381 | + }, | ||
382 | + "total": { | ||
383 | + "type": "integer", | ||
384 | + "format": "int64" | ||
385 | + } | ||
386 | + }, | ||
387 | + "title": "ChartSearchResponse", | ||
388 | + "required": [ | ||
389 | + "list", | ||
390 | + "total" | ||
391 | + ] | ||
392 | + }, | ||
393 | + "ChartUpdateRequest": { | ||
394 | + "type": "object", | ||
395 | + "properties": { | ||
396 | + "id": { | ||
397 | + "type": "integer", | ||
398 | + "format": "int64" | ||
399 | + } | ||
400 | + }, | ||
401 | + "title": "ChartUpdateRequest", | ||
402 | + "required": [ | ||
403 | + "id" | ||
404 | + ] | ||
405 | + }, | ||
406 | + "ChartUpdateResponse": { | ||
407 | + "type": "object", | ||
408 | + "title": "ChartUpdateResponse" | ||
409 | + }, | ||
410 | + "ChartUpdateSortRequest": { | ||
411 | + "type": "object", | ||
412 | + "properties": { | ||
413 | + "id": { | ||
414 | + "type": "integer", | ||
415 | + "format": "int64" | ||
416 | + }, | ||
417 | + "sort": { | ||
418 | + "type": "integer", | ||
419 | + "format": "int32" | ||
420 | + }, | ||
421 | + "pid": { | ||
422 | + "type": "integer", | ||
423 | + "format": "int32" | ||
424 | + } | ||
425 | + }, | ||
426 | + "title": "ChartUpdateSortRequest", | ||
427 | + "required": [ | ||
428 | + "id", | ||
429 | + "sort", | ||
430 | + "pid" | ||
431 | + ] | ||
432 | + }, | ||
433 | + "ChartUpdateSortResponse": { | ||
434 | + "type": "object", | ||
435 | + "title": "ChartUpdateSortResponse" | ||
436 | + } | ||
437 | + }, | ||
438 | + "securityDefinitions": { | ||
439 | + "apiKey": { | ||
440 | + "type": "apiKey", | ||
441 | + "description": "Enter JWT Bearer token **_only_**", | ||
442 | + "name": "Authorization", | ||
443 | + "in": "header" | ||
444 | + } | ||
445 | + }, | ||
446 | + "security": [ | ||
447 | + { | ||
448 | + "apiKey": [] | ||
449 | + } | ||
450 | + ] | ||
451 | +} |
doc/dsl/api/core/chart.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: v1 | ||
14 | + group: chart | ||
15 | + jwt: JwtAuth | ||
16 | + //middleware: Authority | ||
17 | +) | ||
18 | +service Core { | ||
19 | + @doc "获取图表详情" | ||
20 | + @handler getChart | ||
21 | + get /chart/:id (ChartGetRequest) returns (ChartGetResponse) | ||
22 | + @doc "保存图表" | ||
23 | + @handler saveChart | ||
24 | + post /chart (ChartSaveRequest) returns (ChartSaveResponse) | ||
25 | + @doc "删除图表" | ||
26 | + @handler deleteChart | ||
27 | + delete /chart/:id (ChartDeleteRequest) returns (ChartDeleteResponse) | ||
28 | + @doc "更新图表(配置)" | ||
29 | + @handler updateChart | ||
30 | + put /chart/:id (ChartUpdateRequest) returns (ChartUpdateResponse) | ||
31 | + @doc "搜索图表" | ||
32 | + @handler searchChart | ||
33 | + post /chart/search (ChartSearchRequest) returns (ChartSearchResponse) | ||
34 | + @doc "移动图表" | ||
35 | + @handler updateChartSort | ||
36 | + post /chart/move (ChartUpdateSortRequest) returns (ChartUpdateSortResponse) | ||
37 | + @doc "重命名图表" | ||
38 | + @handler renameChartSort | ||
39 | + post /chart/rename (ChartRenameRequest) returns (ChartRenameResponse) | ||
40 | +} | ||
41 | + | ||
42 | +type ( | ||
43 | + ChartGetRequest { | ||
44 | + Id int64 `path:"id"` | ||
45 | + } | ||
46 | + ChartGetResponse struct{ | ||
47 | + Chart ChartItem `json:"chart"` | ||
48 | + } | ||
49 | + | ||
50 | + ChartSaveRequest struct{ | ||
51 | + Pid int64 `json:"pid,optional"`// 父级ID | ||
52 | + Type string `json:"type"`// 类型 report:报表 group:分组 chart:图表 | ||
53 | + Name string `json:"name,optional"`// 名称 | ||
54 | + } | ||
55 | + ChartSaveResponse struct{ | ||
56 | + Chart ChartItem `json:"chart"` | ||
57 | + } | ||
58 | + | ||
59 | + ChartDeleteRequest struct{ | ||
60 | + Id int64 `path:"id"` | ||
61 | + } | ||
62 | + ChartDeleteResponse struct{} | ||
63 | + | ||
64 | + ChartUpdateRequest struct{ | ||
65 | + Id int64 `path:"id"` | ||
66 | + } | ||
67 | + ChartUpdateResponse struct{} | ||
68 | + | ||
69 | + ChartSearchRequest struct { | ||
70 | + IncludeTypes []string `json:"includeTypes,optional"` //包含的类型: 类型 report:报表 group:分组 chart:图表(未指定返回所有) | ||
71 | + DataStyle string `json:"dataStyle,optional"` // 数据样式 tree:树形 flat:平铺 | ||
72 | + } | ||
73 | + ChartSearchResponse{ | ||
74 | + List []ChartItem `json:"list"` | ||
75 | + Total int64 `json:"total"` | ||
76 | + } | ||
77 | + ChartUpdateSortRequest struct{ | ||
78 | + Id int64 `json:"id"` | ||
79 | + Pid int64 `json:"pid"` | ||
80 | + Index int `json:"index"` // 元素下标 | ||
81 | + } | ||
82 | + ChartUpdateSortResponse struct{} | ||
83 | + ChartRenameRequest struct{ | ||
84 | + Id int64 `json:"id"` | ||
85 | + Name string `json:"name"`// 名称 | ||
86 | + } | ||
87 | + ChartRenameResponse struct{} | ||
88 | + ChartItem struct{ | ||
89 | + Id int64 `json:"id,optional"`// ID | ||
90 | + Pid int64 `json:"pid,optional"`// 父级ID | ||
91 | + Type string `json:"type,optional"`// 类型 report:报表 group:分组 chart:图表 | ||
92 | + Sort int64 `json:"sort,optional"`// 排序 | ||
93 | + Name string `json:"name,optional"`// 名称 | ||
94 | + Charts []ChartItem `json:"charts,optional"` | ||
95 | + } | ||
96 | +) |
doc/dsl/api/core/chart_setting.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: v1 | ||
14 | + group: chart-setting | ||
15 | + jwt: JwtAuth | ||
16 | +) | ||
17 | +service Core { | ||
18 | + @handler getChartSetting | ||
19 | + post /chart-setting/:id (ChartSettingGetRequest) returns (ChartSettingGetResponse) | ||
20 | + @handler saveChartSetting | ||
21 | + post /chart-setting (ChartSettingSaveRequest) returns (ChartSettingSaveResponse) | ||
22 | + @handler deleteChartSetting | ||
23 | + delete /chart-setting/:id (ChartSettingDeleteRequest) returns (ChartSettingDeleteResponse) | ||
24 | + @handler updateChartSetting | ||
25 | + put /chart-setting/:id (ChartSettingUpdateRequest) returns (ChartSettingUpdateResponse) | ||
26 | + @handler searchChartSetting | ||
27 | + post /chart-setting/search (ChartSettingSearchRequest) returns (ChartSettingSearchResponse) | ||
28 | +} | ||
29 | + | ||
30 | +type ( | ||
31 | + ChartSettingGetRequest { | ||
32 | + Id int64 `path:"id"` | ||
33 | + } | ||
34 | + ChartSettingGetResponse struct{ | ||
35 | + ChartSetting ChartSettingItem `json:"chartSetting"` | ||
36 | + } | ||
37 | + | ||
38 | + ChartSettingSaveRequest struct{ | ||
39 | + ChartSetting ChartSettingItem `json:"chartSetting"` | ||
40 | + } | ||
41 | + ChartSettingSaveResponse struct{} | ||
42 | + | ||
43 | + ChartSettingDeleteRequest struct{ | ||
44 | + Id int64 `path:"id"` | ||
45 | + } | ||
46 | + ChartSettingDeleteResponse struct{} | ||
47 | + | ||
48 | + ChartSettingUpdateRequest struct{ | ||
49 | + Id int64 `path:"id"` | ||
50 | + ChartSetting ChartSettingItem `json:"chartSetting"` | ||
51 | + } | ||
52 | + ChartSettingUpdateResponse struct{} | ||
53 | + | ||
54 | + ChartSettingSearchRequest struct{ | ||
55 | + Page int `json:"page"` | ||
56 | + Size int `json:"size"` | ||
57 | + } | ||
58 | + ChartSettingSearchResponse{ | ||
59 | + List []ChartSettingItem `json:"list"` | ||
60 | + Total int64 `json:"total"` | ||
61 | + } | ||
62 | + ChartSettingItem struct{ | ||
63 | + | ||
64 | + } | ||
65 | +) |
doc/dsl/model/table.sql
0 → 100644
1 | + | ||
2 | +CREATE TABLE `chart` ( | ||
3 | + `id` bigint(0) NOT NULL COMMENT 'ID', | ||
4 | + `pid` bigint(0) NOT NULL COMMENT '父级ID', | ||
5 | + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '类型', | ||
6 | + `sort` int(0) NOT NULL COMMENT '排序', | ||
7 | + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名称', | ||
8 | + PRIMARY KEY (`id`) USING BTREE | ||
9 | +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; | ||
10 | + | ||
11 | +CREATE TABLE `chart_setting` ( | ||
12 | + `id` bigint(0) NOT NULL COMMENT 'ID ', | ||
13 | + `chart_id` bigint(0) NOT NULL COMMENT '图表ID', | ||
14 | + `property` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '属性', | ||
15 | + `style` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '样式', | ||
16 | + `series` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '系列值-数据绑定', | ||
17 | + PRIMARY KEY (`id`) USING BTREE | ||
18 | +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; |
go.mod
0 → 100644
1 | +module gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart | ||
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/pkg/errors v0.9.1 | ||
10 | + github.com/stretchr/testify v1.8.4 | ||
11 | + github.com/tiptok/gocomm v1.0.14 | ||
12 | + github.com/zeromicro/go-zero v1.5.5 | ||
13 | + google.golang.org/grpc v1.57.0 | ||
14 | + gorm.io/driver/mysql v1.5.1 | ||
15 | + gorm.io/driver/postgres v1.5.2 | ||
16 | + gorm.io/gorm v1.25.4 | ||
17 | + gorm.io/plugin/soft_delete v1.2.1 | ||
18 | +) | ||
19 | + | ||
20 | +require ( | ||
21 | + github.com/Shopify/sarama v1.37.2 // indirect | ||
22 | + github.com/beego/beego/v2 v2.0.1 // indirect | ||
23 | + github.com/beorn7/perks v1.0.1 // indirect | ||
24 | + github.com/cenkalti/backoff/v4 v4.2.0 // indirect | ||
25 | + github.com/cespare/xxhash/v2 v2.2.0 // indirect | ||
26 | + github.com/coreos/go-semver v0.3.1 // indirect | ||
27 | + github.com/coreos/go-systemd/v22 v22.5.0 // indirect | ||
28 | + github.com/davecgh/go-spew v1.1.1 // indirect | ||
29 | + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect | ||
30 | + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | ||
31 | + github.com/eapache/go-resiliency v1.3.0 // indirect | ||
32 | + github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect | ||
33 | + github.com/eapache/queue v1.1.0 // indirect | ||
34 | + github.com/emicklei/go-restful/v3 v3.9.0 // indirect | ||
35 | + github.com/fatih/color v1.15.0 // indirect | ||
36 | + github.com/fsnotify/fsnotify v1.4.9 // indirect | ||
37 | + github.com/garyburd/redigo v1.6.3 // indirect | ||
38 | + github.com/gin-contrib/sse v0.1.0 // indirect | ||
39 | + github.com/gin-gonic/gin v1.5.0 // indirect | ||
40 | + github.com/go-logr/logr v1.2.3 // indirect | ||
41 | + github.com/go-logr/stdr v1.2.2 // indirect | ||
42 | + github.com/go-openapi/jsonpointer v0.19.6 // indirect | ||
43 | + github.com/go-openapi/jsonreference v0.20.1 // indirect | ||
44 | + github.com/go-openapi/swag v0.22.3 // indirect | ||
45 | + github.com/go-playground/locales v0.12.1 // indirect | ||
46 | + github.com/go-playground/universal-translator v0.16.0 // indirect | ||
47 | + github.com/go-redis/redis/v8 v8.11.5 // indirect | ||
48 | + github.com/go-sql-driver/mysql v1.7.1 // indirect | ||
49 | + github.com/gogo/protobuf v1.3.2 // indirect | ||
50 | + github.com/golang/mock v1.6.0 // indirect | ||
51 | + github.com/golang/protobuf v1.5.3 // indirect | ||
52 | + github.com/golang/snappy v0.0.4 // indirect | ||
53 | + github.com/google/gnostic v0.5.7-v3refs // indirect | ||
54 | + github.com/google/go-cmp v0.5.9 // indirect | ||
55 | + github.com/google/gofuzz v1.2.0 // indirect | ||
56 | + github.com/google/uuid v1.3.0 // indirect | ||
57 | + github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 // indirect | ||
58 | + github.com/hashicorp/errwrap v1.1.0 // indirect | ||
59 | + github.com/hashicorp/go-multierror v1.1.1 // indirect | ||
60 | + github.com/hashicorp/go-uuid v1.0.3 // indirect | ||
61 | + github.com/hashicorp/hcl v1.0.0 // indirect | ||
62 | + github.com/jackc/pgpassfile v1.0.0 // indirect | ||
63 | + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect | ||
64 | + github.com/jackc/pgx/v5 v5.4.3 // indirect | ||
65 | + github.com/jcmturner/aescts/v2 v2.0.0 // indirect | ||
66 | + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect | ||
67 | + github.com/jcmturner/gofork v1.7.6 // indirect | ||
68 | + github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect | ||
69 | + github.com/jcmturner/rpc/v2 v2.0.3 // indirect | ||
70 | + github.com/jinzhu/inflection v1.0.0 // indirect | ||
71 | + github.com/josharian/intern v1.0.0 // indirect | ||
72 | + github.com/json-iterator/go v1.1.12 // indirect | ||
73 | + github.com/klauspost/compress v1.15.15 // indirect | ||
74 | + github.com/leodido/go-urn v1.1.0 // indirect | ||
75 | + github.com/magiconair/properties v1.8.0 // indirect | ||
76 | + github.com/mailru/easyjson v0.7.7 // indirect | ||
77 | + github.com/mattn/go-colorable v0.1.13 // indirect | ||
78 | + github.com/mattn/go-isatty v0.0.17 // indirect | ||
79 | + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect | ||
80 | + github.com/mitchellh/mapstructure v1.3.3 // indirect | ||
81 | + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||
82 | + github.com/modern-go/reflect2 v1.0.2 // indirect | ||
83 | + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect | ||
84 | + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect | ||
85 | + github.com/openzipkin/zipkin-go v0.4.1 // indirect | ||
86 | + github.com/pelletier/go-toml v1.8.1 // indirect | ||
87 | + github.com/pelletier/go-toml/v2 v2.0.9 // indirect | ||
88 | + github.com/pierrec/lz4/v4 v4.1.17 // indirect | ||
89 | + github.com/pmezard/go-difflib v1.0.0 // indirect | ||
90 | + github.com/prometheus/client_golang v1.16.0 // indirect | ||
91 | + github.com/prometheus/client_model v0.3.0 // indirect | ||
92 | + github.com/prometheus/common v0.42.0 // indirect | ||
93 | + github.com/prometheus/procfs v0.10.1 // indirect | ||
94 | + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect | ||
95 | + github.com/samber/lo v1.38.1 // indirect | ||
96 | + github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect | ||
97 | + github.com/spaolacci/murmur3 v1.1.0 // indirect | ||
98 | + github.com/spf13/afero v1.2.2 // indirect | ||
99 | + github.com/spf13/cast v1.3.0 // indirect | ||
100 | + github.com/spf13/jwalterweatherman v1.0.0 // indirect | ||
101 | + github.com/spf13/pflag v1.0.5 // indirect | ||
102 | + github.com/spf13/viper v1.4.0 // indirect | ||
103 | + github.com/ugorji/go/codec v1.1.7 // indirect | ||
104 | + go.etcd.io/etcd/api/v3 v3.5.9 // indirect | ||
105 | + go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect | ||
106 | + go.etcd.io/etcd/client/v3 v3.5.9 // indirect | ||
107 | + go.opentelemetry.io/otel v1.14.0 // indirect | ||
108 | + go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect | ||
109 | + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect | ||
110 | + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect | ||
111 | + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect | ||
112 | + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0 // indirect | ||
113 | + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 // indirect | ||
114 | + go.opentelemetry.io/otel/exporters/zipkin v1.14.0 // indirect | ||
115 | + go.opentelemetry.io/otel/sdk v1.14.0 // indirect | ||
116 | + go.opentelemetry.io/otel/trace v1.14.0 // indirect | ||
117 | + go.opentelemetry.io/proto/otlp v0.19.0 // indirect | ||
118 | + go.uber.org/atomic v1.10.0 // indirect | ||
119 | + go.uber.org/automaxprocs v1.5.3 // indirect | ||
120 | + go.uber.org/multierr v1.9.0 // indirect | ||
121 | + go.uber.org/zap v1.24.0 // indirect | ||
122 | + golang.org/x/crypto v0.12.0 // indirect | ||
123 | + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect | ||
124 | + golang.org/x/net v0.14.0 // indirect | ||
125 | + golang.org/x/oauth2 v0.7.0 // indirect | ||
126 | + golang.org/x/sys v0.11.0 // indirect | ||
127 | + golang.org/x/term v0.11.0 // indirect | ||
128 | + golang.org/x/text v0.12.0 // indirect | ||
129 | + golang.org/x/time v0.3.0 // indirect | ||
130 | + google.golang.org/appengine v1.6.7 // indirect | ||
131 | + google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect | ||
132 | + google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect | ||
133 | + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect | ||
134 | + google.golang.org/protobuf v1.31.0 // indirect | ||
135 | + gopkg.in/go-playground/validator.v9 v9.29.1 // indirect | ||
136 | + gopkg.in/inf.v0 v0.9.1 // indirect | ||
137 | + gopkg.in/yaml.v2 v2.4.0 // indirect | ||
138 | + gopkg.in/yaml.v3 v3.0.1 // indirect | ||
139 | + k8s.io/api v0.26.3 // indirect | ||
140 | + k8s.io/apimachinery v0.27.0-alpha.3 // indirect | ||
141 | + k8s.io/client-go v0.26.3 // indirect | ||
142 | + k8s.io/klog/v2 v2.90.1 // indirect | ||
143 | + k8s.io/kube-openapi v0.0.0-20230307230338-69ee2d25a840 // indirect | ||
144 | + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect | ||
145 | + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect | ||
146 | + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect | ||
147 | + sigs.k8s.io/yaml v1.3.0 // indirect | ||
148 | +) |
pkg/cache/multi_level_cache.go
0 → 100644
1 | +package cache | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | + "github.com/tiptok/gocomm/pkg/cache" | ||
6 | + "github.com/tiptok/gocomm/pkg/cache/gzcache" | ||
7 | + "github.com/tiptok/gocomm/pkg/log" | ||
8 | +) | ||
9 | + | ||
10 | +func NewMultiLevelCache(hosts []string, password string) *cache.MultiLevelCache { | ||
11 | + fmt.Println("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 | + "github.com/zeromicro/go-zero/zrpc" | ||
6 | + "time" | ||
7 | +) | ||
8 | + | ||
9 | +type JWT struct { | ||
10 | + Secret string `json:",optional"` | ||
11 | + Expires time.Duration `json:",optional"` | ||
12 | +} | ||
13 | +type JwtAuth struct { | ||
14 | + AccessSecret string | ||
15 | + Expire int64 | ||
16 | +} | ||
17 | +type Config struct { | ||
18 | + JwtAuth JwtAuth `json:",optional"` | ||
19 | + UserRpc zrpc.RpcClientConf `json:",optional"` | ||
20 | + AuthRpc zrpc.RpcClientConf `json:",optional"` | ||
21 | + PostRpc zrpc.RpcClientConf `json:",optional"` | ||
22 | + CommentRpc zrpc.RpcClientConf `json:",optional"` | ||
23 | + JWT JWT `json:",optional"` | ||
24 | + DB struct { | ||
25 | + DataSource string | ||
26 | + } `json:",optional"` | ||
27 | + Cache cache.CacheConf `json:",optional"` | ||
28 | + DTM DTM `json:",optional"` | ||
29 | + Sms Sms `json:",optional"` | ||
30 | + Oss Oss `json:",optional"` | ||
31 | + Wechat Wechat `json:",optional"` // 学员端微信 | ||
32 | + CoachClient Wechat `json:",optional"` // 教练端微信 | ||
33 | + OfficialAccount Wechat `json:",optional"` | ||
34 | + ThirdWechatApps []Wechat `json:",optional"` | ||
35 | +} | ||
36 | + | ||
37 | +type DTM struct { | ||
38 | + Server Server `json:",optional"` | ||
39 | +} | ||
40 | + | ||
41 | +type Server struct { | ||
42 | + Name string `json:",optional"` | ||
43 | + Host string `json:",optional"` | ||
44 | + GRPC GRPC `json:",optional"` | ||
45 | + HTTP HTTP `json:",optional"` | ||
46 | + Metrics Metrics `json:",optional"` | ||
47 | +} | ||
48 | + | ||
49 | +type HTTP struct { | ||
50 | + Port string | ||
51 | +} | ||
52 | + | ||
53 | +type GRPC struct { | ||
54 | + Port string | ||
55 | +} | ||
56 | + | ||
57 | +type Metrics struct { | ||
58 | + Port string | ||
59 | +} | ||
60 | + | ||
61 | +type Sms struct { | ||
62 | + Debug bool | ||
63 | + DebugCode string | ||
64 | + Expire int `json:",default=180"` | ||
65 | + MaxSendTime int `json:",default=5"` | ||
66 | + CompanyName string | ||
67 | + SecretId string | ||
68 | + SecretKey string | ||
69 | + SmsAppId string | ||
70 | + Sign string | ||
71 | + TemplateId string | ||
72 | +} | ||
73 | + | ||
74 | +type Oss struct { | ||
75 | + OssEndPoint string | ||
76 | + AccessKeyID string | ||
77 | + AccessKeySecret string | ||
78 | + BuckName string | ||
79 | + | ||
80 | + RegionID string | ||
81 | + RoleArn string | ||
82 | + | ||
83 | + CDN CDN | ||
84 | +} | ||
85 | + | ||
86 | +type Wechat struct { | ||
87 | + AppName string `json:",optional"` | ||
88 | + AppID string | ||
89 | + AppSecret string | ||
90 | + MsgTemplates []Template `json:",optional"` | ||
91 | +} | ||
92 | + | ||
93 | +func (wx Wechat) GetTemplate(code string) (Template, bool) { | ||
94 | + for _, temp := range wx.MsgTemplates { | ||
95 | + if temp.Code == code { | ||
96 | + return temp, true | ||
97 | + } | ||
98 | + } | ||
99 | + return Template{}, false | ||
100 | +} | ||
101 | + | ||
102 | +type CDN struct { | ||
103 | + HostPairs []string | ||
104 | +} | ||
105 | + | ||
106 | +type Template struct { | ||
107 | + ID string // 模板ID | ||
108 | + Name string // 模板名称 | ||
109 | + Code string // 模板编码 | ||
110 | +} |
pkg/contextdata/tenant.go
0 → 100644
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 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/config" | ||
9 | + "time" | ||
10 | +) | ||
11 | + | ||
12 | +var ( | ||
13 | + CtxKeyJwtUserId = "UserId" | ||
14 | +) | ||
15 | + | ||
16 | +func GetUserIdFromCtx(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: GetUserIdFromCtx(ctx, CtxKeyJwtUserId), | ||
56 | + } | ||
57 | +} | ||
58 | + | ||
59 | +type UserToken struct { | ||
60 | + UserId int64 `json:"userId"` | ||
61 | +} | ||
62 | + | ||
63 | +func (tk UserToken) GenerateToken(jwtConfig config.JwtAuth) (string, error) { | ||
64 | + claims := make(jwt.MapClaims) | ||
65 | + claims["exp"] = time.Now().Unix() + jwtConfig.Expire | ||
66 | + claims["iat"] = time.Now().Unix() | ||
67 | + claims["UserId"] = tk.UserId | ||
68 | + token := jwt.New(jwt.SigningMethodHS256) | ||
69 | + token.Claims = claims | ||
70 | + | ||
71 | + return token.SignedString([]byte(jwtConfig.AccessSecret)) | ||
72 | +} | ||
73 | + | ||
74 | +func (tk *UserToken) ParseToken(jwtConfig config.JWT, str string) error { | ||
75 | + return nil | ||
76 | +} | ||
77 | + | ||
78 | +// CheckUserInfo 如果UserToken有效 返回:true 否则返回false | ||
79 | +func (tk *UserToken) CheckUserInfo() bool { | ||
80 | + return !(tk.UserId > 100000000 || tk.UserId <= 0) | ||
81 | +} |
pkg/database/db.go
0 → 100644
1 | +package database | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | + "gorm.io/driver/mysql" | ||
6 | + "gorm.io/driver/postgres" | ||
7 | + "gorm.io/gorm" | ||
8 | + "gorm.io/gorm/logger" | ||
9 | + "log" | ||
10 | + "os" | ||
11 | + "time" | ||
12 | +) | ||
13 | + | ||
14 | +func OpenGormDB(source string) *gorm.DB { | ||
15 | + newLogger := logger.New( | ||
16 | + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer | ||
17 | + logger.Config{ | ||
18 | + SlowThreshold: time.Second, // Slow SQL threshold | ||
19 | + LogLevel: logger.Info, // Log level | ||
20 | + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger | ||
21 | + Colorful: false, // Disable color | ||
22 | + }, | ||
23 | + ) | ||
24 | + fmt.Println("starting db...") | ||
25 | + db, err := gorm.Open(mysql.Open(source), &gorm.Config{ | ||
26 | + Logger: newLogger, | ||
27 | + }) | ||
28 | + if err != nil { | ||
29 | + panic(err) | ||
30 | + } | ||
31 | + return db | ||
32 | +} | ||
33 | + | ||
34 | +func OpenGormPGDB(source string) *gorm.DB { | ||
35 | + newLogger := logger.New( | ||
36 | + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer | ||
37 | + logger.Config{ | ||
38 | + SlowThreshold: time.Second, // Slow SQL threshold | ||
39 | + LogLevel: logger.Info, // Log level | ||
40 | + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger | ||
41 | + Colorful: false, // Disable color | ||
42 | + }, | ||
43 | + ) | ||
44 | + fmt.Println("starting db...") | ||
45 | + db, err := gorm.Open(postgres.Open(source), &gorm.Config{ | ||
46 | + Logger: newLogger, | ||
47 | + }) | ||
48 | + if err != nil { | ||
49 | + panic(err) | ||
50 | + } | ||
51 | + return db | ||
52 | +} |
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/result/httpResult.go
0 → 100644
1 | +package result | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr" | ||
6 | + "net/http" | ||
7 | + | ||
8 | + "github.com/pkg/errors" | ||
9 | + "github.com/zeromicro/go-zero/core/logx" | ||
10 | + "github.com/zeromicro/go-zero/rest/httpx" | ||
11 | + "google.golang.org/grpc/status" | ||
12 | +) | ||
13 | + | ||
14 | +// http返回 | ||
15 | +func HttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) { | ||
16 | + | ||
17 | + if err == nil { | ||
18 | + //成功返回 | ||
19 | + r := Success(resp) | ||
20 | + httpx.WriteJson(w, http.StatusOK, r) | ||
21 | + } else { | ||
22 | + //错误返回 | ||
23 | + errcode := xerr.SERVER_COMMON_ERROR | ||
24 | + errmsg := "服务器开小差啦,稍后再来试一试" | ||
25 | + internalErr := "" | ||
26 | + causeErr := errors.Cause(err) // err类型 | ||
27 | + if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型 | ||
28 | + //自定义CodeError | ||
29 | + errcode = e.GetErrCode() | ||
30 | + errmsg = e.GetErrMsg() | ||
31 | + if e.InternalError != nil { | ||
32 | + internalErr = e.InternalError.Error() | ||
33 | + } | ||
34 | + } else { | ||
35 | + if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误 | ||
36 | + grpcCode := uint32(gstatus.Code()) | ||
37 | + if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端 | ||
38 | + errcode = grpcCode | ||
39 | + errmsg = gstatus.Message() | ||
40 | + } | ||
41 | + } | ||
42 | + } | ||
43 | + | ||
44 | + logx.WithContext(r.Context()).Errorf("【API-ERR】 : %+v ", err) | ||
45 | + response := Error(errcode, errmsg) | ||
46 | + response.Error = internalErr | ||
47 | + httpx.WriteJson(w, http.StatusBadRequest, response) | ||
48 | + } | ||
49 | +} | ||
50 | + | ||
51 | +// 授权的http方法 | ||
52 | +func AuthHttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) { | ||
53 | + | ||
54 | + if err == nil { | ||
55 | + //成功返回 | ||
56 | + r := Success(resp) | ||
57 | + httpx.WriteJson(w, http.StatusOK, r) | ||
58 | + } else { | ||
59 | + //错误返回 | ||
60 | + errcode := xerr.SERVER_COMMON_ERROR | ||
61 | + errmsg := "服务器开小差啦,稍后再来试一试" | ||
62 | + | ||
63 | + causeErr := errors.Cause(err) // err类型 | ||
64 | + if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型 | ||
65 | + //自定义CodeError | ||
66 | + errcode = e.GetErrCode() | ||
67 | + errmsg = e.GetErrMsg() | ||
68 | + } else { | ||
69 | + if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误 | ||
70 | + grpcCode := uint32(gstatus.Code()) | ||
71 | + if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端 | ||
72 | + errcode = grpcCode | ||
73 | + errmsg = gstatus.Message() | ||
74 | + } | ||
75 | + } | ||
76 | + } | ||
77 | + | ||
78 | + logx.WithContext(r.Context()).Errorf("【GATEWAY-ERR】 : %+v ", err) | ||
79 | + | ||
80 | + httpx.WriteJson(w, http.StatusUnauthorized, Error(errcode, errmsg)) | ||
81 | + } | ||
82 | +} | ||
83 | + | ||
84 | +// http 参数错误返回 | ||
85 | +func ParamErrorResult(r *http.Request, w http.ResponseWriter, err error) { | ||
86 | + errMsg := fmt.Sprintf("%s ,%s", xerr.MapErrMsg(xerr.REUQEST_PARAM_ERROR), err.Error()) | ||
87 | + httpx.WriteJson(w, http.StatusBadRequest, Error(xerr.REUQEST_PARAM_ERROR, errMsg)) | ||
88 | +} |
pkg/result/jobResult.go
0 → 100644
1 | +package result | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr" | ||
6 | + | ||
7 | + "github.com/pkg/errors" | ||
8 | + "github.com/zeromicro/go-zero/core/logx" | ||
9 | + "google.golang.org/grpc/status" | ||
10 | +) | ||
11 | + | ||
12 | +// job返回 | ||
13 | +func JobResult(ctx context.Context, resp interface{}, err error) { | ||
14 | + if err == nil { | ||
15 | + // 成功返回 ,只有dev环境下才会打印info,线上不显示 | ||
16 | + if resp != nil { | ||
17 | + logx.Infof("resp: %+v", resp) | ||
18 | + } | ||
19 | + return | ||
20 | + } else { | ||
21 | + errCode := xerr.SERVER_COMMON_ERROR | ||
22 | + errMsg := "服务器开小差啦,稍后再来试一试" | ||
23 | + | ||
24 | + // 错误返回 | ||
25 | + causeErr := errors.Cause(err) // err类型 | ||
26 | + if e, ok := causeErr.(*xerr.CodeError); ok { // 自定义错误类型 | ||
27 | + // 自定义CodeError | ||
28 | + errCode = e.GetErrCode() | ||
29 | + errMsg = e.GetErrMsg() | ||
30 | + } else { | ||
31 | + if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误 | ||
32 | + grpcCode := uint32(gstatus.Code()) | ||
33 | + if xerr.IsCodeErr(grpcCode) { // 区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端 | ||
34 | + errCode = grpcCode | ||
35 | + errMsg = gstatus.Message() | ||
36 | + } | ||
37 | + } | ||
38 | + } | ||
39 | + | ||
40 | + logx.WithContext(ctx).Errorf("【JOB-ERR】 : %+v ,errCode:%d , errMsg:%s ", err, errCode, errMsg) | ||
41 | + return | ||
42 | + } | ||
43 | +} |
pkg/result/responseBean.go
0 → 100644
1 | +package result | ||
2 | + | ||
3 | +type ResponseSuccessBean struct { | ||
4 | + Code uint32 `json:"code"` | ||
5 | + Msg string `json:"msg"` | ||
6 | + Data interface{} `json:"data"` | ||
7 | +} | ||
8 | +type NullJson struct{} | ||
9 | + | ||
10 | +func Success(data interface{}) *ResponseSuccessBean { | ||
11 | + return &ResponseSuccessBean{Code: 0, Msg: "OK", Data: data} | ||
12 | +} | ||
13 | + | ||
14 | +type ResponseErrorBean struct { | ||
15 | + Code uint32 `json:"code"` | ||
16 | + Msg string `json:"msg"` | ||
17 | + Error string `json:"err"` | ||
18 | +} | ||
19 | + | ||
20 | +func Error(errCode uint32, errMsg string) *ResponseErrorBean { | ||
21 | + return &ResponseErrorBean{Code: errCode, Msg: errMsg} | ||
22 | +} |
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 | + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/config" | ||
6 | + "time" | ||
7 | +) | ||
8 | + | ||
9 | +type UserToken struct { | ||
10 | + UserId int64 `json:"userId"` | ||
11 | + CoachId int64 `json:"coach_id"` | ||
12 | + AdminId int64 `json:"adminId"` | ||
13 | + ClientType string `json:"clientType"` | ||
14 | + AccessShops []int64 `json:"accessShops"` | ||
15 | +} | ||
16 | + | ||
17 | +func (tk UserToken) GenerateToken(jwtConfig config.JwtAuth) (string, error) { | ||
18 | + claims := make(jwt.MapClaims) | ||
19 | + claims["exp"] = time.Now().Unix() + jwtConfig.Expire | ||
20 | + claims["iat"] = time.Now().Unix() | ||
21 | + claims["UserId"] = tk.UserId | ||
22 | + claims["CoachId"] = tk.CoachId | ||
23 | + claims["AdminId"] = tk.AdminId | ||
24 | + claims["ClientType"] = tk.ClientType | ||
25 | + claims["AccessShops"] = tk.AccessShops | ||
26 | + token := jwt.New(jwt.SigningMethodHS256) | ||
27 | + token.Claims = claims | ||
28 | + | ||
29 | + return token.SignedString([]byte(jwtConfig.AccessSecret)) | ||
30 | +} | ||
31 | + | ||
32 | +func (tk *UserToken) ParseToken(jwtConfig config.JWT, str string) error { | ||
33 | + //tokenClaims, err := jwt.ParseWithClaims( | ||
34 | + // str, | ||
35 | + // tk, | ||
36 | + // func(token *jwt.Token) (interface{}, error) { | ||
37 | + // return []byte(jwtConfig.Secret), nil | ||
38 | + // }) | ||
39 | + //if err != nil { | ||
40 | + // return err | ||
41 | + //} | ||
42 | + //if claim, ok := tokenClaims.Claims.(*UserToken); ok && tokenClaims.Valid { | ||
43 | + // *tk = *claim | ||
44 | + // return nil | ||
45 | + //} | ||
46 | + //return errors.New("token 解析失败") | ||
47 | + return nil | ||
48 | +} | ||
49 | + | ||
50 | +// CheckUserInfo 如果UserToken有效 返回:true 否则返回false | ||
51 | +func (tk *UserToken) CheckUserInfo() bool { | ||
52 | + return !(tk.UserId > 100000000 || tk.UserId <= 0) | ||
53 | +} |
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/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/errCode.go
0 → 100644
1 | +package xerr | ||
2 | + | ||
3 | +// 成功返回 | ||
4 | +const OK uint32 = 200 | ||
5 | + | ||
6 | +/**(前3位代表业务,后三位代表具体功能)**/ | ||
7 | + | ||
8 | +// 全局错误码 | ||
9 | +const SERVER_COMMON_ERROR uint32 = 100001 | ||
10 | +const REUQEST_PARAM_ERROR uint32 = 100002 | ||
11 | +const TOKEN_EXPIRE_ERROR uint32 = 100003 | ||
12 | +const TOKEN_GENERATE_ERROR uint32 = 100004 | ||
13 | +const DB_ERROR uint32 = 100005 | ||
14 | +const DB_UPDATE_AFFECTED_ZERO_ERROR uint32 = 100006 | ||
15 | + | ||
16 | +const REQUEST_ARGS_ERROR = 200001 | ||
17 | + | ||
18 | +// 微信模块 | ||
19 | +const ErrWxMiniAuthFailError uint32 = 500001 | ||
20 | +const ErrUserNoAuth uint32 = 500002 |
pkg/xerr/errMsg.go
0 → 100644
1 | +package xerr | ||
2 | + | ||
3 | +var message map[uint32]string | ||
4 | + | ||
5 | +func init() { | ||
6 | + message = make(map[uint32]string) | ||
7 | + message[OK] = "SUCCESS" | ||
8 | + message[SERVER_COMMON_ERROR] = "服务器开小差啦,稍后再来试一试" | ||
9 | + message[REUQEST_PARAM_ERROR] = "参数错误" | ||
10 | + message[TOKEN_EXPIRE_ERROR] = "token失效,请重新登陆" | ||
11 | + message[TOKEN_GENERATE_ERROR] = "生成token失败" | ||
12 | + message[DB_ERROR] = "数据库繁忙,请稍后再试" | ||
13 | + message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0" | ||
14 | + message[ErrUserNoAuth] = "无权限" | ||
15 | + message[ErrWxMiniAuthFailError] = "微信授权失败" | ||
16 | +} | ||
17 | + | ||
18 | +func MapErrMsg(errcode uint32) string { | ||
19 | + if msg, ok := message[errcode]; ok { | ||
20 | + return msg | ||
21 | + } else { | ||
22 | + return "服务器开小差啦,稍后再来试一试" | ||
23 | + } | ||
24 | +} | ||
25 | + | ||
26 | +func IsCodeErr(errcode uint32) bool { | ||
27 | + if _, ok := message[errcode]; ok { | ||
28 | + return true | ||
29 | + } else { | ||
30 | + return false | ||
31 | + } | ||
32 | +} |
pkg/xerr/errors.go
0 → 100644
1 | +package xerr | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | +) | ||
6 | + | ||
7 | +/** | ||
8 | +常用通用固定错误 | ||
9 | +*/ | ||
10 | + | ||
11 | +type CodeError struct { | ||
12 | + errCode uint32 | ||
13 | + errMsg string | ||
14 | + InternalError error | ||
15 | +} | ||
16 | + | ||
17 | +// GetErrCode 返回给前端的错误码 | ||
18 | +func (e *CodeError) GetErrCode() uint32 { | ||
19 | + return e.errCode | ||
20 | +} | ||
21 | + | ||
22 | +// GetErrMsg 返回给前端显示端错误信息 | ||
23 | +func (e *CodeError) GetErrMsg() string { | ||
24 | + return e.errMsg | ||
25 | +} | ||
26 | + | ||
27 | +func (e *CodeError) Error() string { | ||
28 | + if e.InternalError != nil { | ||
29 | + return fmt.Sprintf("ErrCode:%d,ErrMsg:%s InternalError:%s", e.errCode, e.errMsg, e.InternalError.Error()) | ||
30 | + } | ||
31 | + return fmt.Sprintf("ErrCode:%d,ErrMsg:%s", e.errCode, e.errMsg) | ||
32 | +} | ||
33 | + | ||
34 | +/* | ||
35 | + 指定错误码的错误 | ||
36 | +*/ | ||
37 | + | ||
38 | +func NewCodeErr(errCode uint32, err error) *CodeError { | ||
39 | + return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode), InternalError: err} | ||
40 | +} | ||
41 | + | ||
42 | +func NewCodeErrMsg(errCode uint32, err error, msg string) *CodeError { | ||
43 | + return &CodeError{errCode: errCode, errMsg: msg, InternalError: err} | ||
44 | +} | ||
45 | + | ||
46 | +/* | ||
47 | + 默认的服务错误 | ||
48 | +*/ | ||
49 | + | ||
50 | +func NewErr(err error) *CodeError { | ||
51 | + return &CodeError{errCode: SERVER_COMMON_ERROR, InternalError: err} | ||
52 | +} | ||
53 | + | ||
54 | +func NewErrMsg(errMsg string) *CodeError { | ||
55 | + return &CodeError{errCode: SERVER_COMMON_ERROR, errMsg: errMsg} | ||
56 | +} | ||
57 | + | ||
58 | +func NewErrMsgErr(errMsg string, err error) *CodeError { | ||
59 | + return &CodeError{errCode: SERVER_COMMON_ERROR, errMsg: errMsg, InternalError: err} | ||
60 | +} |
-
请 注册 或 登录 后发表评论