作者 yangfu

项目初始化

正在显示 75 个修改的文件 包含 3948 行增加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
.gitkeep
app.log
go.sum
lastupdate.tmp
*.log
public/*
logs/
cmd/discuss/api/etc/core.local.yaml
... ...
.PHONY: bsi-model
bsi-model:
goctl model mysql ddl -s .\cmd\bsi\deploy\database\table.sql -d cmd/bsi
.PHONY: bsi-api
bsi-api:
goctl api go -api .\cmd\bsi\api\dsl\core.api -dir cmd/bsi/api -style go_zero
.PHONY: bsi-swagger
bsi-swagger:
goctl api plugin -plugin goctl-swagger="swagger -filename core.json" -api .\cmd\bsi\api\dsl\core.api -dir .\cmd\discuss\api\dsl
.PHONY: bsi-build
bsi-build:
docker build -f cmd/bsi/deploy/docker/Dockerfile -t sumifcc/bsi:1.0.0 .
... ...
package main
import (
"flag"
"fmt"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/interanl/pkg/db"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/interanl/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/xerr"
"net/http"
"strings"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/config"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/handler"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
"github.com/golang-jwt/jwt/v4/request"
"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)
// 系统设置
systemSetup(c)
// 服务初始化
opts := make([]rest.RunOption, 0)
opts = append(opts, rest.WithCustomCors(func(header http.Header) {
header.Set("Access-Control-Allow-Headers", "*")
}, func(writer http.ResponseWriter) {
}))
opts = append(opts, rest.WithUnauthorizedCallback(func(w http.ResponseWriter, r *http.Request, err error) {
if err != nil {
logx.Debugf("unauthorized: %s \n", err.Error())
}
}))
server := rest.MustNewServer(c.RestConf, opts...)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
// 数据迁移
if c.Migrate {
db.Migrate(ctx.DB)
}
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}
func systemSetup(c config.Config) {
// 初始化Domain里面的配置
domain.ProjectName = c.Name
// 默认的token头 Authorization 修改为 x-mmm-accesstoken
request.AuthorizationHeaderExtractor = &request.PostExtractionFilter{
Extractor: request.HeaderExtractor{"x-mmm-accesstoken"},
Filter: 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
},
}
// 系统错误应答包装
httpx.SetErrorHandlerCtx(xerr.ErrorHandlerCtx)
// 系统成功应答包装
httpx.SetOkHandler(xerr.OkHandlerCtx)
}
... ...
syntax = "v1"
import "core/common.api"
import "core/screen.api"
\ No newline at end of file
... ...
// 通用接口
@server(
prefix: v1
group: common
)
service Core {
@doc "日志查询"
@handler commonGetLog
get /log/:module
}
// 通用接口
@server(
prefix: v1
middleware: LogRequest
group: common
)
service Core {
@doc "短信验证码"
@handler commonSmsCode
post /common/sms/code (CommonSmsCodeRequest) returns (CommonSmsCodeResposne)
@doc "微信二维码"
@handler miniQrcodeInvite
post /mini/qrcode (MiniQrCodeRequest)
@doc "清理缓存"
@handler commonGetClearCache
get /clear
}
// 短信验证码
type(
CommonSmsCodeRequest{
Phone string `json:"phone"`
}
CommonSmsCodeResposne{
}
)
type(
MiniQrCodeRequest{
Page string `json:"page"` // 微信页面入口
Scene string `json:"scene"` // 参数
}
)
\ No newline at end of file
... ...
syntax = "v1"
info(
title: "易数家显示大屏互动服务"
desc: "易数家显示大屏互动服务 BSI(Big screen interaction)"
author: "bsi"
email: "bsi@gmail.com"
version: "v1"
)
// H5接口
@server(
prefix: v1/h5
group: todo
middleware: LogRequest
//jwt: MiniAuth
)
service Core {
@doc ""
@handler H5Todo
get /todo
@doc ""
@handler H5TodoErrorCommon
get /todo/err1 returns(TodoResonse)
@doc ""
@handler H5TodoErrorInternal
get /todo/err2 returns(TodoResonse)
@doc ""
@handler H5TodoErrorGrpc
get /todo/err3 returns(TodoResonse)
}
type (
TodoRequest struct{}
TodoResonse struct{}
)
\ No newline at end of file
... ...
Name: Core
Host: 0.0.0.0
Port: 8080
Verbose: false
Migrate: true
Timeout: 30000
LogRequest: true # 记录详细请求日志
ContentSecurityCheck: true # 内容安全检查(调用微信接口)
Log:
#Mode: file
Encoding: plain
Level: debug # info
MaxSize: 1 # 2MB
TimeFormat: 2006-01-02 15:04:05
Rotation: size
MaxContentLength: 10240
SystemAuth:
AccessSecret: digital-platform
AccessExpire: 360000
MiniAuth:
AccessSecret: discuss-secret
AccessExpire: 360000
Redis:
Host: 127.0.0.1:6379
Type: node
Pass:
DB:
DataSource: host=114.55.200.59 user=postgres password=eagle1010 dbname=sumifcc-discuss-dev port=31543 sslmode=disable TimeZone=Asia/Shanghai
ApiAuth:
Name: ApiAuth
Host: http://digital-platform-dev.fjmaimaimai.com
Timeout: 0s
Wechat:
AppID: wxae5b305849343ec8
AppSecret: f584adb68f7d784425b60e1ebb2ffd4b
QrcodeEnv: trial
\ No newline at end of file
... ...
package config
import (
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/config"
"time"
)
type Config struct {
rest.RestConf
config.Config
Redis redis.RedisConf `json:",optional"`
SystemAuth config.Auth
MiniAuth config.Auth
Migrate bool `json:",optional,default=true"`
ApiAuth ApiService
DebugSmsCode string `json:",optional,default=999512"`
LogRequest bool `json:",optional,default=true"`
ContentSecurityCheck bool `json:",optional,default=false"`
}
type ApiService struct {
Name string
Host string
Timeout time.Duration
}
... ...
package common
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/common"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
)
func CommonGetClearCacheHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewCommonGetClearCacheLogic(r.Context(), svcCtx)
err := l.CommonGetClearCache()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}
... ...
package common
import (
"net/http"
"path/filepath"
"strings"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
)
func CommonGetLogHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req struct {
Module string `path:"module"`
}
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
path := svcCtx.Config.Log.Path
if svcCtx.Config.Log.Mode != "file" {
return
}
if path == "" {
path = "logs"
}
if !strings.HasSuffix(req.Module, ".log") {
req.Module += ".log"
}
handler := http.FileServer(http.Dir(path))
r.URL.Path = filepath.Join(req.Module)
handler.ServeHTTP(w, r)
}
}
... ...
package common
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/common"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types"
)
func CommonSmsCodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CommonSmsCodeRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := common.NewCommonSmsCodeLogic(r.Context(), svcCtx)
resp, err := l.CommonSmsCode(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
... ...
package common
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/common"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types"
)
func MiniQrcodeInviteHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.MiniQrCodeRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := common.NewMiniQrcodeInviteLogic(r.Context(), svcCtx)
err := l.MiniQrcodeInvite(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}
... ...
// Code generated by goctl. DO NOT EDIT.
package handler
import (
"net/http"
common "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/handler/common"
todo "gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/handler/todo"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/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: "/log/:module",
Handler: common.CommonGetLogHandler(serverCtx),
},
},
rest.WithPrefix("/v1"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.LogRequest},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/common/sms/code",
Handler: common.CommonSmsCodeHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/mini/qrcode",
Handler: common.MiniQrcodeInviteHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/clear",
Handler: common.CommonGetClearCacheHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/v1"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.LogRequest},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/todo",
Handler: todo.H5TodoHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/todo/err1",
Handler: todo.H5TodoErrorCommonHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/todo/err2",
Handler: todo.H5TodoErrorInternalHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/todo/err3",
Handler: todo.H5TodoErrorGrpcHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/v1/h5"),
)
}
... ...
package todo
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/todo"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
)
func H5TodoErrorCommonHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := todo.NewH5TodoErrorCommonLogic(r.Context(), svcCtx)
resp, err := l.H5TodoErrorCommon()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
... ...
package todo
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/todo"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
)
func H5TodoErrorGrpcHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := todo.NewH5TodoErrorGrpcLogic(r.Context(), svcCtx)
resp, err := l.H5TodoErrorGrpc()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
... ...
package todo
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/todo"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
)
func H5TodoErrorInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := todo.NewH5TodoErrorInternalLogic(r.Context(), svcCtx)
resp, err := l.H5TodoErrorInternal()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
... ...
package todo
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/logic/todo"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
)
func H5TodoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := todo.NewH5TodoLogic(r.Context(), svcCtx)
resp, err := l.H5Todo()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJson(w, resp)
}
}
}
... ...
package common
import (
"context"
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/xerr"
"strings"
"github.com/zeromicro/go-zero/core/logx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
)
type CommonGetClearCacheLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCommonGetClearCacheLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommonGetClearCacheLogic {
return &CommonGetClearCacheLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CommonGetClearCacheLogic) CommonGetClearCache() error {
var (
appName = l.svcCtx.Config.Name
success int
)
if strings.TrimSpace(appName) == "" {
return nil
}
keyPattern := fmt.Sprintf("%s*", appName)
list, err := l.svcCtx.Redis.Keys(keyPattern)
if err != nil {
return xerr.NewErrMsg(err.Error())
}
for _, key := range list {
if _, err = l.svcCtx.Redis.Del(key); err == nil {
success++
}
}
logx.Infof("清理缓存:%d/%d", success, len(list))
return nil
}
... ...
package common
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
)
type CommonGetLogLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCommonGetLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommonGetLogLogic {
return &CommonGetLogLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CommonGetLogLogic) CommonGetLog() error {
// todo: add your logic here and delete this line
return nil
}
... ...
package common
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type CommonSmsCodeLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCommonSmsCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommonSmsCodeLogic {
return &CommonSmsCodeLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CommonSmsCodeLogic) CommonSmsCode(req *types.CommonSmsCodeRequest) (resp *types.CommonSmsCodeResposne, err error) {
// todo: add your logic here and delete this line
return
}
... ...
package common
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type MiniQrcodeInviteLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewMiniQrcodeInviteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MiniQrcodeInviteLogic {
return &MiniQrcodeInviteLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *MiniQrcodeInviteLogic) MiniQrcodeInvite(req *types.MiniQrCodeRequest) error {
// todo: add your logic here and delete this line
return nil
}
... ...
package todo
import (
"context"
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/xerr"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type H5TodoErrorCommonLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewH5TodoErrorCommonLogic(ctx context.Context, svcCtx *svc.ServiceContext) *H5TodoErrorCommonLogic {
return &H5TodoErrorCommonLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *H5TodoErrorCommonLogic) H5TodoErrorCommon() (resp *types.TodoResonse, err error) {
// todo: add your logic here and delete this line
err = xerr.NewCodeErr(xerr.DbError, fmt.Errorf("no result"))
return
}
... ...
package todo
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type H5TodoErrorGrpcLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewH5TodoErrorGrpcLogic(ctx context.Context, svcCtx *svc.ServiceContext) *H5TodoErrorGrpcLogic {
return &H5TodoErrorGrpcLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *H5TodoErrorGrpcLogic) H5TodoErrorGrpc() (resp *types.TodoResonse, err error) {
// todo: add your logic here and delete this line
err = status.Error(codes.DeadlineExceeded, "Grpc deadline...")
return
}
... ...
package todo
import (
"context"
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/xerr"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type H5TodoErrorInternalLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewH5TodoErrorInternalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *H5TodoErrorInternalLogic {
return &H5TodoErrorInternalLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *H5TodoErrorInternalLogic) H5TodoErrorInternal() (resp *types.TodoResonse, err error) {
// todo: add your logic here and delete this line
err = xerr.NewErrMsgErr("busy", fmt.Errorf("internal error"))
return
}
... ...
package todo
import (
"context"
"fmt"
"github.com/zeromicro/go-zero/core/logx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/svc"
)
type H5TodoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewH5TodoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *H5TodoLogic {
return &H5TodoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *H5TodoLogic) H5Todo() (interface{}, error) {
// todo: add your logic here and delete this line
return "todo", fmt.Errorf("todo error")
}
... ...
package middleware
import "net/http"
type LogRequestMiddleware struct {
}
func NewLogRequestMiddleware() *LogRequestMiddleware {
return &LogRequestMiddleware{}
}
func (m *LogRequestMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// TODO generate middleware implement function, delete after code implementation
// Passthrough to next handler if need
next(w, r)
}
}
... ...
package svc
import (
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/api/internal/config"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/database"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway/authlib"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway/openlib"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway/smslib"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/middleware"
"gorm.io/gorm"
)
type ServiceContext struct {
Config config.Config
DB *gorm.DB
Redis *redis.Redis
LogRequest rest.Middleware
LoginStatusCheck rest.Middleware
ApiAuthService authlib.ApiAuthService
SmsService smslib.SMSService
OpenApiService openlib.OpenApiService
}
func NewServiceContext(c config.Config) *ServiceContext {
db := database.OpenGormPGDB(c.DB.DataSource, c.Log.Mode)
//mlCache := cache.NewMultiLevelCache([]string{c.Redis.Host}, c.Redis.Pass)
redis, _ := redis.NewRedis(redis.RedisConf{Host: c.Redis.Host, Pass: c.Redis.Pass, Type: "node"})
apiAuth := authlib.ApiAuthService{
Service: gateway.NewService(c.ApiAuth.Name, c.ApiAuth.Host, c.ApiAuth.Timeout),
}
return &ServiceContext{
Config: c,
DB: db,
Redis: redis,
ApiAuthService: apiAuth,
LogRequest: middleware.NewLogRequestMiddleware(c.LogRequest).Handle,
}
}
... ...
// Code generated by goctl. DO NOT EDIT.
package types
type CommonSmsCodeRequest struct {
Phone string `json:"phone"`
}
type CommonSmsCodeResposne struct {
}
type MiniQrCodeRequest struct {
Page string `json:"page"` // 微信页面入口
Scene string `json:"scene"` // 参数
}
type TodoRequest struct {
}
type TodoResonse struct {
}
... ...
CREATE TABLE `todo`
(
`id` int(0) NOT NULL COMMENT '唯一标识',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
\ No newline at end of file
... ...
FROM golang:1.19-alpine as builder
# Define the project name | 定义项目名称
ARG PROJECT=core
ARG PROJECTCODE=bsi
WORKDIR /build
COPY . .
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk update --no-cache && apk add --no-cache tzdata
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/${PROJECTCODE}/api \
&& go build -ldflags="-s -w" -o /build/api/${PROJECT} ${PROJECT}.go
FROM alpine:latest
# Define the project name | 定义项目名称
ARG PROJECT=core
ARG PROJECTCODE=bsi
# 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}
ENV TZ Asia/Shanghai
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
COPY --from=builder /build/api/${PROJECT} ./
COPY --from=builder /build/cmd/${PROJECTCODE}/api/etc/${CONFIG_FILE} ./etc/
EXPOSE 8080
ENTRYPOINT ./${PROJECT} -f etc/${CONFIG_FILE}
\ No newline at end of file
... ...
#!/bin/bash
export PATH=/root/local/bin:$PATH
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
if [ "$?" == "1" ];then
kubectl create -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml --record
kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss
if [ "$?" == "0" ];then
echo "sumifcc-discuss service install success!"
else
echo "sumifcc-discuss service install fail!"
fi
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
if [ "$?" == "0" ];then
echo "sumifcc-discuss deployment install success!"
else
echo "sumifcc-discuss deployment install fail!"
fi
else
kubectl delete -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml
kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss
while [ "$?" == "0" ]
do
kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss
done
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
while [ "$?" == "0" ]
do
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
done
kubectl create -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml --record
kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss
if [ "$?" == "0" ];then
echo "sumifcc-discuss service update success!"
else
echo "sumifcc-discuss service update fail!"
fi
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
if [ "$?" == "0" ];then
echo "sumifcc-discuss deployment update success!"
else
echo "sumifcc-discuss deployment update fail!"
fi
fi
\ No newline at end of file
... ...
#!/bin/bash
export PATH=/root/local/bin:$PATH
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
if [ "$?" == "1" ];then
kubectl create -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml --record
kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss
if [ "$?" == "0" ];then
echo "sumifcc-discuss service install success!"
else
echo "sumifcc-discuss service install fail!"
fi
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
if [ "$?" == "0" ];then
echo "sumifcc-discuss deployment install success!"
else
echo "sumifcc-discuss deployment install fail!"
fi
else
kubectl delete -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml
kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss
while [ "$?" == "0" ]
do
kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss
done
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
while [ "$?" == "0" ]
do
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
done
kubectl create -f /tmp/test/sumifcc-discuss/sumifcc-discuss.yaml --record
kubectl -n mmm-suplus-test get svc | grep -q sumifcc-discuss
if [ "$?" == "0" ];then
echo "sumifcc-discuss service update success!"
else
echo "sumifcc-discuss service update fail!"
fi
kubectl -n mmm-suplus-test get pods | grep -q sumifcc-discuss
if [ "$?" == "0" ];then
echo "sumifcc-discuss deployment update success!"
else
echo "sumifcc-discuss deployment update fail!"
fi
fi
\ No newline at end of file
... ...
apiVersion: v1
kind: Service
metadata:
name: sumifcc-discuss
namespace: mmm-suplus-test
labels:
k8s-app: sumifcc-discuss
spec:
ports:
- name: "http"
port: 80
targetPort: 8081
- name: "https"
port: 443
targetPort: 443
selector:
k8s-app: sumifcc-discuss
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: sumifcc-discuss
namespace: mmm-suplus-test
labels:
k8s-app: sumifcc-discuss
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: sumifcc-discuss
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference: {}
weight: 100
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cn-hangzhou.i-bp1djh1xn7taumbue1ze
containers:
- name: sumifcc-discuss
image: 192.168.0.243:5000/mmm/sumifcc-discuss:dev
imagePullPolicy: Always
ports:
- containerPort: 8081
- containerPort: 443
volumeMounts:
- mountPath: /opt/logs
name: accesslogs
env:
- name: LOG_LEVEL
value: "debug"
- name: LOG_FILE
value: "true"
- name: REDIS_HOST
valueFrom:
configMapKeyRef:
name: suplus-config
key: redis.ip
- name: REDIS_PORT
valueFrom:
configMapKeyRef:
name: suplus-config
key: redis.port
volumes:
- name: accesslogs
emptyDir: {}
... ...
syntax = "v1"
info(
title: "xx实例"
desc: "xx实例"
author: "author"
email: "email"
version: "v1"
)
@server(
prefix: todo/v1
group: todo
jwt: JwtAuth
)
service Core {
@doc "详情"
@handler todoGet
get /todo/:id (TodoGetRequest) returns (TodoGetResponse)
@doc "保存"
@handler todoSave
post /todo (TodoSaveRequest) returns (TodoSaveResponse)
@doc "删除"
@handler todoDelete
delete /todo/:id (TodoDeleteRequest) returns (TodoDeleteResponse)
@doc "更新"
@handler todoUpdate
put /todo/:id (TodoUpdateRequest) returns (TodoUpdateResponse)
@doc "搜索"
@handler todoSearch
post /todo/search (TodoSearchRequest) returns (TodoSearchResponse)
}
type (
TodoGetRequest {
Id int64 `path:"id"`
}
TodoGetResponse struct{
Todo TodoItem `json:"todo"`
}
TodoSaveRequest struct{
Todo TodoItem `json:"todo"`
}
TodoSaveResponse struct{}
TodoDeleteRequest struct{
Id int64 `path:"id"`
}
TodoDeleteResponse struct{}
TodoUpdateRequest struct{
Id int64 `path:"id"`
Todo TodoItem `json:"todo"`
}
TodoUpdateResponse struct{}
TodoSearchRequest struct{
Page int `json:"page"`
Size int `json:"size"`
}
TodoSearchResponse{
List []TodoItem `json:"list"`
Total int64 `json:"total"`
}
TodoItem struct{
}
)
// logic CRUD
// Save
//var (
// conn = l.svcCtx.DefaultDBConn()
// dm *domain.Todo
//)
//// 唯一判断
//dm = NewDomainTodo(req.Todo)
//if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
// dm, err = l.svcCtx.TodoRepository.Insert(l.ctx, conn, dm)
// return err
//}, true); err != nil {
// return nil, xerr.NewErrMsg("保存失败")
//}
////resp = &types.TodoSaveResponse{}
//return
//func NewDomainTodo(item types.TodoItem) *domain.Todo {
// return &domain.Todo{
// }
//}
//
//func NewTypesTodo(item *domain.Todo) types.TodoItem {
// return types.TodoItem{
// Id: item.Id,
// }
//}
// Get
//var (
// conn = l.svcCtx.DefaultDBConn()
// dm *domain.Todo
//)
//// 货号唯一
//if dm, err = l.svcCtx.TodoRepository.FindOne(l.ctx, conn, req.Id); err != nil {
// return nil, xerr.NewErrMsgErr("不存在", err)
//}
//resp = &types.TodoGetResponse{
// Todo: NewTypesTodo(dm),
//}
//return
// Delete
//var (
// conn = l.svcCtx.DefaultDBConn()
// dm *domain.Todo
//)
//if dm, err = l.svcCtx.TodoRepository.FindOne(l.ctx, conn, req.Id); err != nil {
// return nil, xerr.NewErrMsgErr("不存在", err)
//}
//if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
// if dm, err = l.svcCtx.TodoRepository.Delete(l.ctx, conn, dm); err != nil {
// return err
// }
// return nil
//}, true); err != nil {
// return nil, xerr.NewErrMsgErr("移除失败", err)
//}
//return
// Search
//var (
// conn = l.svcCtx.DefaultDBConn()
// dms []*domain.Todo
// total int64
//)
//
//queryOptions := domain.NewQueryOptions().WithOffsetLimit(req.Page, req.Size).
// WithKV("", "")
//total, dms, err = l.svcCtx.TodoRepository.Find(l.ctx, conn, queryOptions)
//list := make([]types.TodoItem, 0)
//for i := range dms {
// list = append(list, NewTypesTodo(dms[i]))
//}
//resp = &types.TodoSearchResponse{
// List: list,
// Total: total,
//}
//return
// Update
//var (
// conn = l.svcCtx.DefaultDBConn()
// dm *domain.Todo
//)
//if dm, err = l.svcCtx.TodoRepository.FindOne(l.ctx, conn, req.Id); err != nil {
// return nil, xerr.NewErrMsgErr("不存在", err)
//}
//// 不可编辑判断
//// 赋值
//// 更新
//if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
// dm, err = l.svcCtx.TodoRepository.UpdateWithVersion(l.ctx, conn, dm)
// return err
//}, true); err != nil {
// return nil, xerr.NewErrMsg("更新失败")
//}
//resp = &types.TodoUpdateResponse{}
//return
... ...
syntax = "proto3";
option go_package ="./pb";
package pb;
message TodoGetReq {
int64 Id = 1;
}
message TodoGetResp{
TodoItem User = 1;
}
message TodoSaveReq {
}
message TodoSaveResp{
}
message TodoDeleteReq {
int64 Id = 1;
}
message TodoDeleteResp{
}
message TodoUpdateReq {
int64 Id = 1;
}
message TodoUpdateResp{
}
message TodoSearchReq {
int64 PageNumber = 1;
int64 PageSize = 2;
}
message TodoSearchResp{
repeated TodoItem List =1;
int64 Total =2;
}
message TodoItem {
}
service TodoService {
rpc TodoGet(TodoGetReq) returns(TodoGetResp);
rpc TodoSave(TodoSaveReq) returns(TodoSaveResp);
rpc TodoDelete(TodoDeleteReq) returns(TodoDeleteResp);
rpc TodoUpdate(TodoUpdateReq) returns(TodoUpdateResp);
rpc TodoSearch(TodoSearchReq) returns(TodoSearchResp);
}
... ...
package db
import (
"gorm.io/gorm"
)
func Migrate(db *gorm.DB) {
db.AutoMigrate()
}
... ...
package models
import (
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/interanl/pkg/domain"
"gorm.io/gorm"
"gorm.io/plugin/soft_delete"
)
type Todo struct {
Id int64 // 唯一标识
CreatedAt int64
UpdatedAt int64
IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"`
DeletedAt int64
Version int
}
func (m *Todo) TableName() string {
return "todo"
}
func (m *Todo) BeforeCreate(tx *gorm.DB) (err error) {
// m.CreatedAt = time.Now().Unix()
// m.UpdatedAt = time.Now().Unix()
return
}
func (m *Todo) BeforeUpdate(tx *gorm.DB) (err error) {
// m.UpdatedAt = time.Now().Unix()
return
}
func (m *Todo) CacheKeyFunc() string {
if m.Id == 0 {
return ""
}
return fmt.Sprintf("%v:cache:%v:id:%v", domain.ProjectName, m.TableName(), m.Id)
}
func (m *Todo) CacheKeyFuncByObject(obj interface{}) string {
if v, ok := obj.(*Todo); ok {
return v.CacheKeyFunc()
}
return ""
}
func (m *Todo) 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/cmd/bsi/interanl/pkg/db/models"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/cmd/bsi/interanl/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/transaction"
"gorm.io/gorm"
)
type TodoRepository struct {
*cache.CachedRepository
}
func (repository *TodoRepository) Insert(ctx context.Context, conn transaction.Conn, dm *domain.Todo) (*domain.Todo, error) {
var (
err error
m = &models.Todo{}
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 *TodoRepository) Update(ctx context.Context, conn transaction.Conn, dm *domain.Todo) (*domain.Todo, error) {
var (
err error
m *models.Todo
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 *TodoRepository) UpdateWithVersion(ctx context.Context, transaction transaction.Conn, dm *domain.Todo) (*domain.Todo, error) {
var (
err error
m *models.Todo
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 *TodoRepository) Delete(ctx context.Context, conn transaction.Conn, dm *domain.Todo) (*domain.Todo, error) {
var (
tx = conn.DB()
m = &models.Todo{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 *TodoRepository) FindOne(ctx context.Context, conn transaction.Conn, id int64) (*domain.Todo, error) {
var (
err error
tx = conn.DB()
m = new(models.Todo)
)
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.Todo)
cacheModel.Id = id
if err = repository.QueryCache(cacheModel.CacheKeyFunc, m, queryFunc); err != nil {
return nil, err
}
return repository.ModelToDomainModel(m)
}
func (repository *TodoRepository) Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*domain.Todo, error) {
var (
tx = conn.DB()
ms []*models.Todo
dms = make([]*domain.Todo, 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 *TodoRepository) ModelToDomainModel(from *models.Todo) (*domain.Todo, error) {
to := &domain.Todo{}
err := copier.Copy(to, from)
return to, err
}
func (repository *TodoRepository) DomainModelToModel(from *domain.Todo) (*models.Todo, error) {
to := &models.Todo{}
err := copier.Copy(to, from)
return to, err
}
func NewTodoRepository(cache *cache.CachedRepository) domain.TodoRepository {
return &TodoRepository{CachedRepository: cache}
}
... ...
package domain
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/transaction"
"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) MustWithKV(key string, value interface{}) QueryOptions {
options[key] = value
return options
}
func (options QueryOptions) Copy() QueryOptions {
newOptions := NewQueryOptions()
for k, v := range options {
newOptions[k] = v
}
return newOptions
}
type IndexQueryOptionFunc func() QueryOptions
// 自定义的一些查询条件
func (options QueryOptions) WithCountOnly() QueryOptions {
options["countOnly"] = true
return options
}
func (options QueryOptions) WithFindOnly() QueryOptions {
options["findOnly"] = true
return options
}
func LazyLoad[K comparable, T any](source map[K]T, ctx context.Context, conn transaction.Conn, k K, load func(context.Context, transaction.Conn, K) (T, error)) (T, error) {
if v, ok := source[k]; ok {
return v, nil
}
if v, err := load(ctx, conn, k); err != nil {
return v, err
} else {
source[k] = v
return v, nil
}
}
func Values[T any, V any](list []T, each func(item T) V) []V {
var result []V
for _, item := range list {
value := each(item)
result = append(result, value)
}
return result
}
... ...
package domain
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/transaction"
)
type Todo struct {
Id int64 // 唯一标识
CreatedAt int64
UpdatedAt int64
DeletedAt int64
Version int
}
type TodoRepository interface {
Insert(ctx context.Context, conn transaction.Conn, dm *Todo) (*Todo, error)
Update(ctx context.Context, conn transaction.Conn, dm *Todo) (*Todo, error)
UpdateWithVersion(ctx context.Context, conn transaction.Conn, dm *Todo) (*Todo, error)
Delete(ctx context.Context, conn transaction.Conn, dm *Todo) (*Todo, error)
FindOne(ctx context.Context, conn transaction.Conn, id int64) (*Todo, error)
Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*Todo, error)
}
func (m *Todo) Identify() interface{} {
if m.Id == 0 {
return nil
}
return m.Id
}
... ...
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 = "bsi"
... ...
module gitlab.fjmaimaimai.com/allied-creation/sumifcc
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/mozillazg/go-pinyin v0.20.0
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/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/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-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/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // 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/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/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/onsi/gomega v1.26.0 // 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/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.4.1 // 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.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/automaxprocs v1.5.3 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // 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/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
... ...
package cache
import (
"github.com/tiptok/gocomm/pkg/cache"
"github.com/tiptok/gocomm/pkg/cache/gzcache"
"github.com/tiptok/gocomm/pkg/log"
"github.com/zeromicro/go-zero/core/logx"
)
func NewMultiLevelCache(hosts []string, password string) *cache.MultiLevelCache {
logx.Infof("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"
)
type Auth struct {
AccessSecret string
AccessExpire int64
}
type Config struct {
DB struct {
DataSource string `json:",env=DataSource"`
} `json:",optional"`
Cache cache.CacheConf `json:",optional"`
DTM DTM `json:",optional"`
Sms Sms `json:",optional"`
Oss Oss `json:",optional"`
Wechat 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
QrcodeEnv string `json:",optional,default=release"`
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"
"encoding/json"
"github.com/golang-jwt/jwt/v4"
"github.com/zeromicro/go-zero/core/logx"
"time"
)
var (
CtxKeyJwtUserId = "userId"
CtxKeyJwtCompanyId = "companyId"
)
func GetInt64FromCtx(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: GetInt64FromCtx(ctx, CtxKeyJwtUserId),
CompanyId: GetInt64FromCtx(ctx, CtxKeyJwtCompanyId),
}
}
type UserToken struct {
UserId int64
CompanyId int64
}
func (tk UserToken) GenerateToken(secret string, expire int64) (string, error) {
claims := make(jwt.MapClaims)
claims["exp"] = time.Now().Unix() + expire
claims["iat"] = time.Now().Unix()
claims["UserId"] = tk.UserId
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = claims
return token.SignedString([]byte(secret))
}
... ...
package database
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
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, logMode string) *gorm.DB {
logx.Infof("starting db...")
db, err := gorm.Open(postgres.New(postgres.Config{
DSN: source,
PreferSimpleProtocol: true, // disables implicit prepared statement usage
}), &gorm.Config{
Logger: NewLogger(logMode), //newLogger,
})
if err != nil {
panic(err)
}
return db
}
func NewLogger(logType string) logger.Interface {
if logType == "console" {
return 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
},
)
}
return ZeroLog{}
}
type ZeroLog struct {
}
func (l ZeroLog) LogMode(logger.LogLevel) logger.Interface {
return l
}
func (l ZeroLog) Info(ctx context.Context, s string, values ...interface{}) {
logx.Infof(s, values...)
}
func (l ZeroLog) Warn(ctx context.Context, s string, values ...interface{}) {
logx.Errorf(s, values...)
}
func (l ZeroLog) Error(ctx context.Context, s string, values ...interface{}) {
logx.Errorf(s, values...)
}
func (l ZeroLog) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
now := time.Now()
sql, rows := fc()
logx.Infof("[%v] [rows:%v] %s", now.Sub(begin).String(), rows, sql)
}
... ...
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 authlib
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway"
"net/http"
)
type ApiAuthService struct {
gateway.Service
}
func (svc *ApiAuthService) MeInfo(ctx context.Context, request RequestUserMeQuery) (*DataUserMe, error) {
var result DataUserMe
if err := svc.Do(ctx, "/v1/user/me", http.MethodGet, request, &result); err != nil {
return nil, err
}
return &result, nil
}
func (svc *ApiAuthService) MeAppInfo(ctx context.Context, request RequestUserMeQuery) (*DataUserAppInfo, error) {
var result DataUserAppInfo
if err := svc.Do(ctx, "/v1/user/me-app-info", http.MethodGet, request, &result); err != nil {
return nil, err
}
return &result, nil
}
func (svc *ApiAuthService) LoginCheck(ctx context.Context, request RequestLoginCheck) (*DataLoginCheck, error) {
var (
result DataLoginCheck
err error
)
if err = svc.Do(ctx, "/v1/login/check?token="+request.Token, http.MethodGet, request, &result); err != nil {
return nil, err
}
if errCodeMsg, ok := err.(gateway.HttpError); ok {
return &DataLoginCheck{
Code: errCodeMsg.Base.Code,
Msg: errCodeMsg.Base.Msg,
}, nil
}
return &result, nil
}
func (svc *ApiAuthService) AppLogin(ctx context.Context, request RequestAppLogin) (*DataAppLogin, error) {
var result DataAppLogin
if err := svc.Do(ctx, "/v1/login/check?token="+request.Token, http.MethodGet, request, &result); err != nil {
return nil, err
}
return &result, nil
}
... ...
package authlib
type RequestUserMeQuery struct {
Token string `header:"x-mmm-accesstoken"`
//UserId int
//CompanyId int
}
type DataUserMe struct {
User *struct {
ID string `json:"id"`
Phone string `json:"phone"`
NickName string `json:"nickName"`
Avatar string `json:"avatar"`
} `json:"user,optional"`
CompanyList []*struct {
ID string `json:"id"`
Name string `json:"name"`
Logo string `json:"logo"`
DefaultLogin int `json:"defaultLogin"`
Types int `json:"types"`
} `json:"companyList,optional"`
CurrentCompany *struct {
ID string `json:"id"`
Name string `json:"name"`
Logo string `json:"logo"`
DefaultLogin int `json:"defaultLogin"`
Types int `json:"types"`
} `json:"currentCompany,optional"`
Workbench []*struct {
ID int `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
CoverImage string `json:"coverImage"`
URL string `json:"url"`
} `json:"workbench,optional"`
Menus []*struct {
MenuID int `json:"menuId"`
ParentID int `json:"parentId"`
MenuName string `json:"menuName"`
Code string `json:"code"`
Types string `json:"types"`
} `json:"menus,optional"`
}
type RequestLoginCheck struct {
Token string
}
type DataLoginCheck struct {
Code int `json:"code,optional"`
Msg string `json:"msg,optional"`
}
type (
RequestAppLogin struct {
AppKey string `json:"appKey" valid:"Required"` // 应用键值
Token string `json:"token" valid:"Required"` // 凭证
}
DataAppLogin struct {
AppEnabled bool `json:"appEnabled"`
}
)
type (
DataUserAppInfo struct {
Apps []AppItem `json:"apps"`
}
AppItem struct {
AppId int64
AppKey string
AppName string
}
)
... ...
package gateway
import (
"encoding/json"
"fmt"
)
// Response 统一消息返回格式
type Response struct {
Code int `json:"code,optional"`
Msg string `json:"msg,optional"`
Data json.RawMessage `json:"data,optional"`
}
//
//type Request struct {
// Url string
// Method string
// Param interface{}
//}
type HttpError struct {
Base Response
}
func (e HttpError) Error() string {
if e.Base.Code > 0 && e.Base.Msg != "" {
return e.Base.Msg
}
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"
"io/ioutil"
"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{
Host: host,
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()
body []byte
)
response, err := gateway.service.Do(ctx, method, gateway.Host+url, 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{
Code: response.StatusCode,
Msg: response.Status,
},
}
}
body, err = Bytes(response)
if err != nil {
return err
}
if err = json.Unmarshal(body, &baseResponse); err != nil {
return err
}
if baseResponse.Code != 0 {
return HttpError{
Base: Response{
Code: baseResponse.Code,
Msg: baseResponse.Msg,
},
}
}
if err = mapping.UnmarshalJsonBytes(baseResponse.Data, result); err != nil {
return err
}
return nil
}
func (gateway Service) HandlerResponse(ctx context.Context, request *http.Request, response *http.Response, val, result interface{}) error {
var (
baseResponse = Response{}
begin = time.Now()
body []byte
err error
)
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, request.URL.Path, request.Method,
result,
string(jsonParam),
string(jsonData),
))
}
}()
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return HttpError{
Base: Response{
Code: response.StatusCode,
Msg: response.Status,
},
}
}
body, err = Bytes(response)
if err != nil {
return err
}
if err = json.Unmarshal(body, &baseResponse); err != nil {
return err
}
if baseResponse.Code != 0 {
return HttpError{
Base: Response{
Code: baseResponse.Code,
Msg: baseResponse.Msg,
},
}
}
if err = mapping.UnmarshalJsonBytes(baseResponse.Data, result); err != nil {
return err
}
return nil
}
func (gateway Service) GetService() httpc.Service {
return gateway.service
}
func Bytes(resp *http.Response) ([]byte, error) {
var body []byte
if resp.Body == nil {
return nil, nil
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
return body, err
}
... ...
package openlib
import (
"bytes"
"context"
"encoding/json"
"github.com/zeromicro/go-zero/core/mapping"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
)
type OpenApiService struct {
gateway.Service
}
func (svc *OpenApiService) PutFile(ctx context.Context, request RequestPutFile) (*DataPutFile, error) {
var result DataPutFile
r, _ := buildRequest(ctx, http.MethodPost, svc.Host+"/v1/vod/putObject", request)
response, err := svc.GetService().DoRequest(r)
if err != nil {
return nil, err
}
if err := svc.HandlerResponse(ctx, r, response, nil, &result); err != nil {
return nil, err
}
return &result, nil
}
func buildRequest(ctx context.Context, method, tmpUrl string, data any) (*http.Request, error) {
u, err := url.Parse(tmpUrl)
if err != nil {
return nil, err
}
var val map[string]map[string]any
if data != nil {
val, err = mapping.Marshal(data)
if err != nil {
return nil, err
}
}
var reader io.Reader
jsonVars, hasJsonBody := val["json"]
if hasJsonBody {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
if err := enc.Encode(jsonVars); err != nil {
return nil, err
}
reader = &buf
}
req, err := http.NewRequestWithContext(ctx, method, u.String(), reader)
if err != nil {
return nil, err
}
fileVars, hasFile := val["file"]
if hasFile {
if err = fillFile(req, fileVars); err != nil {
return nil, err
}
}
if hasJsonBody {
req.Header.Set("Content-Type", "application/json; charset=utf-8")
}
return req, nil
}
func fillFile(req *http.Request, val map[string]any) error {
if len(val) == 0 {
return nil
}
pr, pw := io.Pipe()
bodyWriter := multipart.NewWriter(pw)
go func() {
for k, v := range val {
fileWriter, err := bodyWriter.CreateFormFile(k, k)
if err != nil {
return
}
//fh, err := os.Open(v.(string))
//if err != nil {
// return err
//}
fh := bytes.NewBuffer(v.([]byte))
// iocopy
_, err = io.Copy(fileWriter, fh)
if err != nil {
return
}
}
bodyWriter.Close()
pw.Close()
}()
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
req.Body = ioutil.NopCloser(pr)
req.Header.Set("Transfer-Encoding", "chunked")
return nil
}
... ...
package openlib
type (
RequestPutFile struct {
File []byte `file:"file.png"`
}
DataPutFile []*DataUploadItem
DataUploadItem struct {
Host string `json:"host"`
Key string `json:"key"`
Path string `json:"path"`
FileName string `json:"fileName"`
}
)
... ...
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
}
}
... ...
package smslib
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway"
"net/http"
)
type SMSService struct {
gateway.Service
}
func (svc *SMSService) SendSmsCode(ctx context.Context, request RequestSendSmsCode) (*DataSendSmsCode, error) {
var result DataSendSmsCode
if err := svc.Do(ctx, "/service/sendSms", http.MethodPost, request, &result); err != nil {
return nil, err
}
return &result, nil
}
func (svc *SMSService) CheckSmsCode(ctx context.Context, request RequestCheckSmsCode) (*DataCheckSmsCode, error) {
var result DataCheckSmsCode
if err := svc.Do(ctx, "/service/checkSmsCode", http.MethodPost, request, &result); err != nil {
return nil, err
}
return &result, nil
}
... ...
package smslib
type (
RequestSendSmsCode struct {
Phone string `json:"phone"`
}
DataSendSmsCode struct {
}
)
type (
RequestCheckSmsCode struct {
Phone string `json:"phone"`
Code string `json:"code"`
}
DataCheckSmsCode struct {
}
)
... ...
package middleware
import (
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc/pkg/gateway/authlib"
"net/http"
)
type LoginStatusCheckMiddleware struct {
apiAuth authlib.ApiAuthService
}
func NewLoginStatusCheckMiddleware(apiAuth authlib.ApiAuthService) *LoginStatusCheckMiddleware {
return &LoginStatusCheckMiddleware{
apiAuth: apiAuth,
}
}
func (m *LoginStatusCheckMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("x-mmm-accesstoken")
if len(token) > 0 {
_, err := m.apiAuth.LoginCheck(r.Context(), authlib.RequestLoginCheck{
Token: token,
})
if err != nil {
gatewayError, ok := err.(gateway.HttpError)
if ok {
unAuthResponse(w, gatewayError.Base.Code, gatewayError.Base.Msg)
return
}
httpx.ErrorCtx(r.Context(), w, err)
return
}
}
next(w, r)
}
}
func unAuthResponse(w http.ResponseWriter, code int, msg string) {
data := map[string]interface{}{
"msg": msg,
"code": code,
"data": struct{}{},
}
httpx.WriteJson(w, http.StatusUnauthorized, data)
}
... ...
package middleware
import (
"github.com/zeromicro/go-zero/rest/handler"
"net/http"
)
type LogRequestMiddleware struct {
logRequest bool
}
// NewLogRequestMiddleware 记录请求
// logRequest true开启记录,false关闭记录
func NewLogRequestMiddleware(logRequest bool) *LogRequestMiddleware {
return &LogRequestMiddleware{
logRequest: logRequest,
}
}
func (m *LogRequestMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
if !m.logRequest {
return func(writer http.ResponseWriter, request *http.Request) {
next(writer, request)
}
}
return handler.DetailedLogHandler(next).(http.HandlerFunc)
}
... ...
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"
"time"
)
type UserToken struct {
UserId int64 `json:"userId"`
AdminId int64 `json:"adminId"`
CompanyId int64 `json:"companyId"`
ClientType string `json:"clientType"`
}
func (tk UserToken) GenerateToken(secret string, expire int64) (string, error) {
claims := make(jwt.MapClaims)
claims["exp"] = time.Now().Unix() + expire
claims["iat"] = time.Now().Unix()
claims["userId"] = tk.UserId
claims["adminId"] = tk.AdminId
claims["companyId"] = tk.CompanyId
claims["clientType"] = tk.ClientType
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = claims
return token.SignedString([]byte(secret))
}
... ...
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 oss
import (
"encoding/json"
"fmt"
"image"
_ "image/jpeg"
"net/http"
"strings"
"time"
)
type FileInfo struct {
FileSize struct {
Value string
}
Format struct {
Value string
}
ImageHeight struct {
Value string
}
ImageWidth struct {
Value string
}
}
func GetImageInfo(url string) (info FileInfo, err error) {
//ok := strings.HasPrefix(url, "https://timeless-world.oss-cn-shenzhen.aliyuncs.com")
ok := strings.HasPrefix(url, "http")
if !ok {
return
}
ok = strings.Contains(url, "aliyuncs")
if !ok {
return
}
apiUrl := url + `?x-oss-process=image/info`
req, err := http.NewRequest(http.MethodGet, apiUrl, nil)
if err != nil {
return info, err
}
httpclient := http.Client{
Timeout: 5 * time.Second,
}
resp, err := httpclient.Do(req)
if err != nil {
return info, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return
}
jDecoder := json.NewDecoder(resp.Body)
err = jDecoder.Decode(&info)
if err != nil {
return info, err
}
return info, nil
}
// 获取视频封面图
func GetVideoCover(videoUrl string) (coverUrl string, w int, h int, err error) {
ok := strings.HasPrefix(videoUrl, "http")
if !ok {
return
}
ok = strings.Contains(videoUrl, "aliyuncs")
if !ok {
return
}
videoUrl = videoUrl + "?x-oss-process=video/snapshot,t_100,f_jpg,m_fast"
httpclient := http.Client{
Timeout: 5 * time.Second,
}
res, err := httpclient.Get(videoUrl)
if err != nil || res.StatusCode != http.StatusOK {
return videoUrl, 600, 600, fmt.Errorf("获取图片失败:%s", err)
}
defer res.Body.Close()
m, _, err := image.Decode(res.Body)
if err != nil {
return videoUrl, 600, 600, fmt.Errorf("获取图片失败:%s", err)
}
return videoUrl, m.Bounds().Dx(), m.Bounds().Dy(), nil
}
... ...
package oss
import "testing"
func TestGetVideoCover(t *testing.T) {
cover, w, h, err := GetVideoCover("https://timeless-world.oss-cn-shenzhen.aliyuncs.com/open-api/dev_online/20230913/object/1694587897_yYfG6TYTsGMCKETxdnTEhAQjXpYGD3MB.mp4")
t.Logf("cover=%v, w=%v, h=%v, err=%v", cover, w, h, err)
}
... ...
package tool
import (
"strings"
"github.com/mozillazg/go-pinyin"
)
func ToPinYin(hans string, sep string) string {
a := pinyin.NewArgs()
tmp := pinyin.Pinyin(hans, a)
result := make([]string, 0)
for i := range tmp {
result = append(result, tmp[i]...)
}
py := strings.Join(result, sep)
if len(py) == 0 {
return strings.ToLower(hans)
}
return py
}
... ...
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{}, dst interface{}) (int64, *gorm.DB) {
var total int64
// 只返回数量
if v, ok := params["countOnly"]; ok && v.(bool) {
tx = tx.Count(&total)
return total, tx
}
// 只返回记录
if v, ok := params["findOnly"]; ok && v.(bool) {
if v, ok := params["offset"]; ok {
tx.Offset(v.(int))
}
if v, ok := params["limit"]; ok {
tx.Limit(v.(int))
}
if tx = tx.Find(dst); tx.Error != nil {
return 0, tx
}
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(dst); tx.Error != nil {
return 0, tx
}
return total, tx
}
... ...
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
/**默认的服务错误**/
func NewErr(err error) *CodeError {
return &CodeError{errCode: ServerCommonError, InternalError: err}
}
func NewErrMsg(errMsg string) *CodeError {
return &CodeError{errCode: ServerCommonError, errMsg: errMsg}
}
func NewErrMsgErr(errMsg string, internalError error) *CodeError {
return &CodeError{errCode: ServerCommonError, errMsg: errMsg, InternalError: internalError}
}
/**指定错误码的错误**/
func NewCodeErr(errCode int, err error) *CodeError {
return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode), InternalError: err}
}
func NewCodeErrMsg(errCode int, err error, msg string) *CodeError {
return &CodeError{errCode: errCode, errMsg: msg, InternalError: err}
}
... ...
package xerr
import "fmt"
const (
// OK 成功返回
OK int = 200
)
// 全局错误码
// 系统错误前3位代表业务,后三位代表具体功能
const (
ServerCommonError int = 100001 // 系统错误
RequestParamError int = 100002 // 参数请求错误
TokenExpireError int = 100003 // token失效
TokenGenerateError int = 100004 // 生成token失败
DbError int = 100005 // 数据库错误
DbUpdateAffectedZeroError int = 100006 // 数据库更新错误
)
/**微信模块**/
const (
ErrWxMiniAuthFailError int = 500001
ErrUserNoAuth int = 500002
)
type CodeError struct {
errCode int
errMsg string
InternalError error
}
// GetErrCode 返回给前端的错误码
func (e *CodeError) GetErrCode() int {
return e.errCode
}
// GetErrMsg 返回给前端显示端错误信息
func (e *CodeError) GetErrMsg() string {
if e.errMsg == "" {
return MapErrMsg(e.errCode)
}
return e.errMsg
}
func (e *CodeError) Error() string {
if e.InternalError != nil {
return fmt.Sprintf("code error: code=%d msg:%s error:%s", e.errCode, e.errMsg, e.InternalError.Error())
}
return fmt.Sprintf("code error: code=%d msg:%s", e.errCode, e.errMsg)
}
... ...
package xerr
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc/status"
"net/http"
)
func ErrorHandlerCtx(ctx context.Context, err error) (int, any) {
//错误返回
var (
errCode = ServerCommonError
)
// 自定义错误类型
if codeError, ok := err.(*CodeError); ok {
errCode = codeError.GetErrCode()
} else {
// grpc err错误
var grpcStatus *status.Status
if grpcStatus, ok = status.FromError(err); ok {
grpcCode := int(grpcStatus.Code())
if IsCodeErr(grpcCode) {
errCode = grpcCode
}
}
}
logx.WithContext(ctx).Errorf("【ERROR-HANDLER】 : %+v ", err)
response := Error(errCode, MapErrMsg(errCode))
response.Error = err.Error()
return http.StatusOK, response
}
func OkHandlerCtx(ctx context.Context, a any) any {
return Success(a)
}
func Success(data interface{}) *ResponseSuccessBean {
return &ResponseSuccessBean{Code: 0, Msg: "OK", Data: data}
}
type ResponseSuccessBean struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
type ResponseErrorBean struct {
Code int `json:"code"`
Msg string `json:"msg"`
Error string `json:"err"`
}
func Error(errCode int, errMsg string) *ResponseErrorBean {
return &ResponseErrorBean{Code: errCode, Msg: errMsg}
}
... ...
package xerr
var message map[int]string
func init() {
message = make(map[int]string)
message[OK] = "SUCCESS"
message[ServerCommonError] = "服务器开小差啦,稍后再来试一试"
message[RequestParamError] = "参数错误"
message[TokenExpireError] = "token失效,请重新登陆"
message[TokenGenerateError] = "生成token失败"
message[DbError] = "数据库繁忙,请稍后再试"
message[DbUpdateAffectedZeroError] = "更新数据影响行数为0"
message[ErrUserNoAuth] = "无权限"
message[ErrWxMiniAuthFailError] = "微信授权失败"
}
func MapErrMsg(errCode int) string {
if msg, ok := message[errCode]; ok {
return msg
} else {
return "服务器开小差啦,稍后再来试一试"
}
}
func IsCodeErr(errCode int) bool {
if _, ok := message[errCode]; ok {
return true
} else {
return false
}
}
... ...