作者 yangfu

init

正在显示 76 个修改的文件 包含 4664 行增加0 行删除
# Compiled Object codefiles, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
.log
.idea
.vscode
app.log
go.sum
lastupdate.tmp
*.log
public/*
logs/
\ No newline at end of file
... ...
FROM golang:1.19-alpine as builder
# Define the project name | 定义项目名称
ARG PROJECT=core
WORKDIR /build
COPY . .
RUN go env -w GO111MODULE=on \
&& go env -w GOPROXY=https://goproxy.cn,direct \
&& go env -w CGO_ENABLED=0 \
&& go env \
&& go mod tidy \
&& cd cmd/chart-server/api \
&& go build -ldflags="-s -w" -o /build/api/${PROJECT} ${PROJECT}.go
FROM alpine:latest
# Define the project name | 定义项目名称
ARG PROJECT=core
# Define the config file name | 定义配置文件名
ARG CONFIG_FILE=core.yaml
# Define the author | 定义作者
ARG AUTHOR=785409885@qq.com
LABEL org.opencontainers.image.authors=${AUTHOR}
WORKDIR /app
ENV PROJECT=${PROJECT}
ENV CONFIG_FILE=${CONFIG_FILE}
COPY --from=builder /build/api/${PROJECT} ./
COPY --from=builder /build/cmd/chart-server/api/etc/${CONFIG_FILE} ./etc/
EXPOSE 8080
ENTRYPOINT ./${PROJECT} -f etc/${CONFIG_FILE}
\ No newline at end of file
... ...
package main
import (
"flag"
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"net/http"
"strings"
"github.com/golang-jwt/jwt/v4/request"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/config"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/handler"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
var configFile = flag.String("f", "etc/core.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
// 默认的token头 Authorization 修改未 x-token
request.AuthorizationHeaderExtractor = &request.PostExtractionFilter{
request.HeaderExtractor{"x-mmm-accesstoken"}, func(tok string) (string, error) {
// Should be a bearer token
if len(tok) > 6 && strings.ToUpper(tok[0:7]) == "BEARER " {
return tok[7:], nil
}
return tok, nil
},
}
// 初始化Domain里面的配置
domain.ProjectName = c.Name
opts := make([]rest.RunOption, 0)
// cors
opt := rest.WithCustomCors(func(header http.Header) {
header.Set("Access-Control-Allow-Headers", "*")
}, func(writer http.ResponseWriter) {
})
opts = append(opts, opt)
server := rest.MustNewServer(c.RestConf, opts...)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
db.Migrate(ctx.DB)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}
... ...
Name: Core
Host: 0.0.0.0
Port: 8080
Verbose: false
JwtAuth:
AccessSecret: digital-platform
Expire: 360000
Redis:
Host: 127.0.0.1:6379
Type: node
Pass:
DB:
DataSource: host=106.52.103.187 user=postgres password=UYXN134KUm8TeE7 dbname=skateboard-test port=25431 sslmode=disable TimeZone=Asia/Shanghai
... ...
package config
import (
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/config"
)
type Config struct {
rest.RestConf
config.Config
Redis redis.RedisConf `json:",optional"`
}
... ...
package chart
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
)
func DeleteChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ChartDeleteRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := chart.NewDeleteChartLogic(r.Context(), svcCtx)
resp, err := l.DeleteChart(&req)
result.HttpResult(r, w, resp, err)
}
}
... ...
package chart
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
)
func GetChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ChartGetRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := chart.NewGetChartLogic(r.Context(), svcCtx)
resp, err := l.GetChart(&req)
result.HttpResult(r, w, resp, err)
}
}
... ...
package chart
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
)
func RenameChartSortHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ChartRenameRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := chart.NewRenameChartSortLogic(r.Context(), svcCtx)
resp, err := l.RenameChartSort(&req)
result.HttpResult(r, w, resp, err)
}
}
... ...
package chart
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
)
func SaveChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ChartSaveRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := chart.NewSaveChartLogic(r.Context(), svcCtx)
resp, err := l.SaveChart(&req)
result.HttpResult(r, w, resp, err)
}
}
... ...
package chart
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
)
func SearchChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ChartSearchRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := chart.NewSearchChartLogic(r.Context(), svcCtx)
resp, err := l.SearchChart(&req)
result.HttpResult(r, w, resp, err)
}
}
... ...
package chart
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
)
func UpdateChartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ChartUpdateRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := chart.NewUpdateChartLogic(r.Context(), svcCtx)
resp, err := l.UpdateChart(&req)
result.HttpResult(r, w, resp, err)
}
}
... ...
package chart
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/result"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/logic/chart"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
)
func UpdateChartSortHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ChartUpdateSortRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := chart.NewUpdateChartSortLogic(r.Context(), svcCtx)
resp, err := l.UpdateChartSort(&req)
result.HttpResult(r, w, resp, err)
}
}
... ...
// Code generated by goctl. DO NOT EDIT.
package handler
import (
"net/http"
chart "gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/handler/chart"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodGet,
Path: "/chart/:id",
Handler: chart.GetChartHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/chart",
Handler: chart.SaveChartHandler(serverCtx),
},
{
Method: http.MethodDelete,
Path: "/chart/:id",
Handler: chart.DeleteChartHandler(serverCtx),
},
{
Method: http.MethodPut,
Path: "/chart/:id",
Handler: chart.UpdateChartHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/chart/search",
Handler: chart.SearchChartHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/chart/move",
Handler: chart.UpdateChartSortHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/chart/rename",
Handler: chart.RenameChartSortHandler(serverCtx),
},
},
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
rest.WithPrefix("/v1"),
)
}
... ...
package chart
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type DeleteChartLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDeleteChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteChartLogic {
return &DeleteChartLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteChartLogic) DeleteChart(req *types.ChartDeleteRequest) (resp *types.ChartDeleteResponse, err error) {
var (
conn = l.svcCtx.DefaultDBConn()
chart *domain.Chart
tenantId = contextdata.GetTenantFromCtx(l.ctx)
)
resp = &types.ChartDeleteResponse{}
if chart, err = l.svcCtx.ChartRepository.FindOne(l.ctx, conn, req.Id); err != nil {
return nil, xerr.NewErrMsgErr("不存在", err)
}
if chart.TenantId != tenantId {
return nil, xerr.NewErrMsgErr("无权限", nil)
}
// TODO:图表被引用不能删除
// TODO:删除下级图表
if chart, err = l.svcCtx.ChartRepository.Delete(l.ctx, conn, chart); err != nil {
return nil, xerr.NewErrMsgErr("删除失败", err)
}
resp = &types.ChartDeleteResponse{}
return
}
... ...
package chart
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetChartLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetChartLogic {
return &GetChartLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetChartLogic) GetChart(req *types.ChartGetRequest) (resp *types.ChartGetResponse, err error) {
// todo: add your logic here and delete this line
return
}
... ...
package chart
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type RenameChartSortLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewRenameChartSortLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RenameChartSortLogic {
return &RenameChartSortLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *RenameChartSortLogic) RenameChartSort(req *types.ChartRenameRequest) (resp *types.ChartRenameResponse, err error) {
var (
conn = l.svcCtx.DefaultDBConn()
chart *domain.Chart
tenantId = contextdata.GetTenantFromCtx(l.ctx)
charts []*domain.Chart
)
resp = &types.ChartRenameResponse{}
if chart, err = l.svcCtx.ChartRepository.FindOne(l.ctx, conn, req.Id); err != nil {
return nil, xerr.NewErrMsgErr("不存在", err)
}
if chart.Name == req.Name {
return
}
if chart.TenantId != tenantId {
return nil, xerr.NewErrMsgErr("无权限", nil)
}
_, charts, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexChartPid(tenantId, chart.Pid)())
if err != nil {
return nil, xerr.NewErr(err)
}
for i := range charts {
if charts[i].Name == req.Name {
return nil, xerr.NewErrMsgErr("名称已存在", nil)
}
}
chart.Name = req.Name
chart, err = l.svcCtx.ChartRepository.UpdateWithVersion(l.ctx, conn, chart)
if err != nil {
return nil, xerr.NewErr(err)
}
return
}
... ...
package chart
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/tool"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type SaveChartLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSaveChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SaveChartLogic {
return &SaveChartLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SaveChartLogic) SaveChart(req *types.ChartSaveRequest) (resp *types.ChartSaveResponse, err error) {
var (
conn = l.svcCtx.DefaultDBConn()
parentChart *domain.Chart
tenantId = contextdata.GetTenantFromCtx(l.ctx)
)
if !domain.ChartTypeContain(req.Type) {
return nil, xerr.NewErrMsgErr("未知类型:"+req.Type, err)
}
chart := &domain.Chart{
Name: req.Name,
Type: req.Type,
Pid: req.Pid,
Sort: 1,
Group: tool.Krand(10, tool.KC_RAND_KIND_UPPER),
TenantId: tenantId,
}
if chart.Name == "" {
chart.Name = chart.RandName()
}
if req.Pid > 0 {
if parentChart, err = l.svcCtx.ChartRepository.FindOne(l.ctx, conn, req.Pid); err != nil {
return nil, xerr.NewErrMsgErr("父级不存在", err)
}
chart.Group = parentChart.Group
}
// current sort
if currentSortChart, _ := l.svcCtx.ChartRepository.FindOneByGroup(l.ctx, conn, tenantId, req.Pid); currentSortChart != nil {
chart.Sort = currentSortChart.Sort + 1
}
if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
if chart, err = l.svcCtx.ChartRepository.Insert(ctx, conn, chart); err != nil {
return err
}
return nil
}, true); err != nil {
return nil, xerr.NewErrMsgErr("创建失败", err)
}
resp = &types.ChartSaveResponse{
Chart: types.NewChartItem(chart),
}
return
}
... ...
package chart
import (
"context"
"github.com/samber/lo"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xcollection"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type SearchChartLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSearchChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SearchChartLogic {
return &SearchChartLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SearchChartLogic) SearchChart(req *types.ChartSearchRequest) (resp interface{}, err error) {
var (
conn = l.svcCtx.DefaultDBConn()
tenantId = contextdata.GetTenantFromCtx(l.ctx)
charts []*domain.Chart
)
_, charts, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexTenantId(tenantId)())
var chartItems = make([]xcollection.TreeNode, 0)
lo.EveryBy(charts, func(chart *domain.Chart) bool {
// 按类型过滤
if len(req.IncludeTypes) > 0 {
if !lo.Contains(req.IncludeTypes, chart.Type) {
return true
}
}
chartItems = append(chartItems, types.NewChartItem(chart))
return true
})
if req.DataStyle == "tree" {
resp = xcollection.NewTree(chartItems).Nodes
} else if req.DataStyle == "flat" {
resp = chartItems
} else {
resp = chartItems
}
return map[string]interface{}{
"list": resp,
}, nil
}
... ...
package chart
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateChartLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUpdateChartLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateChartLogic {
return &UpdateChartLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateChartLogic) UpdateChart(req *types.ChartUpdateRequest) (resp *types.ChartUpdateResponse, err error) {
// todo: add your logic here and delete this line
return
}
... ...
package chart
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/types"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/contextdata"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr"
)
type UpdateChartSortLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUpdateChartSortLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateChartSortLogic {
return &UpdateChartSortLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateChartSortLogic) UpdateChartSort(req *types.ChartUpdateSortRequest) (resp *types.ChartUpdateSortResponse, err error) {
var (
conn = l.svcCtx.DefaultDBConn()
chart *domain.Chart
chartGroupBefore []*domain.Chart
chartGroupAfter []*domain.Chart
tenantId = contextdata.GetTenantFromCtx(l.ctx)
)
if chart, err = l.svcCtx.ChartRepository.FindOne(l.ctx, conn, req.Id); err != nil {
return nil, xerr.NewErrMsgErr("不存在", err)
}
var (
oldPid = chart.Pid
inSameGroup = req.Pid == chart.Pid // 同一分组
)
chart.Sort = req.Index + 1
chart.Pid = req.Pid
resp = &types.ChartUpdateSortResponse{}
// TODO:图表被引用不能移动
// 移动到同一个分组某个位置
if inSameGroup {
if _, chartGroupBefore, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexChartPid(tenantId, req.Pid)()); err != nil {
return nil, xerr.NewErr(err)
}
if err = l.updateSort(domain.Reorder(chartGroupBefore, chart, true)); err != nil {
return nil, xerr.NewErrMsgErr("更新失败", err)
}
return
}
// 移动到不同分组的某个位置
if _, chartGroupBefore, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexChartPid(tenantId, oldPid)()); err != nil {
return nil, xerr.NewErr(err)
}
if _, chartGroupAfter, err = l.svcCtx.ChartRepository.Find(l.ctx, conn, domain.IndexChartPid(tenantId, req.Pid)()); err != nil {
return nil, xerr.NewErr(err)
}
list := domain.Reorder(chartGroupBefore, chart, false)
list = append(list, domain.Reorder(chartGroupAfter, chart, true)...)
if err = l.updateSort(list); err != nil {
return nil, xerr.NewErrMsgErr("更新失败", err)
}
return
}
func (l *UpdateChartSortLogic) updateSort(charts []*domain.Chart) error {
if err := transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
for i := range charts {
if charts[i].Id == 0 {
continue
}
if _, err := l.svcCtx.ChartRepository.Update(l.ctx, conn, charts[i]); err != nil {
return err
}
}
return nil
}, true); err != nil {
return err
}
return nil
}
... ...
package svc
import (
"github.com/tiptok/gocomm/pkg/cache/gzcache"
"github.com/zeromicro/go-zero/core/stores/redis"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/api/internal/config"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/repository"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/cache"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/database"
"gorm.io/gorm"
)
type ServiceContext struct {
Config config.Config
DB *gorm.DB
Redis *redis.Redis
RedisCache gzcache.GZCache
ChartRepository domain.ChartRepository
}
func NewServiceContext(c config.Config) *ServiceContext {
db := database.OpenGormPGDB(c.DB.DataSource)
mlCache := cache.NewMultiLevelCache([]string{c.Redis.Host}, c.Redis.Pass)
redisCache := gzcache.NewClusterCache([]string{c.Redis.Host}, c.Redis.Pass)
redis, _ := redis.NewRedis(redis.RedisConf{Host: c.Redis.Host, Pass: c.Redis.Pass, Type: "node"})
return &ServiceContext{
Config: c,
DB: db,
RedisCache: redisCache,
Redis: redis,
ChartRepository: repository.NewChartRepository(cache.NewCachedRepository(mlCache)),
}
}
func (svc *ServiceContext) DefaultDBConn() transaction.Conn {
return transaction.NewTransactionContext(svc.DB)
}
... ...
package types
import "strconv"
func (item ChartItem) PID() string {
return strconv.Itoa(int(item.Pid))
}
func (item ChartItem) ID() string {
return strconv.Itoa(int(item.Id))
}
... ...
package types
import (
"github.com/jinzhu/copier"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
)
func NewChartItem(chart *domain.Chart) ChartItem {
item := ChartItem{}
copier.Copy(&item, chart)
//item.Charts = make([]ChartItem, 0)
return item
}
... ...
// Code generated by goctl. DO NOT EDIT.
package types
type ChartGetRequest struct {
Id int64 `path:"id"`
}
type ChartGetResponse struct {
Chart ChartItem `json:"chart"`
}
type ChartSaveRequest struct {
Pid int64 `json:"pid,optional"` // 父级ID
Type string `json:"type"` // 类型 report:报表 group:分组 chart:图表
Name string `json:"name,optional"` // 名称
}
type ChartSaveResponse struct {
Chart ChartItem `json:"chart"`
}
type ChartDeleteRequest struct {
Id int64 `path:"id"`
}
type ChartDeleteResponse struct {
}
type ChartUpdateRequest struct {
Id int64 `path:"id"`
}
type ChartUpdateResponse struct {
}
type ChartSearchRequest struct {
IncludeTypes []string `json:"includeTypes,optional"` //包含的类型: 类型 report:报表 group:分组 chart:图表(未指定返回所有)
DataStyle string `json:"dataStyle,optional"` // 数据样式 tree:树形 flat:平铺
}
type ChartSearchResponse struct {
List []ChartItem `json:"list"`
Total int64 `json:"total"`
}
type ChartUpdateSortRequest struct {
Id int64 `json:"id"`
Pid int64 `json:"pid"`
Index int `json:"index"` // 元素下标,从0开始
}
type ChartUpdateSortResponse struct {
}
type ChartRenameRequest struct {
Id int64 `json:"id"`
Name string `json:"name"` // 名称
}
type ChartRenameResponse struct {
}
type ChartItem struct {
Id int64 `json:"id,optional"` // ID
Pid int64 `json:"pid,optional"` // 父级ID
Type string `json:"type,optional"` // 类型 report:报表 group:分组 chart:图表
Sort int64 `json:"sort,optional"` // 排序
Name string `json:"name,optional"` // 名称
//Charts []ChartItem `json:"charts,optional"`
}
... ...
syntax = "v1"
info(
title: "xx实例"
desc: "xx实例"
author: "author"
email: "email"
version: "v1"
)
@server(
prefix: v1
group: chart
jwt: JwtAuth
)
service Core {
@handler getChart
post /chart/:id (ChartGetRequest) returns (ChartGetResponse)
@handler saveChart
post /chart (ChartSaveRequest) returns (ChartSaveResponse)
@handler deleteChart
delete /chart/:id (ChartDeleteRequest) returns (ChartDeleteResponse)
@handler updateChart
put /chart/:id (ChartUpdateRequest) returns (ChartUpdateResponse)
@handler searchChart
post /chart/search (ChartSearchRequest) returns (ChartSearchResponse)
}
type (
ChartGetRequest {
Id int64 `path:"id"`
}
ChartGetResponse struct{
Chart ChartItem `json:"chart"`
}
ChartSaveRequest struct{
Chart ChartItem `json:"chart"`
}
ChartSaveResponse struct{}
ChartDeleteRequest struct{
Id int64 `path:"id"`
}
ChartDeleteResponse struct{}
ChartUpdateRequest struct{
Id int64 `path:"id"`
Chart ChartItem `json:"chart"`
}
ChartUpdateResponse struct{}
ChartSearchRequest struct{
Page int `json:"page"`
Size int `json:"size"`
}
ChartSearchResponse{
List []ChartItem `json:"list"`
Total int64 `json:"total"`
}
ChartItem struct{
}
)
... ...
syntax = "v1"
info(
title: "xx实例"
desc: "xx实例"
author: "author"
email: "email"
version: "v1"
)
@server(
prefix: v1
group: chart-setting
jwt: JwtAuth
)
service Core {
@handler getChartSetting
post /chart-setting/:id (ChartSettingGetRequest) returns (ChartSettingGetResponse)
@handler saveChartSetting
post /chart-setting (ChartSettingSaveRequest) returns (ChartSettingSaveResponse)
@handler deleteChartSetting
delete /chart-setting/:id (ChartSettingDeleteRequest) returns (ChartSettingDeleteResponse)
@handler updateChartSetting
put /chart-setting/:id (ChartSettingUpdateRequest) returns (ChartSettingUpdateResponse)
@handler searchChartSetting
post /chart-setting/search (ChartSettingSearchRequest) returns (ChartSettingSearchResponse)
}
type (
ChartSettingGetRequest {
Id int64 `path:"id"`
}
ChartSettingGetResponse struct{
ChartSetting ChartSettingItem `json:"chartSetting"`
}
ChartSettingSaveRequest struct{
ChartSetting ChartSettingItem `json:"chartSetting"`
}
ChartSettingSaveResponse struct{}
ChartSettingDeleteRequest struct{
Id int64 `path:"id"`
}
ChartSettingDeleteResponse struct{}
ChartSettingUpdateRequest struct{
Id int64 `path:"id"`
ChartSetting ChartSettingItem `json:"chartSetting"`
}
ChartSettingUpdateResponse struct{}
ChartSettingSearchRequest struct{
Page int `json:"page"`
Size int `json:"size"`
}
ChartSettingSearchResponse{
List []ChartSettingItem `json:"list"`
Total int64 `json:"total"`
}
ChartSettingItem struct{
}
)
... ...
syntax = "proto3";
option go_package ="./pb";
package pb;
message ChartGetReq {
int64 Id = 1;
}
message ChartGetResp{
ChartItem User = 1;
}
message ChartSaveReq {
}
message ChartSaveResp{
}
message ChartDeleteReq {
int64 Id = 1;
}
message ChartDeleteResp{
}
message ChartUpdateReq {
int64 Id = 1;
}
message ChartUpdateResp{
}
message ChartSearchReq {
int64 PageNumber = 1;
int64 PageSize = 2;
}
message ChartSearchResp{
repeated ChartItem List =1;
int64 Total =2;
}
message ChartItem {
}
service ChartService {
rpc ChartGet(ChartGetReq) returns(ChartGetResp);
rpc ChartSave(ChartSaveReq) returns(ChartSaveResp);
rpc ChartDelete(ChartDeleteReq) returns(ChartDeleteResp);
rpc ChartUpdate(ChartUpdateReq) returns(ChartUpdateResp);
rpc ChartSearch(ChartSearchReq) returns(ChartSearchResp);
}
... ...
syntax = "proto3";
option go_package ="./pb";
package pb;
message ChartSettingGetReq {
int64 Id = 1;
}
message ChartSettingGetResp{
ChartSettingItem User = 1;
}
message ChartSettingSaveReq {
}
message ChartSettingSaveResp{
}
message ChartSettingDeleteReq {
int64 Id = 1;
}
message ChartSettingDeleteResp{
}
message ChartSettingUpdateReq {
int64 Id = 1;
}
message ChartSettingUpdateResp{
}
message ChartSettingSearchReq {
int64 PageNumber = 1;
int64 PageSize = 2;
}
message ChartSettingSearchResp{
repeated ChartSettingItem List =1;
int64 Total =2;
}
message ChartSettingItem {
}
service ChartSettingService {
rpc ChartSettingGet(ChartSettingGetReq) returns(ChartSettingGetResp);
rpc ChartSettingSave(ChartSettingSaveReq) returns(ChartSettingSaveResp);
rpc ChartSettingDelete(ChartSettingDeleteReq) returns(ChartSettingDeleteResp);
rpc ChartSettingUpdate(ChartSettingUpdateReq) returns(ChartSettingUpdateResp);
rpc ChartSettingSearch(ChartSettingSearchReq) returns(ChartSettingSearchResp);
}
... ...
package db
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/models"
"gorm.io/gorm"
)
func Migrate(db *gorm.DB) {
db.AutoMigrate(&models.Chart{})
}
... ...
package models
import (
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gorm.io/gorm"
"gorm.io/plugin/soft_delete"
)
type Chart struct {
Id int64 // ID
Pid int64 `gorm:"index:idx_chart_t_pid_sort"` // 父级ID
Type string // 类型
Sort int `gorm:"index:idx_chart_t_pid_sort"` // 排序
Name string // 名称
Group string `gorm:"index:idx_chart_group"` // 分组
TenantId int64 `gorm:"index:idx_chart_t_pid_sort"` // 租户ID
CreatedAt int64 `json:",omitempty"`
UpdatedAt int64 `json:",omitempty"`
DeletedAt int64 `json:",omitempty"`
Version int `json:",omitempty"`
IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"`
}
func (m *Chart) TableName() string {
return "chart"
}
func (m *Chart) BeforeCreate(tx *gorm.DB) (err error) {
// m.CreatedAt = time.Now().Unix()
// m.UpdatedAt = time.Now().Unix()
return
}
func (m *Chart) BeforeUpdate(tx *gorm.DB) (err error) {
// m.UpdatedAt = time.Now().Unix()
return
}
func (m *Chart) CacheKeyFunc() string {
if m.Id == 0 {
return ""
}
return fmt.Sprintf("%v:cache:%v:id:%v", domain.ProjectName, m.TableName(), m.Id)
}
func (m *Chart) CacheKeyFuncByObject(obj interface{}) string {
if v, ok := obj.(*Chart); ok {
return v.CacheKeyFunc()
}
return ""
}
func (m *Chart) CachePrimaryKeyFunc() string {
if len("") == 0 {
return ""
}
return fmt.Sprintf("%v:cache:%v:primarykey:%v", domain.ProjectName, m.TableName(), "key")
}
... ...
package models
import (
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gorm.io/gorm"
)
type ChartSetting struct {
Id int64 // ID
ChartId int64 // 图表ID
Property string // 属性
Style string // 样式
Series string // 系列值-数据绑定
CreatedAt int64 `json:",omitempty"`
UpdatedAt int64 `json:",omitempty"`
DeletedAt int64 `json:",omitempty"`
Version int `json:",omitempty"`
}
func (m *ChartSetting) TableName() string {
return "chart_setting"
}
func (m *ChartSetting) BeforeCreate(tx *gorm.DB) (err error) {
// m.CreatedAt = time.Now().Unix()
// m.UpdatedAt = time.Now().Unix()
return
}
func (m *ChartSetting) BeforeUpdate(tx *gorm.DB) (err error) {
// m.UpdatedAt = time.Now().Unix()
return
}
func (m *ChartSetting) CacheKeyFunc() string {
if m.Id == 0 {
return ""
}
return fmt.Sprintf("%v:cache:%v:id:%v", domain.ProjectName, m.TableName(), m.Id)
}
func (m *ChartSetting) CacheKeyFuncByObject(obj interface{}) string {
if v, ok := obj.(*ChartSetting); ok {
return v.CacheKeyFunc()
}
return ""
}
func (m *ChartSetting) CachePrimaryKeyFunc() string {
if len("") == 0 {
return ""
}
return fmt.Sprintf("%v:cache:%v:primarykey:%v", domain.ProjectName, m.TableName(), "key")
}
... ...
package repository
import (
"context"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/tiptok/gocomm/pkg/cache"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/models"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gorm.io/gorm"
)
type ChartRepository struct {
*cache.CachedRepository
}
func (repository *ChartRepository) Insert(ctx context.Context, conn transaction.Conn, dm *domain.Chart) (*domain.Chart, error) {
var (
err error
m = &models.Chart{}
tx = conn.DB()
)
if m, err = repository.DomainModelToModel(dm); err != nil {
return nil, err
}
if tx = tx.Model(m).Save(m); tx.Error != nil {
return nil, tx.Error
}
dm.Id = m.Id
return repository.ModelToDomainModel(m)
}
func (repository *ChartRepository) Update(ctx context.Context, conn transaction.Conn, dm *domain.Chart) (*domain.Chart, error) {
var (
err error
m *models.Chart
tx = conn.DB()
)
if m, err = repository.DomainModelToModel(dm); err != nil {
return nil, err
}
queryFunc := func() (interface{}, error) {
tx = tx.Model(m).Updates(m)
return nil, tx.Error
}
if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
return nil, err
}
return repository.ModelToDomainModel(m)
}
func (repository *ChartRepository) UpdateWithVersion(ctx context.Context, transaction transaction.Conn, dm *domain.Chart) (*domain.Chart, error) {
var (
err error
m *models.Chart
tx = transaction.DB()
)
if m, err = repository.DomainModelToModel(dm); err != nil {
return nil, err
}
oldVersion := dm.Version
m.Version += 1
queryFunc := func() (interface{}, error) {
tx = tx.Model(m).Select("*").Where("id = ?", m.Id).Where("version = ?", oldVersion).Updates(m)
if tx.RowsAffected == 0 {
return nil, domain.ErrUpdateFail
}
return nil, tx.Error
}
if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
return nil, err
}
return repository.ModelToDomainModel(m)
}
func (repository *ChartRepository) Delete(ctx context.Context, conn transaction.Conn, dm *domain.Chart) (*domain.Chart, error) {
var (
tx = conn.DB()
m = &models.Chart{Id: dm.Identify().(int64)}
)
queryFunc := func() (interface{}, error) {
tx = tx.Where("id = ?", m.Id).Delete(m)
return m, tx.Error
}
if _, err := repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
return dm, err
}
return repository.ModelToDomainModel(m)
}
func (repository *ChartRepository) FindOne(ctx context.Context, conn transaction.Conn, id int64) (*domain.Chart, error) {
var (
err error
tx = conn.DB()
m = new(models.Chart)
)
queryFunc := func() (interface{}, error) {
tx = tx.Model(m).Where("id = ?", id).First(m)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, domain.ErrNotFound
}
return m, tx.Error
}
cacheModel := new(models.Chart)
cacheModel.Id = id
if err = repository.QueryCache(cacheModel.CacheKeyFunc, m, queryFunc); err != nil {
return nil, err
}
return repository.ModelToDomainModel(m)
}
func (repository *ChartRepository) Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*domain.Chart, error) {
var (
tx = conn.DB()
ms []*models.Chart
dms = make([]*domain.Chart, 0)
total int64
)
queryFunc := func() (interface{}, error) {
tx = tx.Model(&ms).Order("pid asc").Order("sort asc")
if v, ok := queryOptions["tenantId"]; ok {
tx.Where("tenant_id = ?", v)
}
if v, ok := queryOptions["pid"]; ok {
tx.Where("pid = ?", v)
}
if total, tx = transaction.PaginationAndCount(ctx, tx, queryOptions, &ms); tx.Error != nil {
return dms, tx.Error
}
return dms, nil
}
if _, err := repository.Query(queryFunc); err != nil {
return 0, nil, err
}
for _, item := range ms {
if dm, err := repository.ModelToDomainModel(item); err != nil {
return 0, dms, err
} else {
dms = append(dms, dm)
}
}
return total, dms, nil
}
func (repository *ChartRepository) FindOneByGroup(ctx context.Context, conn transaction.Conn, tenantId, pid int64) (*domain.Chart, error) {
var (
err error
tx = conn.DB()
m = new(models.Chart)
)
queryFunc := func() (interface{}, error) {
tx = tx.Model(m).Where("tenant_id = ?", tenantId).Where("pid = ?", pid).Order("sort desc").Limit(1).First(m)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, domain.ErrNotFound
}
return m, tx.Error
}
if _, err = repository.Query(queryFunc); err != nil {
return nil, err
}
return repository.ModelToDomainModel(m)
}
func (repository *ChartRepository) ModelToDomainModel(from *models.Chart) (*domain.Chart, error) {
to := &domain.Chart{}
err := copier.Copy(to, from)
return to, err
}
func (repository *ChartRepository) DomainModelToModel(from *domain.Chart) (*models.Chart, error) {
to := &models.Chart{}
err := copier.Copy(to, from)
return to, err
}
func NewChartRepository(cache *cache.CachedRepository) domain.ChartRepository {
return &ChartRepository{CachedRepository: cache}
}
... ...
package repository
import (
"context"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/tiptok/gocomm/pkg/cache"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/models"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/domain"
"gorm.io/gorm"
)
type ChartSettingRepository struct {
*cache.CachedRepository
}
func (repository *ChartSettingRepository) Insert(ctx context.Context, conn transaction.Conn, dm *domain.ChartSetting) (*domain.ChartSetting, error) {
var (
err error
m = &models.ChartSetting{}
tx = conn.DB()
)
if m, err = repository.DomainModelToModel(dm); err != nil {
return nil, err
}
if tx = tx.Model(m).Save(m); tx.Error != nil {
return nil, tx.Error
}
dm.Id = m.Id
return repository.ModelToDomainModel(m)
}
func (repository *ChartSettingRepository) Update(ctx context.Context, conn transaction.Conn, dm *domain.ChartSetting) (*domain.ChartSetting, error) {
var (
err error
m *models.ChartSetting
tx = conn.DB()
)
if m, err = repository.DomainModelToModel(dm); err != nil {
return nil, err
}
queryFunc := func() (interface{}, error) {
tx = tx.Model(m).Updates(m)
return nil, tx.Error
}
if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
return nil, err
}
return repository.ModelToDomainModel(m)
}
func (repository *ChartSettingRepository) UpdateWithVersion(ctx context.Context, transaction transaction.Conn, dm *domain.ChartSetting) (*domain.ChartSetting, error) {
var (
err error
m *models.ChartSetting
tx = transaction.DB()
)
if m, err = repository.DomainModelToModel(dm); err != nil {
return nil, err
}
oldVersion := dm.Version
m.Version += 1
queryFunc := func() (interface{}, error) {
tx = tx.Model(m).Select("*").Where("id = ?", m.Id).Where("version = ?", oldVersion).Updates(m)
if tx.RowsAffected == 0 {
return nil, domain.ErrUpdateFail
}
return nil, tx.Error
}
if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
return nil, err
}
return repository.ModelToDomainModel(m)
}
func (repository *ChartSettingRepository) Delete(ctx context.Context, conn transaction.Conn, dm *domain.ChartSetting) (*domain.ChartSetting, error) {
var (
tx = conn.DB()
m = &models.ChartSetting{Id: dm.Identify().(int64)}
)
queryFunc := func() (interface{}, error) {
tx = tx.Where("id = ?", m.Id).Delete(m)
return m, tx.Error
}
if _, err := repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
return dm, err
}
return repository.ModelToDomainModel(m)
}
func (repository *ChartSettingRepository) FindOne(ctx context.Context, conn transaction.Conn, id int64) (*domain.ChartSetting, error) {
var (
err error
tx = conn.DB()
m = new(models.ChartSetting)
)
queryFunc := func() (interface{}, error) {
tx = tx.Model(m).Where("id = ?", id).First(m)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, domain.ErrNotFound
}
return m, tx.Error
}
cacheModel := new(models.ChartSetting)
cacheModel.Id = id
if err = repository.QueryCache(cacheModel.CacheKeyFunc, m, queryFunc); err != nil {
return nil, err
}
return repository.ModelToDomainModel(m)
}
func (repository *ChartSettingRepository) Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*domain.ChartSetting, error) {
var (
tx = conn.DB()
ms []*models.ChartSetting
dms = make([]*domain.ChartSetting, 0)
total int64
)
queryFunc := func() (interface{}, error) {
tx = tx.Model(&ms).Order("id desc")
if total, tx = transaction.PaginationAndCount(ctx, tx, queryOptions, &ms); tx.Error != nil {
return dms, tx.Error
}
return dms, nil
}
if _, err := repository.Query(queryFunc); err != nil {
return 0, nil, err
}
for _, item := range ms {
if dm, err := repository.ModelToDomainModel(item); err != nil {
return 0, dms, err
} else {
dms = append(dms, dm)
}
}
return total, dms, nil
}
func (repository *ChartSettingRepository) ModelToDomainModel(from *models.ChartSetting) (*domain.ChartSetting, error) {
to := &domain.ChartSetting{}
err := copier.Copy(to, from)
return to, err
}
func (repository *ChartSettingRepository) DomainModelToModel(from *domain.ChartSetting) (*models.ChartSetting, error) {
to := &models.ChartSetting{}
err := copier.Copy(to, from)
return to, err
}
func NewChartSettingRepository(cache *cache.CachedRepository) domain.ChartSettingRepository {
return &ChartSettingRepository{CachedRepository: cache}
}
... ...
package transaction
import (
"context"
"fmt"
"gorm.io/gorm"
"sync"
)
type Context struct {
//启用事务标识
beginTransFlag bool
db *gorm.DB
session *gorm.DB
lock sync.Mutex
}
func (transactionContext *Context) Begin() error {
transactionContext.lock.Lock()
defer transactionContext.lock.Unlock()
transactionContext.beginTransFlag = true
tx := transactionContext.db.Begin()
transactionContext.session = tx
return nil
}
func (transactionContext *Context) Commit() error {
transactionContext.lock.Lock()
defer transactionContext.lock.Unlock()
if !transactionContext.beginTransFlag {
return nil
}
tx := transactionContext.session.Commit()
return tx.Error
}
func (transactionContext *Context) Rollback() error {
transactionContext.lock.Lock()
defer transactionContext.lock.Unlock()
if !transactionContext.beginTransFlag {
return nil
}
tx := transactionContext.session.Rollback()
return tx.Error
}
func (transactionContext *Context) DB() *gorm.DB {
if transactionContext.beginTransFlag && transactionContext.session != nil {
return transactionContext.session
}
return transactionContext.db
}
func NewTransactionContext(db *gorm.DB) *Context {
return &Context{
db: db,
}
}
type Conn interface {
Begin() error
Commit() error
Rollback() error
DB() *gorm.DB
}
// UseTrans when beginTrans is true , it will begin a new transaction
// to execute the function, recover when panic happen
func UseTrans(ctx context.Context,
db *gorm.DB,
fn func(context.Context, Conn) error, beginTrans bool) (err error) {
var tx Conn
tx = NewTransactionContext(db)
if beginTrans {
if err = tx.Begin(); err != nil {
return
}
}
defer func() {
if p := recover(); p != nil {
if e := tx.Rollback(); e != nil {
err = fmt.Errorf("recover from %#v, rollback failed: %w", p, e)
} else {
err = fmt.Errorf("recoveer from %#v", p)
}
} else if err != nil {
if e := tx.Rollback(); e != nil {
err = fmt.Errorf("transaction failed: %s, rollback failed: %w", err, e)
}
} else {
err = tx.Commit()
}
}()
return fn(ctx, tx)
}
func PaginationAndCount(ctx context.Context, tx *gorm.DB, params map[string]interface{}, v interface{}) (int64, *gorm.DB) {
var total int64
var enableCounter bool = false
if v, ok := params["enableCounter"]; ok {
enableCounter = v.(bool)
}
if enableCounter {
tx = tx.Count(&total)
return total, tx
}
tx = tx.Count(&total)
if tx.Error != nil {
return total, tx
}
if v, ok := params["offset"]; ok {
tx.Offset(v.(int))
}
if v, ok := params["limit"]; ok {
tx.Limit(v.(int))
}
if tx = tx.Find(v); tx.Error != nil {
return 0, tx
}
return total, tx
}
... ...
package domain
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/tool"
"golang.org/x/exp/slices"
"sort"
"strings"
)
type Chart struct {
Id int64 // ID
Pid int64 // 父级ID
Type string // 类型
Sort int // 排序
Name string // 名称
Group string // 分组
TenantId int64 // 租户ID
CreatedAt int64 `json:",omitempty"`
UpdatedAt int64 `json:",omitempty"`
DeletedAt int64 `json:",omitempty"`
Version int `json:",omitempty"`
}
type ChartRepository interface {
Insert(ctx context.Context, conn transaction.Conn, dm *Chart) (*Chart, error)
Update(ctx context.Context, conn transaction.Conn, dm *Chart) (*Chart, error)
UpdateWithVersion(ctx context.Context, conn transaction.Conn, dm *Chart) (*Chart, error)
Delete(ctx context.Context, conn transaction.Conn, dm *Chart) (*Chart, error)
FindOne(ctx context.Context, conn transaction.Conn, id int64) (*Chart, error)
Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*Chart, error)
FindOneByGroup(ctx context.Context, conn transaction.Conn, tenantId, pid int64) (*Chart, error)
}
/*************** 索引函数 开始****************/
func IndexTenantId(tenantId int64) IndexQueryOptionFunc {
return func() QueryOptions {
return NewQueryOptions().WithKV("tenantId", tenantId)
}
}
func IndexChartPid(tenantId, pid int64) IndexQueryOptionFunc {
return func() QueryOptions {
return NewQueryOptions().WithKV("tenantId", tenantId).WithKV("pid", pid)
}
}
func (m *Chart) Identify() interface{} {
if m.Id == 0 {
return nil
}
return m.Id
}
func (m *Chart) RandName() string {
randNumber := tool.Krand(6, tool.KC_RAND_KIND_NUM)
var result strings.Builder
switch m.Type {
case ChartTypeReport:
result.WriteString("未命名报表")
case ChartTypeGroup:
result.WriteString("未命名分组")
case ChartTypeChart:
result.WriteString("未命名图表")
}
result.WriteString(randNumber)
return result.String()
}
// Reorder 重新排序charts
// appendFlag:true 追加 target 到 charts
// appendFlag:false 不追加
func Reorder(charts []*Chart, target *Chart, appendFlag bool) []*Chart {
// 重排序
var result = make([]*Chart, 0)
sort.SliceStable(charts, func(i, j int) bool {
return charts[i].Sort < charts[j].Sort
})
for index, c := range charts {
c.Sort = index + 1
if c.Id == target.Id {
result = append(result, &Chart{Sort: c.Sort}) // 旧节点继续追击啊,ID设置为0,后面跳过更新
continue
}
result = append(result, c)
}
// 插入新的节点
if appendFlag {
index := slices.IndexFunc(result, func(c *Chart) bool {
if float64(c.Sort) > float64(target.Sort)-0.5 {
return true
}
return false
})
if index == -1 {
index = len(result)
}
result = slices.Insert(result, index, target)
}
// 重排序
var index = 1
for i := range result {
if result[i].Id == 0 {
continue
}
result[i].Sort = index
index++
}
return result
}
... ...
package domain
type ChartProperty struct {
Title *Title `json:"title,optional"` // 标题
FilterRule *FilterRule `json:"filterRule,optional"` // 过滤规则
Series []*Series `json:"series,optional"` // 系列
Cover string `json:"cover,optional"` // 封面
//XAxis interface{} `json:"xAxis"` // X轴
//YAxis interface{} `json:"yAxis"` // Y轴
}
type Series struct {
Type string `json:"type"` // 图表类型 (记录型表格:RecordTable-1 总体指标:MetricsCard-1 容器卡片:ContainerCard-1 四分图:QuarterChart-1)
Name string `json:"name"` // 名称
Data interface{} `json:"data"` // 保存配置的时候置空
Config *DataConfig `json:"config"` // 配置
}
type Title struct {
MainTitle *TitleInfo `json:"main,optional"` // 主标题
SubTitle *TitleInfo `json:"sub,optional"` // 副标题
}
type TitleInfo struct {
Content string `json:"content,optional"` // 标题内容
Description *Description `json:"description,optional"` // 描述
}
type Description struct {
Type string `json:"type"` // text:文字 attachment:附件
Remark string `json:"remark,optional"` // 备注说明
Attachment *File `json:"attachment,optional"` // 附件
}
type File struct {
FileName string `json:"name"`
Url string `json:"url"`
}
type FilterRule struct {
FilterItems []*FilterItem `json:"items"`
}
type FilterItem struct {
FieldItem
}
// FieldItem 字段项
type FieldItem struct {
Source
Field string `json:"field"`
}
type Source struct {
From string `json:"from"` // 数据源类型 ByteBank:字库 User:用户自定义
SourceId int64 `json:"id"` // 数据源ID(from值为ByteBank时有值)
CustomData interface{} `json:"customData"` // 自定义数据(from值为User时有值)
}
type DataConfig struct {
Source Source `json:"source"`
}
... ...
package domain
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/db/transaction"
)
type ChartSetting struct {
Id int64 // ID
ChartId int64 // 图表ID
Property string // 属性
Style string // 样式
Series string // 系列值-数据绑定
CreatedAt int64 `json:",omitempty"`
UpdatedAt int64 `json:",omitempty"`
DeletedAt int64 `json:",omitempty"`
Version int `json:",omitempty"`
}
type ChartSettingRepository interface {
Insert(ctx context.Context, conn transaction.Conn, dm *ChartSetting) (*ChartSetting, error)
Update(ctx context.Context, conn transaction.Conn, dm *ChartSetting) (*ChartSetting, error)
UpdateWithVersion(ctx context.Context, conn transaction.Conn, dm *ChartSetting) (*ChartSetting, error)
Delete(ctx context.Context, conn transaction.Conn, dm *ChartSetting) (*ChartSetting, error)
FindOne(ctx context.Context, conn transaction.Conn, id int64) (*ChartSetting, error)
Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*ChartSetting, error)
}
func (m *ChartSetting) Identify() interface{} {
if m.Id == 0 {
return nil
}
return m.Id
}
... ...
package domain
import (
"github.com/magiconair/properties/assert"
"testing"
)
func TestReorder(t *testing.T) {
//chartsAfter:
//target := &Chart{Id: 3, Pid: 0, Sort: 3}
inputs := []struct {
charts []*Chart
targetId int64
sortIndex int
pid int64
appendFlag bool
want []int64
}{
{createChartsBefore(), 3, 1, 0, true, []int64{3, 1, 2}},
{createChartsBefore(), 3, 2, 0, true, []int64{1, 3, 2}},
{createChartsBefore(), 3, 3, 0, true, []int64{1, 2, 3}},
{createChartsBefore(), 3, 4, 0, true, []int64{1, 2, 3}},
{createChartsBefore(), 3, 1, 1, false, []int64{1, 2}},
}
for _, input := range inputs {
assert.Equal(t, chartIds(Reorder(input.charts, &Chart{Id: input.targetId, Pid: input.pid, Sort: input.sortIndex}, input.appendFlag)), input.want)
}
}
func chartIds(charts []*Chart) []int64 {
var idList = make([]int64, 0)
for _, c := range charts {
if c.Id == 0 {
continue
}
idList = append(idList, c.Id)
}
return idList
}
func createChartsBefore() []*Chart {
return []*Chart{
{Id: 1, Pid: 0, Sort: 1},
{Id: 2, Pid: 0, Sort: 2},
{Id: 3, Pid: 0, Sort: 3},
}
}
func createChartsAfter() []*Chart {
return []*Chart{
{Id: 5, Pid: 1, Sort: 1},
{Id: 6, Pid: 1, Sort: 2},
{Id: 7, Pid: 1, Sort: 3},
}
}
... ...
package domain
import "github.com/samber/lo"
const (
ChartTypeReport = "report" // 报表
ChartTypeGroup = "group" // 分组
ChartTypeChart = "chart" // 图表
)
func ChartTypeContain(t string) bool {
return lo.Contains([]string{ChartTypeReport, ChartTypeGroup, ChartTypeChart}, t)
}
... ...
package domain
import "reflect"
func OffsetLimit(page, size int) (offset int, limit int) {
if page == 0 {
page = 1
}
if size == 0 {
size = 20
}
offset = (page - 1) * size
limit = size
return
}
type QueryOptions map[string]interface{}
func NewQueryOptions() QueryOptions {
options := make(map[string]interface{})
return options
}
func (options QueryOptions) WithOffsetLimit(page, size int) QueryOptions {
offset, limit := OffsetLimit(page, size)
options["offset"] = offset
options["limit"] = limit
return options
}
func (options QueryOptions) WithKV(key string, value interface{}) QueryOptions {
if reflect.ValueOf(value).IsZero() {
return options
}
options[key] = value
return options
}
func (options QueryOptions) EnableCounter() QueryOptions {
return options.WithKV("enableCounter", true)
}
func (options QueryOptions) Copy() QueryOptions {
newOptions := NewQueryOptions()
for k, v := range options {
newOptions[k] = v
}
return newOptions
}
type IndexQueryOptionFunc func() QueryOptions
... ...
package domain
import (
"errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var (
ErrNotFound = sqlx.ErrNotFound
ErrUpdateFail = errors.New("sql: no rows affected")
)
var ProjectName = "project"
... ...
package gateway
import (
"encoding/json"
"fmt"
)
type MessageCode struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
// Response 统一消息返回格式
type Response struct {
MessageCode
Data json.RawMessage `json:"data"`
}
//
//type Request struct {
// Url string
// Method string
// Param interface{}
//}
type HttpError struct {
Base Response
}
func (e HttpError) Error() string {
return fmt.Sprintf("HttpError code:%d msg:%s", e.Base.Code, e.Base.Msg)
}
... ...
package gateway
import (
"context"
"encoding/json"
"fmt"
"github.com/zeromicro/go-zero/core/mapping"
"github.com/zeromicro/go-zero/rest/httpc"
"net/http"
"strings"
"time"
)
type Service struct {
Timeout time.Duration
host string
Interceptor func(msg string)
ServiceName string
service httpc.Service
}
func NewService(name string, host string, timeout time.Duration, opts ...httpc.Option) Service {
client := &http.Client{}
client.Timeout = timeout
service := Service{
service: httpc.NewServiceWithClient(name, client, opts...),
}
return service
}
func (gateway Service) Do(ctx context.Context, url string, method string, val interface{}, result interface{}) error {
var (
baseResponse = &Response{}
begin = time.Now()
)
response, err := gateway.service.Do(ctx, gateway.host+url, method, val)
defer func() {
jsonParam, _ := json.Marshal(val)
jsonData, _ := json.Marshal(result)
if err != nil {
result = err.Error()
}
if gateway.Interceptor != nil {
gateway.Interceptor(fmt.Sprintf("【网关】%v | %v%v | %v : %v \n-->> %v \n<<-- %v", time.Since(begin), gateway.host, url, strings.ToUpper(method),
result,
string(jsonParam),
string(jsonData),
))
}
}()
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return HttpError{
Base: Response{
MessageCode: MessageCode{
Code: response.StatusCode,
Msg: response.Status,
},
},
}
}
if err = httpc.ParseJsonBody(response, baseResponse); err != nil {
return err
}
if err = mapping.UnmarshalJsonBytes(baseResponse.Data, result); err != nil {
return err
}
return nil
}
... ...
package bytelib
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/cmd/chart-server/interanl/pkg/gateway"
)
type ByteMetadataService struct {
gateway.Service
}
... ...
package bytelib
import (
"context"
"net/http"
)
func (gateway *ByteMetadataService) TableDataPreview(ctx context.Context, request *TableDataPreviewRequest) (TablePreviewResponse, error) {
var result TablePreviewResponse
if err := gateway.Do(ctx, "/data/table-preview", http.MethodPost, request, &result); err != nil {
return result, err
}
return result, nil
}
type TableDataPreviewRequest struct {
ObjectId int64 `json:"objectId"` //表ID
ObjectType string `json:"objectType"` //表类型 File:文件 MetaTable:元表 DBTable:数据库表 ;当对象类型为DBTable时 objectId 1:表操作日志 2.拆解模块日志
Where *TableQueryWhere `json:"where"` //查询条件
UseCache bool `json:"useCache"`
HiddenData bool `json:"hiddenData"` // 隐藏数据,只返回结构
}
type TableQueryWhere struct {
PageNumber int64 `json:"pageNumber"`
PageSize int64 `json:"pageSize"`
Conditions []*TableQueryCondition `json:"conditions"`
}
type TableQueryCondition struct {
Field *Field `json:"field"` //字段
Like string `json:"like"` //模糊匹配
In []string `json:"in"` //包含项
Ex []string `json:"ex"` //排除项
Order string `json:"order"` //排序 ASC正序 DESC降序
}
type TablePreviewResponse TableData
type TablePreview struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data *TableData `json:"data"`
}
type TableData struct {
//表ID
ObjectId int64 `json:"objectId"`
//表名
Name string `json:"name"`
//数据
Grid *TableDataGrid `json:"grid"`
//字段
Fields []*Field `json:"fields"`
}
type TableDataGrid struct {
//数据列表
List []interface{} `json:"list"`
//总记录数
Total int64 `json:"total"`
}
... ...
package bytelib
import (
"context"
"net/http"
)
func (gateway *ByteMetadataService) TableInfo(ctx context.Context, request *TableInfoRequest) (TableInfoResponse, error) {
var result TableInfoResponse
if err := gateway.Do(ctx, "/tables/:tableId", http.MethodGet, request, &result); err != nil {
return result, err
}
return result, nil
}
type TableInfoRequest struct {
TableId int `path:"tableId"`
}
type TableInfoResponse struct {
// 表Id
TableId int `json:"tableId"`
// 表类型 MainTable:主表 SideTable:副表 SubTable:分表 ExcelTable:Excel表
TableType string `json:"tableType"`
// 名称
Name string `json:"name"`
// 父级ID
ParentId int `json:"parentId"`
// 主表字段
//MainTableFields []*Field `json:"mainTableFields"`
// 手动添加字段
//ManualFields []*Field `json:"manualFields"`
// 数据列
Fields []*Field `json:"fields"`
// 模块 应用于模块 1:数控中心 2:拆解模块 4:计算模块
Module int `json:"module"`
}
... ...
package bytelib
import (
"context"
"net/http"
)
func (gateway *ByteMetadataService) ObjectTableSearch(ctx context.Context, request ObjectTableSearchRequest) (ObjectTableSearchResponse, error) {
result := ObjectTableSearchResponse{}
if err := gateway.Do(ctx, "/data/table-object-search", http.MethodPost, request, &result); err != nil {
return result, err
}
return result, nil
}
type ObjectTableSearchRequest struct {
// 表名称
Name string `cname:"表名称" json:"name"`
//ViewType string `cname:"视图类型 full:完整 main:主表关系" json:"viewType"`
// 表类型 MainTable:主表 SideTable:副表 SubTable:分表 ExcelTable:Excel表 SubProcess:子过程 Schema:方案
TableTypes []string `cname:"表类型 MainTable:主表 SideTable:副表 SubTable:分表 ExcelTable:Excel表" json:"tableTypes" valid:"Required"`
// 父级ID
ParentId int `cname:"父级ID" json:"parentId"`
// 模块 应用于模块 1:数控中心 2:拆解模块 4:计算模块
Module int `json:"module"`
// 父级ID
ParentTableId int `cname:"父级ID" json:"parentTableId"`
// 返回结构信息
ReturnDetailStructInfo bool `cname:"返回具体的结构信息 默认不返回" json:"returnDetailStructInfo"`
// 排除分组项,只返回一级列表;默认 false 不排除,连分组也返回
ReturnGroupItem bool `cname:"排除分组" json:"returnGroupItem"`
// 排除指定表
ExcludeTables []int `cname:"排除指定表" json:"excludeTables"`
FilterRules []*FilterRule `json:"filterRules"`
TableId int `cname:"ID" json:"tableId"`
}
type ObjectTableSearchResponse struct {
Count int `json:"count"`
List []*Table `json:"list"`
}
type FilterRule struct {
// *:匹配所有
TableType string `json:"tableType"`
Status int `json:"status"`
}
type Table struct {
// 序号
Id int `json:"id"`
// 表Id
TableId int `json:"tableId"`
// 表类型 MainTable:主表 SideTable:副表 SubTable:分表 ExcelTable:Excel表
TableType string `json:"tableType"`
// 名称
Name string `json:"name"`
// 对应数据库名称
SQLName string `json:"sqlName,omitempty"`
// 父级ID
ParentId int `json:"parentId"`
// 模块 应用于模块 1:数控中心 2:拆解模块 4:计算模块
Module int `json:"module"`
// 标识
Flag string `json:"flag,omitempty"`
// 启用状态
Status int `json:"status"`
// 冲突状态
InConflict bool `json:"inConflict"`
// 表字段
Fields []*Field `json:"fields"`
}
// Field 字段
type Field struct {
// 名称
Name string `json:"name"`
// 对应数据库名称
SQLName string `json:"sqlName"`
// 对应数据库类型
SQLType string `json:"sqlType"`
// 标识 1.主键 2:主表字段 3:手动添加
Flag int `json:"flag"`
}
... ...
package bytelib
var (
MainTable TableType = "MainTable" // 主表
SideTable TableType = "SideTable" // 副表
SubTable TableType = "SubTable" // 分表
SchemaTable TableType = "Schema" // 方案
SubProcessTable TableType = "SubProcess" // 子过程
CalculateItem TableType = "CalculateItem" // 计算项
CalculateTable TableType = "CalculateTable" // 计算表
CalculateSet TableType = "CalculateSet" // 计算集
)
type TableType string
func (t TableType) ToString() string {
return string(t)
}
... ...
package gateway
import "net/http"
type RequestOptions struct {
Header http.Header
// key:form key value:path
FileMap map[string]string
}
type Option func(o *RequestOptions)
func WithHeader(header http.Header) Option {
return func(o *RequestOptions) {
o.Header = header
}
}
func WithFileMap(v map[string]string) Option {
return func(o *RequestOptions) {
o.FileMap = v
}
}
... ...
## 生成模型
```
goctl model mysql ddl -s .\doc\dsl\model\table.sql -d cmd/chart-server
```
## api生成
```
goctl api go -api .\doc\dsl\api\core.api -dir cmd/chart-server/api -style go_zero
```
## swagger 生成
```
goctl api plugin -plugin goctl-swagger="swagger -filename core.json" -api .\doc\dsl\api\core.api -dir .\doc\dsl\api
```
## 镜像生成
```
docker build -f Dockerfile -t tiptok/sumifcc-bchart:1.0.0 .
```
\ No newline at end of file
... ...
import "core/chart.api"
// import "./core/chart_stting.api"
\ No newline at end of file
... ...
{
"swagger": "2.0",
"info": {
"title": "",
"version": ""
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"v1/chart": {
"post": {
"summary": "保存图表",
"operationId": "saveChart",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/ChartSaveResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/ChartSaveRequest"
}
}
],
"requestBody": {},
"tags": [
"chart"
]
}
},
"v1/chart/rename": {
"post": {
"summary": "更新图表排序",
"operationId": "renameChartSort",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/ChartUpdateSortResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/ChartUpdateSortRequest"
}
}
],
"requestBody": {},
"tags": [
"chart"
]
}
},
"v1/chart/search": {
"post": {
"summary": "搜索图表",
"operationId": "searchChart",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/ChartSearchResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/ChartSearchRequest"
}
}
],
"requestBody": {},
"tags": [
"chart"
]
}
},
"v1/chart/sort": {
"post": {
"summary": "更新图表排序",
"operationId": "updateChartSort",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/ChartUpdateSortResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/ChartUpdateSortRequest"
}
}
],
"requestBody": {},
"tags": [
"chart"
]
}
},
"v1/chart/{id}": {
"get": {
"summary": "获取图表详情",
"operationId": "getChart",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/ChartGetResponse"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
}
],
"requestBody": {},
"tags": [
"chart"
]
},
"delete": {
"summary": "删除图表",
"operationId": "deleteChart",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/ChartDeleteResponse"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/ChartDeleteRequest"
}
}
],
"requestBody": {},
"tags": [
"chart"
]
},
"put": {
"summary": "更新图表(配置)",
"operationId": "updateChart",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/ChartUpdateResponse"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/ChartUpdateRequest"
}
}
],
"requestBody": {},
"tags": [
"chart"
]
}
}
},
"definitions": {
"ChartDeleteRequest": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
},
"title": "ChartDeleteRequest",
"required": [
"id"
]
},
"ChartDeleteResponse": {
"type": "object",
"title": "ChartDeleteResponse"
},
"ChartGetRequest": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
},
"title": "ChartGetRequest",
"required": [
"id"
]
},
"ChartGetResponse": {
"type": "object",
"properties": {
"chart": {
"$ref": "#/definitions/ChartItem"
}
},
"title": "ChartGetResponse",
"required": [
"chart"
]
},
"ChartItem": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"description": " ID"
},
"pid": {
"type": "integer",
"format": "int64",
"description": " 父级ID"
},
"type": {
"type": "string",
"description": " 类型 report:报表 group:分组 chart:图表"
},
"sort": {
"type": "integer",
"format": "int64",
"description": " 排序"
},
"name": {
"type": "string",
"description": " 名称"
},
"charts": {
"type": "array",
"items": {
"$ref": "#/definitions/ChartItem"
}
}
},
"title": "ChartItem"
},
"ChartRenameRequest": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string",
"description": " 名称"
}
},
"title": "ChartRenameRequest",
"required": [
"id",
"name"
]
},
"ChartRenameResponse": {
"type": "object",
"title": "ChartRenameResponse"
},
"ChartSaveRequest": {
"type": "object",
"properties": {
"pid": {
"type": "integer",
"format": "int64",
"description": " 父级ID"
},
"type": {
"type": "string",
"description": " 类型 report:报表 group:分组 chart:图表"
},
"name": {
"type": "string",
"description": " 名称"
}
},
"title": "ChartSaveRequest",
"required": [
"type"
]
},
"ChartSaveResponse": {
"type": "object",
"properties": {
"chart": {
"$ref": "#/definitions/ChartItem"
}
},
"title": "ChartSaveResponse",
"required": [
"chart"
]
},
"ChartSearchRequest": {
"type": "object",
"properties": {
"page": {
"type": "integer",
"format": "int32"
},
"size": {
"type": "integer",
"format": "int32"
}
},
"title": "ChartSearchRequest",
"required": [
"page",
"size"
]
},
"ChartSearchResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/ChartItem"
}
},
"total": {
"type": "integer",
"format": "int64"
}
},
"title": "ChartSearchResponse",
"required": [
"list",
"total"
]
},
"ChartUpdateRequest": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
},
"title": "ChartUpdateRequest",
"required": [
"id"
]
},
"ChartUpdateResponse": {
"type": "object",
"title": "ChartUpdateResponse"
},
"ChartUpdateSortRequest": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"sort": {
"type": "integer",
"format": "int32"
},
"pid": {
"type": "integer",
"format": "int32"
}
},
"title": "ChartUpdateSortRequest",
"required": [
"id",
"sort",
"pid"
]
},
"ChartUpdateSortResponse": {
"type": "object",
"title": "ChartUpdateSortResponse"
}
},
"securityDefinitions": {
"apiKey": {
"type": "apiKey",
"description": "Enter JWT Bearer token **_only_**",
"name": "Authorization",
"in": "header"
}
},
"security": [
{
"apiKey": []
}
]
}
... ...
syntax = "v1"
info(
title: "xx实例"
desc: "xx实例"
author: "author"
email: "email"
version: "v1"
)
@server(
prefix: v1
group: chart
jwt: JwtAuth
//middleware: Authority
)
service Core {
@doc "获取图表详情"
@handler getChart
get /chart/:id (ChartGetRequest) returns (ChartGetResponse)
@doc "保存图表"
@handler saveChart
post /chart (ChartSaveRequest) returns (ChartSaveResponse)
@doc "删除图表"
@handler deleteChart
delete /chart/:id (ChartDeleteRequest) returns (ChartDeleteResponse)
@doc "更新图表(配置)"
@handler updateChart
put /chart/:id (ChartUpdateRequest) returns (ChartUpdateResponse)
@doc "搜索图表"
@handler searchChart
post /chart/search (ChartSearchRequest) returns (ChartSearchResponse)
@doc "移动图表"
@handler updateChartSort
post /chart/move (ChartUpdateSortRequest) returns (ChartUpdateSortResponse)
@doc "重命名图表"
@handler renameChartSort
post /chart/rename (ChartRenameRequest) returns (ChartRenameResponse)
}
type (
ChartGetRequest {
Id int64 `path:"id"`
}
ChartGetResponse struct{
Chart ChartItem `json:"chart"`
}
ChartSaveRequest struct{
Pid int64 `json:"pid,optional"`// 父级ID
Type string `json:"type"`// 类型 report:报表 group:分组 chart:图表
Name string `json:"name,optional"`// 名称
}
ChartSaveResponse struct{
Chart ChartItem `json:"chart"`
}
ChartDeleteRequest struct{
Id int64 `path:"id"`
}
ChartDeleteResponse struct{}
ChartUpdateRequest struct{
Id int64 `path:"id"`
}
ChartUpdateResponse struct{}
ChartSearchRequest struct {
IncludeTypes []string `json:"includeTypes,optional"` //包含的类型: 类型 report:报表 group:分组 chart:图表(未指定返回所有)
DataStyle string `json:"dataStyle,optional"` // 数据样式 tree:树形 flat:平铺
}
ChartSearchResponse{
List []ChartItem `json:"list"`
Total int64 `json:"total"`
}
ChartUpdateSortRequest struct{
Id int64 `json:"id"`
Pid int64 `json:"pid"`
Index int `json:"index"` // 元素下标
}
ChartUpdateSortResponse struct{}
ChartRenameRequest struct{
Id int64 `json:"id"`
Name string `json:"name"`// 名称
}
ChartRenameResponse struct{}
ChartItem struct{
Id int64 `json:"id,optional"`// ID
Pid int64 `json:"pid,optional"`// 父级ID
Type string `json:"type,optional"`// 类型 report:报表 group:分组 chart:图表
Sort int64 `json:"sort,optional"`// 排序
Name string `json:"name,optional"`// 名称
Charts []ChartItem `json:"charts,optional"`
}
)
... ...
syntax = "v1"
info(
title: "xx实例"
desc: "xx实例"
author: "author"
email: "email"
version: "v1"
)
@server(
prefix: v1
group: chart-setting
jwt: JwtAuth
)
service Core {
@handler getChartSetting
post /chart-setting/:id (ChartSettingGetRequest) returns (ChartSettingGetResponse)
@handler saveChartSetting
post /chart-setting (ChartSettingSaveRequest) returns (ChartSettingSaveResponse)
@handler deleteChartSetting
delete /chart-setting/:id (ChartSettingDeleteRequest) returns (ChartSettingDeleteResponse)
@handler updateChartSetting
put /chart-setting/:id (ChartSettingUpdateRequest) returns (ChartSettingUpdateResponse)
@handler searchChartSetting
post /chart-setting/search (ChartSettingSearchRequest) returns (ChartSettingSearchResponse)
}
type (
ChartSettingGetRequest {
Id int64 `path:"id"`
}
ChartSettingGetResponse struct{
ChartSetting ChartSettingItem `json:"chartSetting"`
}
ChartSettingSaveRequest struct{
ChartSetting ChartSettingItem `json:"chartSetting"`
}
ChartSettingSaveResponse struct{}
ChartSettingDeleteRequest struct{
Id int64 `path:"id"`
}
ChartSettingDeleteResponse struct{}
ChartSettingUpdateRequest struct{
Id int64 `path:"id"`
ChartSetting ChartSettingItem `json:"chartSetting"`
}
ChartSettingUpdateResponse struct{}
ChartSettingSearchRequest struct{
Page int `json:"page"`
Size int `json:"size"`
}
ChartSettingSearchResponse{
List []ChartSettingItem `json:"list"`
Total int64 `json:"total"`
}
ChartSettingItem struct{
}
)
... ...
CREATE TABLE `chart` (
`id` bigint(0) NOT NULL COMMENT 'ID',
`pid` bigint(0) NOT NULL COMMENT '父级ID',
`type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '类型',
`sort` int(0) NOT NULL COMMENT '排序',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名称',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `chart_setting` (
`id` bigint(0) NOT NULL COMMENT 'ID ',
`chart_id` bigint(0) NOT NULL COMMENT '图表ID',
`property` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '属性',
`style` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '样式',
`series` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '系列值-数据绑定',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
... ...
module gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart
go 1.19
require (
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/jinzhu/copier v0.4.0
github.com/jinzhu/now v1.1.5
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.4
github.com/tiptok/gocomm v1.0.14
github.com/zeromicro/go-zero v1.5.5
google.golang.org/grpc v1.57.0
gorm.io/driver/mysql v1.5.1
gorm.io/driver/postgres v1.5.2
gorm.io/gorm v1.25.4
gorm.io/plugin/soft_delete v1.2.1
)
require (
github.com/Shopify/sarama v1.37.2 // indirect
github.com/beego/beego/v2 v2.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/eapache/go-resiliency v1.3.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/garyburd/redigo v1.6.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.5.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.1 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.4.3 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.7.6 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/leodido/go-urn v1.1.0 // indirect
github.com/magiconair/properties v1.8.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openzipkin/zipkin-go v0.4.1 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.0 // indirect
github.com/spf13/jwalterweatherman v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.4.0 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
go.etcd.io/etcd/api/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/v3 v3.5.9 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.14.0 // indirect
go.opentelemetry.io/otel/sdk v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/automaxprocs v1.5.3 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/term v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.26.3 // indirect
k8s.io/apimachinery v0.27.0-alpha.3 // indirect
k8s.io/client-go v0.26.3 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230307230338-69ee2d25a840 // indirect
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
... ...
package cache
import (
"fmt"
"github.com/tiptok/gocomm/pkg/cache"
"github.com/tiptok/gocomm/pkg/cache/gzcache"
"github.com/tiptok/gocomm/pkg/log"
)
func NewMultiLevelCache(hosts []string, password string) *cache.MultiLevelCache {
fmt.Println("starting multi level cache...")
mlCache := cache.NewMultiLevelCacheNew(cache.WithDebugLog(true, func() log.Log {
return log.DefaultLog
}))
mlCache.RegisterCache(gzcache.NewClusterCache(hosts, password))
return mlCache
}
func NewCachedRepository(c *cache.MultiLevelCache, options ...cache.QueryOption) *cache.CachedRepository {
return cache.NewCachedRepository(c, options...)
}
... ...
package config
import (
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/zrpc"
"time"
)
type JWT struct {
Secret string `json:",optional"`
Expires time.Duration `json:",optional"`
}
type JwtAuth struct {
AccessSecret string
Expire int64
}
type Config struct {
JwtAuth JwtAuth `json:",optional"`
UserRpc zrpc.RpcClientConf `json:",optional"`
AuthRpc zrpc.RpcClientConf `json:",optional"`
PostRpc zrpc.RpcClientConf `json:",optional"`
CommentRpc zrpc.RpcClientConf `json:",optional"`
JWT JWT `json:",optional"`
DB struct {
DataSource string
} `json:",optional"`
Cache cache.CacheConf `json:",optional"`
DTM DTM `json:",optional"`
Sms Sms `json:",optional"`
Oss Oss `json:",optional"`
Wechat Wechat `json:",optional"` // 学员端微信
CoachClient Wechat `json:",optional"` // 教练端微信
OfficialAccount Wechat `json:",optional"`
ThirdWechatApps []Wechat `json:",optional"`
}
type DTM struct {
Server Server `json:",optional"`
}
type Server struct {
Name string `json:",optional"`
Host string `json:",optional"`
GRPC GRPC `json:",optional"`
HTTP HTTP `json:",optional"`
Metrics Metrics `json:",optional"`
}
type HTTP struct {
Port string
}
type GRPC struct {
Port string
}
type Metrics struct {
Port string
}
type Sms struct {
Debug bool
DebugCode string
Expire int `json:",default=180"`
MaxSendTime int `json:",default=5"`
CompanyName string
SecretId string
SecretKey string
SmsAppId string
Sign string
TemplateId string
}
type Oss struct {
OssEndPoint string
AccessKeyID string
AccessKeySecret string
BuckName string
RegionID string
RoleArn string
CDN CDN
}
type Wechat struct {
AppName string `json:",optional"`
AppID string
AppSecret string
MsgTemplates []Template `json:",optional"`
}
func (wx Wechat) GetTemplate(code string) (Template, bool) {
for _, temp := range wx.MsgTemplates {
if temp.Code == code {
return temp, true
}
}
return Template{}, false
}
type CDN struct {
HostPairs []string
}
type Template struct {
ID string // 模板ID
Name string // 模板名称
Code string // 模板编码
}
... ...
package contextdata
import (
"context"
)
func GetTenantFromCtx(ctx context.Context) int64 {
return 1
}
... ...
package contextdata
import (
"context"
"encoding/json"
"github.com/golang-jwt/jwt/v4"
"github.com/zeromicro/go-zero/core/logx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/config"
"time"
)
var (
CtxKeyJwtUserId = "UserId"
)
func GetUserIdFromCtx(ctx context.Context, key string) int64 {
var uid int64
if jsonUid, ok := ctx.Value(key).(json.Number); ok {
if int64Uid, err := jsonUid.Int64(); err == nil {
uid = int64Uid
} else {
logx.WithContext(ctx).Errorf("GetUidFromCtx err : %+v", err)
}
}
return uid
}
func getStringFromCtx(ctx context.Context, key string) string {
var uid string
if jsonUid, ok := ctx.Value(key).(string); ok {
return jsonUid
}
return uid
}
func getArrayInt64FromCtx(ctx context.Context, key string) []int64 {
values := ctx.Value(key)
var ids = make([]int64, 0)
if values == nil {
return ids
}
if list, ok := values.([]interface{}); ok {
for _, item := range list {
if jsonId, ok := item.(json.Number); ok {
id, _ := jsonId.Int64()
ids = append(ids, id)
}
}
}
return ids
}
func GetUserTokenFromCtx(ctx context.Context) UserToken {
return UserToken{
UserId: GetUserIdFromCtx(ctx, CtxKeyJwtUserId),
}
}
type UserToken struct {
UserId int64 `json:"userId"`
}
func (tk UserToken) GenerateToken(jwtConfig config.JwtAuth) (string, error) {
claims := make(jwt.MapClaims)
claims["exp"] = time.Now().Unix() + jwtConfig.Expire
claims["iat"] = time.Now().Unix()
claims["UserId"] = tk.UserId
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = claims
return token.SignedString([]byte(jwtConfig.AccessSecret))
}
func (tk *UserToken) ParseToken(jwtConfig config.JWT, str string) error {
return nil
}
// CheckUserInfo 如果UserToken有效 返回:true 否则返回false
func (tk *UserToken) CheckUserInfo() bool {
return !(tk.UserId > 100000000 || tk.UserId <= 0)
}
... ...
package database
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
func OpenGormDB(source string) *gorm.DB {
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Info, // Log level
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
Colorful: false, // Disable color
},
)
fmt.Println("starting db...")
db, err := gorm.Open(mysql.Open(source), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
return db
}
func OpenGormPGDB(source string) *gorm.DB {
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Info, // Log level
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
Colorful: false, // Disable color
},
)
fmt.Println("starting db...")
db, err := gorm.Open(postgres.Open(source), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
return db
}
... ...
package database
import (
"fmt"
"github.com/jinzhu/now"
"github.com/zeromicro/go-zero/core/stores/redis"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/migrator"
"gorm.io/gorm/schema"
"strings"
"time"
)
var (
// PartitionByRangeTime 按unix时间戳分区
PartitionByRangeTime = 1
// PartitionByHash 按系统的hash值分区
PartitionByHash = 2
// PartitionByList 按List包含值分区
PartitionByList = 3
)
type PartitionTable interface {
TableName() string
}
type PartitionMigrator struct {
ServiceName string
DB *gorm.DB
Redis *redis.Redis
}
func NewPartitionMigrator(serviceName string, db *gorm.DB, redis *redis.Redis) *PartitionMigrator {
return &PartitionMigrator{
DB: db,
ServiceName: serviceName,
Redis: redis,
}
}
func (c *PartitionMigrator) AutoMigrate(t PartitionTable, option ...PartitionOptionFunc) error {
options := NewPartitionOptions()
for i := range option {
option[i](options)
}
tableName := t.TableName()
if !c.DB.Migrator().HasTable(tableName) {
migrator := Migrator{migrator.Migrator{
migrator.Config{
CreateIndexAfterCreateTable: true,
DB: c.DB,
Dialector: c.DB.Dialector,
},
}}
if err := migrator.CreatePartitionTable(options, t); err != nil {
panic(err)
}
}
rk := fmt.Sprintf("%s:auto-partition:%s", c.ServiceName, tableName)
lock := redis.NewRedisLock(c.Redis, rk)
ok, err := lock.Acquire()
if !ok || err != nil {
return nil
}
defer lock.Release()
switch options.Type {
case PartitionByRangeTime:
begin := options.TimeBegin
end := options.TimeEnd
for {
if begin.Unix() > end.Unix() {
break
}
pTable := fmt.Sprintf("%s_%s", tableName, options.FormatTimeSubFunc(begin))
sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES FROM (%d) TO (%d);",
pTable, tableName, begin.Unix(), begin.AddDate(0, options.TimeSpanMonth, 0).Unix())
tx := c.DB.Exec(sql)
if tx.Error != nil {
return tx.Error
}
c.log(t, pTable)
begin = begin.AddDate(0, options.TimeSpanMonth, 0)
}
break
case PartitionByHash:
for i := 0; i < options.Modulus; i++ {
pTable := fmt.Sprintf("%s_%d", tableName, i)
sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES WITH (MODULUS %d, REMAINDER %d);",
pTable, tableName, options.Modulus, i)
tx := c.DB.Exec(sql)
if tx.Error != nil {
return tx.Error
}
c.log(t, pTable)
}
break
case PartitionByList:
for i := 0; i < len(options.ListRange); i++ {
pTable := fmt.Sprintf("%s_%d", tableName, i)
sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES IN %s;",
pTable, tableName, InArgs(options.ListRange[i]))
tx := c.DB.Exec(sql)
if tx.Error != nil {
return tx.Error
}
c.log(t, pTable)
}
break
default:
return nil
}
return nil
}
func (c *PartitionMigrator) log(t PartitionTable, pTable string) {
fmt.Println("【自动分区】 create partition table", pTable, "on table", t.TableName())
}
type PartitionOptions struct {
// 分区类型 1:Hash 2:RangeTime
Type int
// 分区列
Column string
// Hash分区
Modulus int
// List 范围
ListRange []interface{}
// Range时间分区
TimeBegin time.Time
TimeEnd time.Time
TimeSpanMonth int
FormatTimeSubFunc func(time.Time) string
// 禁用PrimaryKey生成
// 分区字段有函数表达式的,需要禁用掉PrimaryKey,使用自定义的唯一ID生成规则
DisablePrimaryKey bool
}
func NewPartitionOptions() *PartitionOptions {
return &PartitionOptions{
Type: PartitionByRangeTime,
FormatTimeSubFunc: func(t time.Time) string {
return t.Format("200601")
},
}
}
func (c *PartitionOptions) Sql() string {
if c.Type == PartitionByHash {
return fmt.Sprintf("PARTITION BY HASH(%s)", c.Column)
}
if c.Type == PartitionByRangeTime {
return fmt.Sprintf("PARTITION BY RANGE(%s)", c.Column)
}
if c.Type == PartitionByList {
return fmt.Sprintf("PARTITION BY LIST(%s)", c.Column)
}
return ""
}
type PartitionOptionFunc func(*PartitionOptions)
func WithPartitionType(t int) PartitionOptionFunc {
return func(options *PartitionOptions) {
options.Type = t
}
}
func WithPartitionColumn(c string) PartitionOptionFunc {
return func(options *PartitionOptions) {
options.Column = c
}
}
func WithPartitionHash(modulus int) PartitionOptionFunc {
return func(options *PartitionOptions) {
options.Modulus = modulus
}
}
func WithPartitionRangeTime(begin, end time.Time, spanMonth int) PartitionOptionFunc {
return func(options *PartitionOptions) {
options.TimeBegin = begin
options.TimeEnd = end
options.TimeSpanMonth = spanMonth
}
}
func WithPartitionList(list ...interface{}) PartitionOptionFunc {
return func(options *PartitionOptions) {
options.ListRange = list
}
}
func WithDisablePrimaryKey(disablePrimaryKey bool) PartitionOptionFunc {
return func(options *PartitionOptions) {
options.DisablePrimaryKey = disablePrimaryKey
}
}
func Date(date string) time.Time {
return now.MustParse(date)
}
type Migrator struct {
migrator.Migrator
}
// CreatePartitionTable create table in database for values
func (m Migrator) CreatePartitionTable(options *PartitionOptions, values ...interface{}) error {
for _, value := range m.ReorderModels(values, false) {
tx := m.DB.Session(&gorm.Session{})
if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) {
var (
createTableSQL = "CREATE TABLE ? ("
values = []interface{}{m.CurrentTable(stmt)}
hasPrimaryKeyInDataType bool
)
for _, dbName := range stmt.Schema.DBNames {
field := stmt.Schema.FieldsByDBName[dbName]
if !field.IgnoreMigration {
createTableSQL += "? ?"
hasPrimaryKeyInDataType = hasPrimaryKeyInDataType || strings.Contains(strings.ToUpper(string(field.DataType)), "PRIMARY KEY")
values = append(values, clause.Column{Name: dbName}, m.DB.Migrator().FullDataTypeOf(field))
createTableSQL += ","
}
}
if !hasPrimaryKeyInDataType && len(stmt.Schema.PrimaryFields) > 0 && !options.DisablePrimaryKey {
createTableSQL += "PRIMARY KEY ?,"
primaryKeys := []interface{}{}
for _, field := range stmt.Schema.PrimaryFields {
primaryKeys = append(primaryKeys, clause.Column{Name: field.DBName})
}
values = append(values, primaryKeys)
}
for _, idx := range stmt.Schema.ParseIndexes() {
if m.CreateIndexAfterCreateTable {
defer func(value interface{}, name string) {
if errr == nil {
errr = tx.Migrator().CreateIndex(value, name)
}
}(value, idx.Name)
} else {
if idx.Class != "" {
createTableSQL += idx.Class + " "
}
createTableSQL += "INDEX ? ?"
if idx.Comment != "" {
createTableSQL += fmt.Sprintf(" COMMENT '%s'", idx.Comment)
}
if idx.Option != "" {
createTableSQL += " " + idx.Option
}
createTableSQL += ","
values = append(values, clause.Column{Name: idx.Name}, tx.Migrator().(migrator.BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt))
}
}
if !m.DB.DisableForeignKeyConstraintWhenMigrating && !m.DB.IgnoreRelationshipsWhenMigrating {
for _, rel := range stmt.Schema.Relationships.Relations {
if rel.Field.IgnoreMigration {
continue
}
if constraint := rel.ParseConstraint(); constraint != nil {
if constraint.Schema == stmt.Schema {
sql, vars := buildConstraint(constraint)
createTableSQL += sql + ","
values = append(values, vars...)
}
}
}
}
for _, chk := range stmt.Schema.ParseCheckConstraints() {
createTableSQL += "CONSTRAINT ? CHECK (?),"
values = append(values, clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint})
}
createTableSQL = strings.TrimSuffix(createTableSQL, ",")
createTableSQL += ")"
if options != nil {
createTableSQL += options.Sql()
}
if tableOption, ok := m.DB.Get("gorm:table_options"); ok {
createTableSQL += fmt.Sprint(tableOption)
}
errr = tx.Exec(createTableSQL, values...).Error
return errr
}); err != nil {
return err
}
}
return nil
}
func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) {
sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??"
if constraint.OnDelete != "" {
sql += " ON DELETE " + constraint.OnDelete
}
if constraint.OnUpdate != "" {
sql += " ON UPDATE " + constraint.OnUpdate
}
var foreignKeys, references []interface{}
for _, field := range constraint.ForeignKeys {
foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName})
}
for _, field := range constraint.References {
references = append(references, clause.Column{Name: field.DBName})
}
results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references)
return
}
... ...
package database
import (
"github.com/zeromicro/go-zero/core/mapping"
"reflect"
)
func InArgs(args interface{}) string {
bytes := make([]byte, 0)
bytes = appendIn(bytes, reflect.ValueOf(args))
return string(bytes)
}
func Arg(args interface{}) string {
bytes := make([]byte, 0)
v := reflect.ValueOf(args)
bytes = appendValue(bytes, v)
return string(bytes)
}
func appendIn(b []byte, slice reflect.Value) []byte {
sliceLen := slice.Len()
b = append(b, '(')
for i := 0; i < sliceLen; i++ {
if i > 0 {
b = append(b, ',')
}
elem := slice.Index(i)
if elem.Kind() == reflect.Interface {
elem = elem.Elem()
}
if elem.Kind() == reflect.Slice {
//b = appendIn(b, elem)
} else {
b = appendValue(b, elem)
}
}
b = append(b, ')')
return b
}
func appendValue(b []byte, v reflect.Value) []byte {
if v.Kind() == reflect.Ptr && v.IsNil() {
return append(b, "NULL"...)
}
if v.Kind() == reflect.Int || v.Kind() == reflect.Int64 || v.Kind() == reflect.Float64 {
return append(b, []byte(mapping.Repr(v.Interface()))...)
}
b = append(b, []byte("'")...)
b = append(b, []byte(mapping.Repr(v.Interface()))...)
b = append(b, []byte("'")...)
return b
}
... ...
package database
... ...
package result
import (
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr"
"net/http"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpx"
"google.golang.org/grpc/status"
)
// http返回
func HttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
if err == nil {
//成功返回
r := Success(resp)
httpx.WriteJson(w, http.StatusOK, r)
} else {
//错误返回
errcode := xerr.SERVER_COMMON_ERROR
errmsg := "服务器开小差啦,稍后再来试一试"
internalErr := ""
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型
//自定义CodeError
errcode = e.GetErrCode()
errmsg = e.GetErrMsg()
if e.InternalError != nil {
internalErr = e.InternalError.Error()
}
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端
errcode = grpcCode
errmsg = gstatus.Message()
}
}
}
logx.WithContext(r.Context()).Errorf("【API-ERR】 : %+v ", err)
response := Error(errcode, errmsg)
response.Error = internalErr
httpx.WriteJson(w, http.StatusBadRequest, response)
}
}
// 授权的http方法
func AuthHttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
if err == nil {
//成功返回
r := Success(resp)
httpx.WriteJson(w, http.StatusOK, r)
} else {
//错误返回
errcode := xerr.SERVER_COMMON_ERROR
errmsg := "服务器开小差啦,稍后再来试一试"
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型
//自定义CodeError
errcode = e.GetErrCode()
errmsg = e.GetErrMsg()
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端
errcode = grpcCode
errmsg = gstatus.Message()
}
}
}
logx.WithContext(r.Context()).Errorf("【GATEWAY-ERR】 : %+v ", err)
httpx.WriteJson(w, http.StatusUnauthorized, Error(errcode, errmsg))
}
}
// http 参数错误返回
func ParamErrorResult(r *http.Request, w http.ResponseWriter, err error) {
errMsg := fmt.Sprintf("%s ,%s", xerr.MapErrMsg(xerr.REUQEST_PARAM_ERROR), err.Error())
httpx.WriteJson(w, http.StatusBadRequest, Error(xerr.REUQEST_PARAM_ERROR, errMsg))
}
... ...
package result
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc/status"
)
// job返回
func JobResult(ctx context.Context, resp interface{}, err error) {
if err == nil {
// 成功返回 ,只有dev环境下才会打印info,线上不显示
if resp != nil {
logx.Infof("resp: %+v", resp)
}
return
} else {
errCode := xerr.SERVER_COMMON_ERROR
errMsg := "服务器开小差啦,稍后再来试一试"
// 错误返回
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { // 自定义错误类型
// 自定义CodeError
errCode = e.GetErrCode()
errMsg = e.GetErrMsg()
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { // 区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端
errCode = grpcCode
errMsg = gstatus.Message()
}
}
}
logx.WithContext(ctx).Errorf("【JOB-ERR】 : %+v ,errCode:%d , errMsg:%s ", err, errCode, errMsg)
return
}
}
... ...
package result
type ResponseSuccessBean struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
type NullJson struct{}
func Success(data interface{}) *ResponseSuccessBean {
return &ResponseSuccessBean{Code: 0, Msg: "OK", Data: data}
}
type ResponseErrorBean struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
Error string `json:"err"`
}
func Error(errCode uint32, errMsg string) *ResponseErrorBean {
return &ResponseErrorBean{Code: errCode, Msg: errMsg}
}
... ...
package tool
import (
"crypto/md5"
"fmt"
"io"
)
/** 加密方式 **/
func Md5ByString(str string) string {
m := md5.New()
_, err := io.WriteString(m, str)
if err != nil {
panic(err)
}
arr := m.Sum(nil)
return fmt.Sprintf("%x", arr)
}
func Md5ByBytes(b []byte) string {
return fmt.Sprintf("%x", md5.Sum(b))
}
... ...
package tool
import (
"path/filepath"
"strings"
)
const (
Image = "image"
Video = "video"
)
var TypeMap = map[string]string{
"jpg": Image,
"png": Image,
"gif": Image,
"webp": Image,
"cr2": Image,
"tif": Image,
"bmp": Image,
"heif": Image,
"jxr": Image,
"psd": Image,
"ico": Image,
"dwg": Image,
"avif": Image,
"mp4": Video,
"m4v": Video,
"mkv": Video,
"webm": Video,
"mov": Video,
"avi": Video,
"wmv": Video,
"mpg": Video,
"flv": Video,
"3gp": Video,
}
var DefaultFileTypeDetector = FileTypeDetector{}
type FileTypeDetector struct {
}
func (c FileTypeDetector) Classify(medias []string, mediaType string) []string {
result := make([]string, 0)
for _, media := range medias {
v, ok := TypeMap[strings.Trim(filepath.Ext(media), ".")]
if !ok {
continue
}
if v == mediaType {
result = append(result, media)
}
}
return result
}
... ...
package tool
import (
jwt "github.com/golang-jwt/jwt/v4"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-bchart/pkg/config"
"time"
)
type UserToken struct {
UserId int64 `json:"userId"`
CoachId int64 `json:"coach_id"`
AdminId int64 `json:"adminId"`
ClientType string `json:"clientType"`
AccessShops []int64 `json:"accessShops"`
}
func (tk UserToken) GenerateToken(jwtConfig config.JwtAuth) (string, error) {
claims := make(jwt.MapClaims)
claims["exp"] = time.Now().Unix() + jwtConfig.Expire
claims["iat"] = time.Now().Unix()
claims["UserId"] = tk.UserId
claims["CoachId"] = tk.CoachId
claims["AdminId"] = tk.AdminId
claims["ClientType"] = tk.ClientType
claims["AccessShops"] = tk.AccessShops
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = claims
return token.SignedString([]byte(jwtConfig.AccessSecret))
}
func (tk *UserToken) ParseToken(jwtConfig config.JWT, str string) error {
//tokenClaims, err := jwt.ParseWithClaims(
// str,
// tk,
// func(token *jwt.Token) (interface{}, error) {
// return []byte(jwtConfig.Secret), nil
// })
//if err != nil {
// return err
//}
//if claim, ok := tokenClaims.Claims.(*UserToken); ok && tokenClaims.Valid {
// *tk = *claim
// return nil
//}
//return errors.New("token 解析失败")
return nil
}
// CheckUserInfo 如果UserToken有效 返回:true 否则返回false
func (tk *UserToken) CheckUserInfo() bool {
return !(tk.UserId > 100000000 || tk.UserId <= 0)
}
... ...
package tool
import (
"math/rand"
"time"
)
const (
KC_RAND_KIND_NUM = 0 // 纯数字
KC_RAND_KIND_LOWER = 1 // 小写字母
KC_RAND_KIND_UPPER = 2 // 大写字母
KC_RAND_KIND_ALL = 3 // 数字、大小写字母
)
// 随机字符串
func Krand(size int, kind int) string {
ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
is_all := kind > 2 || kind < 0
rand.Seed(time.Now().UnixNano())
for i := 0; i < size; i++ {
if is_all { // random ikind
ikind = rand.Intn(3)
}
scope, base := kinds[ikind][0], kinds[ikind][1]
result[i] = uint8(base + rand.Intn(scope))
}
return string(result)
}
... ...
package xcollection
type TreeNode interface {
PID() string
ID() string
}
type Tree struct {
Node TreeNode `json:"chart"`
Nodes []*Tree `json:"charts"`
}
func NewTree(nodes []TreeNode) *Tree {
var tree = &Tree{
Node: nil,
Nodes: make([]*Tree, 0),
}
for i := range nodes {
match := traverseAdd(tree, nodes[i])
if !match {
tree.Nodes = append(tree.Nodes, newTree(nodes[i]))
}
}
return tree
}
func newTree(node TreeNode) *Tree {
return &Tree{
Node: node,
Nodes: make([]*Tree, 0),
}
}
func (tree *Tree) Root() TreeNode {
if tree.Node != nil {
return tree.Node
}
if len(tree.Nodes) > 0 {
return tree.Nodes[0].Node
}
return nil
}
// TreeNodePaths returns all the parents of the current node 1->5->7 , use time n*O(n)(need performance optimization)
func (tree *Tree) TreeNodePaths(node TreeNode) []TreeNode {
treeNode := node
result := make([]TreeNode, 0)
for {
if treeNode == nil {
break
}
tmp := tree.find(treeNode, func(a, b TreeNode) bool {
if a.ID() == b.PID() {
return true
}
return false
})
result = append(result, treeNode)
if tmp == nil {
break
}
treeNode = tmp.Node
}
reserveResult := make([]TreeNode, 0)
for i := len(result) - 1; i >= 0; i-- {
reserveResult = append(reserveResult, result[i])
}
return reserveResult
}
// Add adds a node to the first matching parent tree if add success it return true
func (tree *Tree) Add(node TreeNode) bool {
return traverseAdd(tree, node)
}
// AllChildNode returns all child nodes under Node, including itself
func (tree *Tree) AllChildNode(node TreeNode) []TreeNode {
treeNode := tree.find(node, nil)
if treeNode == nil {
return []TreeNode{}
}
return tree.allChildNode(treeNode, nil)
}
// AllLeafNode returns all leaf node under Node ,if node is nil returns all leaf node under tree
func (tree *Tree) AllLeafNode(node TreeNode) []TreeNode {
treeNode := tree
if node != nil {
treeNode = tree.find(node, nil)
}
if treeNode == nil {
return []TreeNode{}
}
return tree.allChildNode(treeNode, func(node *Tree) bool {
if len(node.Nodes) == 0 {
return true
}
return false
})
}
// Depth returns all child nodes under depth depth=[1:n]
func (tree *Tree) Depth(depth int) []TreeNode {
treeNode := tree.find(tree.Root(), nil)
if treeNode == nil {
return []TreeNode{}
}
return tree.allChildByDepth(treeNode, depth)
}
// AllChildNodeByDepth returns all child nodes under depth Node
func (tree *Tree) AllChildNodeByDepth(node TreeNode, depth int) []TreeNode {
treeNode := tree.find(node, nil)
if treeNode == nil {
return []TreeNode{}
}
return tree.allChildByDepth(treeNode, depth)
}
// Find query the node in this tree
func (tree *Tree) Find(node TreeNode, compared func(a, b TreeNode) bool) *Tree {
return tree.find(node, compared)
}
// find query the node in this tree
func (tree *Tree) find(node TreeNode, compared func(a, b TreeNode) bool) *Tree {
var stack []*Tree
stack = append(stack, tree)
var find *Tree
for {
if len(stack) == 0 {
break
}
pop := stack[0]
stack = stack[1:]
stack = append(stack, pop.Nodes...)
if pop == nil || pop.Node == nil {
continue
}
if compared != nil {
if compared(pop.Node, node) {
find = pop
break
}
continue
}
if pop.Node.ID() == node.ID() {
find = pop
break
}
}
return find
}
// allChildNode 返回treeNode下所有子节点
func (tree *Tree) allChildNode(treeNode *Tree, filter func(node *Tree) bool) []TreeNode {
var stack []*Tree
stack = append(stack, treeNode)
var res []TreeNode
for {
if len(stack) == 0 {
break
}
pop := stack[0]
stack = stack[1:]
stack = append(stack, pop.Nodes...)
if filter != nil && !filter(pop) {
continue
}
res = append(res, pop.Node)
}
return res
}
// traverseAdd 递归添加
//
// tree 当前树
// node 判断的节点
func traverseAdd(tree *Tree, node TreeNode) bool {
list := tree.Nodes
var match bool = false
for i := range list {
id, pid := list[i].Node.ID(), node.PID()
if pid == id {
list[i].Nodes = append(list[i].Nodes, newTree(node))
return true
}
if match || traverseAdd(list[i], node) {
match = true
break
}
}
return match
}
// allChildByDepth 返回treeNode下指定深度的所有子节点 depth=[1:n]
func (tree *Tree) allChildByDepth(treeNode *Tree, depth int) []TreeNode {
var stack []*Tree
stack = append(stack, treeNode)
var res []TreeNode
if depth <= 0 {
return res
}
if treeNode.Root() != nil && depth == 1 {
return []TreeNode{treeNode.Root()}
}
curDepth := 1
var depthStack []*Tree
for {
if len(stack) == 0 {
break
}
pop := stack[0]
stack = stack[1:]
depthStack = append(depthStack, pop.Nodes...)
if len(stack) == 0 {
curDepth++
stack = depthStack[:]
depthStack = []*Tree{}
if curDepth == depth {
for i := range stack {
res = append(res, stack[i].Node)
}
break
}
}
}
return res
}
... ...
package xcollection
import (
"github.com/stretchr/testify/assert"
"strconv"
"testing"
)
func prepare() []struct {
Input []TreeNode
Text string
Except []string
Except2 []string
} {
return []struct {
Input []TreeNode
Text string
Except []string
Except2 []string
}{
{
Input: []TreeNode{
&st{Id: 1, Pid: 0},
&st{Id: 2, Pid: 1}, &st{Id: 3, Pid: 1}, &st{Id: 4, Pid: 1},
&st{Id: 5, Pid: 3},
&st{Id: 6, Pid: 5}, &st{Id: 7, Pid: 5}},
Text: `
树形结构:
1
2 3 4
5
6 7
`,
Except: []string{"5", "6", "7"},
Except2: []string{"2", "4", "6", "7"},
},
}
}
func Test_Tree(t *testing.T) {
table := prepare()
for i := range table {
tree := NewTree(table[i].Input)
out := tree.AllChildNode(&st{Id: 5, Pid: 3})
var res []string = treeNodeResults(out)
assert.Equal(t, res, table[i].Except)
out = tree.AllLeafNode(nil) //tree.Root()
res = treeNodeResults(out)
assert.Equal(t, res, table[i].Except2)
root := tree.Root()
assert.Equal(t, root.ID(), "1")
//tree.Add(&st{Id:10,Pid: 7})
//
//out = tree.AllLeafNode(tree.Root())
//res = treeNodeResults(out)
//assert.Equal(t, res, []string{"2", "4", "6", "10"})
out = tree.TreeNodePaths(&st{Id: 7, Pid: 5})
res = treeNodeResults(out)
assert.Equal(t, res, []string{"1", "3", "5", "7"})
}
}
func Test_TreeNodeByDepth(t *testing.T) {
input := []TreeNode{
&st{Id: 1, Pid: 0},
&st{Id: 2, Pid: 1}, &st{Id: 3, Pid: 1}, &st{Id: 4, Pid: 1},
&st{Id: 5, Pid: 3},
&st{Id: 6, Pid: 5}, &st{Id: 7, Pid: 5},
&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},
}
tree := NewTree(input)
/*
树形结构:
1
2 3 4
5
6 7
8 9 10 11 12
*/
var out []TreeNode
var res []string
out = tree.AllChildNodeByDepth(&st{Id: 5, Pid: 3}, 2)
res = treeNodeResults(out)
assert.Equal(t, []string{"6", "7"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 1)
res = treeNodeResults(out)
assert.Equal(t, []string{"1"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 2)
res = treeNodeResults(out)
assert.Equal(t, []string{"2", "3", "4"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 3)
res = treeNodeResults(out)
assert.Equal(t, []string{"5"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 4)
res = treeNodeResults(out)
assert.Equal(t, []string{"6", "7"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 5)
res = treeNodeResults(out)
assert.Equal(t, []string{"8", "9", "10", "11", "12"}, res)
}
type st struct {
Id int
Pid int
}
func (t *st) PID() string {
return strconv.Itoa(t.Pid)
}
func (t *st) ID() string {
return strconv.Itoa(t.Id)
}
func treeNodeResults(nodes []TreeNode) []string {
var res []string
for i := range nodes {
res = append(res, nodes[i].ID())
}
return res
}
... ...
package xerr
// 成功返回
const OK uint32 = 200
/**(前3位代表业务,后三位代表具体功能)**/
// 全局错误码
const SERVER_COMMON_ERROR uint32 = 100001
const REUQEST_PARAM_ERROR uint32 = 100002
const TOKEN_EXPIRE_ERROR uint32 = 100003
const TOKEN_GENERATE_ERROR uint32 = 100004
const DB_ERROR uint32 = 100005
const DB_UPDATE_AFFECTED_ZERO_ERROR uint32 = 100006
const REQUEST_ARGS_ERROR = 200001
// 微信模块
const ErrWxMiniAuthFailError uint32 = 500001
const ErrUserNoAuth uint32 = 500002
... ...
package xerr
var message map[uint32]string
func init() {
message = make(map[uint32]string)
message[OK] = "SUCCESS"
message[SERVER_COMMON_ERROR] = "服务器开小差啦,稍后再来试一试"
message[REUQEST_PARAM_ERROR] = "参数错误"
message[TOKEN_EXPIRE_ERROR] = "token失效,请重新登陆"
message[TOKEN_GENERATE_ERROR] = "生成token失败"
message[DB_ERROR] = "数据库繁忙,请稍后再试"
message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0"
message[ErrUserNoAuth] = "无权限"
message[ErrWxMiniAuthFailError] = "微信授权失败"
}
func MapErrMsg(errcode uint32) string {
if msg, ok := message[errcode]; ok {
return msg
} else {
return "服务器开小差啦,稍后再来试一试"
}
}
func IsCodeErr(errcode uint32) bool {
if _, ok := message[errcode]; ok {
return true
} else {
return false
}
}
... ...
package xerr
import (
"fmt"
)
/**
常用通用固定错误
*/
type CodeError struct {
errCode uint32
errMsg string
InternalError error
}
// GetErrCode 返回给前端的错误码
func (e *CodeError) GetErrCode() uint32 {
return e.errCode
}
// GetErrMsg 返回给前端显示端错误信息
func (e *CodeError) GetErrMsg() string {
return e.errMsg
}
func (e *CodeError) Error() string {
if e.InternalError != nil {
return fmt.Sprintf("ErrCode:%d,ErrMsg:%s InternalError:%s", e.errCode, e.errMsg, e.InternalError.Error())
}
return fmt.Sprintf("ErrCode:%d,ErrMsg:%s", e.errCode, e.errMsg)
}
/*
指定错误码的错误
*/
func NewCodeErr(errCode uint32, err error) *CodeError {
return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode), InternalError: err}
}
func NewCodeErrMsg(errCode uint32, err error, msg string) *CodeError {
return &CodeError{errCode: errCode, errMsg: msg, InternalError: err}
}
/*
默认的服务错误
*/
func NewErr(err error) *CodeError {
return &CodeError{errCode: SERVER_COMMON_ERROR, InternalError: err}
}
func NewErrMsg(errMsg string) *CodeError {
return &CodeError{errCode: SERVER_COMMON_ERROR, errMsg: errMsg}
}
func NewErrMsgErr(errMsg string, err error) *CodeError {
return &CodeError{errCode: SERVER_COMMON_ERROR, errMsg: errMsg, InternalError: err}
}
... ...