作者 yangfu

init

  1 +# Compiled Object codefiles, Static and Dynamic libs (Shared Objects)
  2 +*.o
  3 +*.a
  4 +*.so
  5 +
  6 +# Folders
  7 +_obj
  8 +_test
  9 +
  10 +# Architecture specific extensions/prefixes
  11 +*.[568vq]
  12 +[568vq].out
  13 +
  14 +*.cgo1.go
  15 +*.cgo2.c
  16 +_cgo_defun.c
  17 +_cgo_gotypes.go
  18 +_cgo_export.*
  19 +
  20 +_testmain.go
  21 +
  22 +*.exe
  23 +*.test
  24 +.log
  25 +.idea
  26 +.vscode
  27 +
  28 +app.log
  29 +go.sum
  30 +lastupdate.tmp
  31 +*.log
  32 +
  33 +public/*
  34 +logs/
  1 +.PHONY: model
  2 +model:
  3 + goctl model mysql ddl -s .\deploy\database\table.sql -d cmd/discuss
  4 +
  5 +.PHONY: api
  6 +api:
  7 + goctl api go -api .\cmd\discuss\mini\dsl\core.api -dir cmd/discuss/api -style go_zero
  8 +
  9 +.PHONY: swagger
  10 +swagger:
  11 + goctl api plugin -plugin goctl-swagger="swagger -filename core.json" -api .\cmd\discuss\api\dsl\core.api -dir .\cmd\discuss\api\dsl
  1 +## 生成模型
  2 +
  3 +```
  4 +goctl model mysql ddl -s .\deploy\database\table.sql -d cmd/discuss
  5 +```
  6 +
  7 +## api生成
  8 +
  9 +```
  10 +goctl api go -api .\cmd\discuss\mini\dsl\core.api -dir cmd/discuss/api -style go_zero
  11 +```
  12 +
  13 +## swagger 生成
  14 +
  15 +```
  16 +goctl api plugin -plugin goctl-swagger="swagger -filename core.json" -api .\cmd\discuss\api\dsl\core.api -dir .\cmd\discuss\api\dsl
  17 +```
  1 +package main
  2 +
  3 +import (
  4 + "flag"
  5 + "github.com/zeromicro/go-zero/core/logx"
  6 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db"
  7 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  8 + "net/http"
  9 + "strings"
  10 +
  11 + "github.com/golang-jwt/jwt/v4/request"
  12 + "github.com/zeromicro/go-zero/core/conf"
  13 + "github.com/zeromicro/go-zero/rest"
  14 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/config"
  15 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler"
  16 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  17 +)
  18 +
  19 +var configFile = flag.String("f", "etc/core.yaml", "the config file")
  20 +
  21 +func main() {
  22 + flag.Parse()
  23 +
  24 + var c config.Config
  25 + conf.MustLoad(*configFile, &c)
  26 +
  27 + // 默认的token头 Authorization 修改未 x-token
  28 + request.AuthorizationHeaderExtractor = &request.PostExtractionFilter{
  29 + request.HeaderExtractor{"x-mmm-accesstoken"}, func(tok string) (string, error) {
  30 + // Should be a bearer token
  31 + if len(tok) > 6 && strings.ToUpper(tok[0:7]) == "BEARER " {
  32 + return tok[7:], nil
  33 + }
  34 + return tok, nil
  35 + },
  36 + }
  37 +
  38 + // 初始化Domain里面的配置
  39 + domain.ProjectName = c.Name
  40 +
  41 + opts := make([]rest.RunOption, 0)
  42 + // cors
  43 + opt := rest.WithCustomCors(func(header http.Header) {
  44 + header.Set("Access-Control-Allow-Headers", "*")
  45 + }, func(writer http.ResponseWriter) {
  46 +
  47 + })
  48 + opts = append(opts, opt)
  49 +
  50 + server := rest.MustNewServer(c.RestConf, opts...)
  51 + defer server.Stop()
  52 +
  53 + ctx := svc.NewServiceContext(c)
  54 + handler.RegisterHandlers(server, ctx)
  55 +
  56 + db.Migrate(ctx.DB)
  57 +
  58 + logx.Infof("Starting server at %s:%d... \n", c.Host, c.Port)
  59 + server.Start()
  60 +}
  1 +import "core/test.api"
  1 +{
  2 + "swagger": "2.0",
  3 + "info": {
  4 + "title": "",
  5 + "version": ""
  6 + },
  7 + "schemes": [
  8 + "http",
  9 + "https"
  10 + ],
  11 + "consumes": [
  12 + "application/json"
  13 + ],
  14 + "produces": [
  15 + "application/json"
  16 + ],
  17 + "paths": {
  18 + "v1/health": {
  19 + "get": {
  20 + "summary": "健康",
  21 + "operationId": "miniHealth",
  22 + "responses": {
  23 + "200": {
  24 + "description": "A successful response.",
  25 + "schema": {
  26 + "$ref": "#/definitions/MiniHealthResposne"
  27 + }
  28 + }
  29 + },
  30 + "requestBody": {},
  31 + "tags": [
  32 + "tool"
  33 + ]
  34 + }
  35 + }
  36 + },
  37 + "definitions": {
  38 + "MiniHealthRequest": {
  39 + "type": "object",
  40 + "title": "MiniHealthRequest"
  41 + },
  42 + "MiniHealthResposne": {
  43 + "type": "object",
  44 + "properties": {
  45 + "ok": {
  46 + "type": "boolean",
  47 + "format": "boolean"
  48 + }
  49 + },
  50 + "title": "MiniHealthResposne",
  51 + "required": [
  52 + "ok"
  53 + ]
  54 + }
  55 + },
  56 + "securityDefinitions": {
  57 + "apiKey": {
  58 + "type": "apiKey",
  59 + "description": "Enter JWT Bearer token **_only_**",
  60 + "name": "Authorization",
  61 + "in": "header"
  62 + }
  63 + },
  64 + "security": [
  65 + {
  66 + "apiKey": []
  67 + }
  68 + ]
  69 +}
  1 +syntax = "v1"
  2 +
  3 +info(
  4 + title: "天联鹰蜓"
  5 + desc: "天联鹰蜓"
  6 + author: "小火箭"
  7 + email: "email"
  8 + version: "v1"
  9 +)
  10 +
  11 +@server(
  12 + prefix: v1
  13 + group: tool
  14 +)
  15 +service Core {
  16 + @doc "健康"
  17 + @handler miniHealth
  18 + get /health (MiniHealthRequest) returns (MiniHealthResposne)
  19 +}
  20 +
  21 +type(
  22 + MiniHealthRequest struct{
  23 +
  24 + }
  25 + MiniHealthResposne struct{
  26 + Ok bool `json:"ok"`
  27 + }
  28 +)
  1 +Name: discuss
  2 +Host: 0.0.0.0
  3 +Port: 8081
  4 +Verbose: true
  5 +
  6 +Log:
  7 + #Mode: file
  8 + Encoding: plain
  9 + Level: debug # info
  10 + MaxSize: 1 # 2MB
  11 + TimeFormat: 2006-01-02 15:04:05.000
  12 +
  13 +JwtAuth:
  14 + AccessSecret: digital-platform
  15 + Expire: 360000
  16 +
  17 +Redis:
  18 + Host: 127.0.0.1:6379
  19 + Type: node
  20 + Pass:
  21 +DB:
  22 + DataSource: host=114.55.200.59 user=postgres password=eagle1010 dbname=sumifcc-discuss-dev port=31543 sslmode=disable TimeZone=Asia/Shanghai
  1 +package config
  2 +
  3 +import (
  4 + "github.com/zeromicro/go-zero/core/stores/redis"
  5 + "github.com/zeromicro/go-zero/rest"
  6 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/config"
  7 +)
  8 +
  9 +type Config struct {
  10 + rest.RestConf
  11 + config.Config
  12 + Redis redis.RedisConf `json:",optional"`
  13 +}
  1 +// Code generated by goctl. DO NOT EDIT.
  2 +package handler
  3 +
  4 +import (
  5 + "net/http"
  6 +
  7 + tool "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/tool"
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  9 +
  10 + "github.com/zeromicro/go-zero/rest"
  11 +)
  12 +
  13 +func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
  14 + server.AddRoutes(
  15 + []rest.Route{
  16 + {
  17 + Method: http.MethodGet,
  18 + Path: "/health",
  19 + Handler: tool.MiniHealthHandler(serverCtx),
  20 + },
  21 + },
  22 + rest.WithPrefix("/v1"),
  23 + )
  24 +}
  1 +package tool
  2 +
  3 +import (
  4 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/result"
  5 + "net/http"
  6 +
  7 + "github.com/zeromicro/go-zero/rest/httpx"
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/tool"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  10 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/types"
  11 +)
  12 +
  13 +func MiniHealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
  14 + return func(w http.ResponseWriter, r *http.Request) {
  15 + var req types.MiniHealthRequest
  16 + if err := httpx.Parse(r, &req); err != nil {
  17 + httpx.ErrorCtx(r.Context(), w, err)
  18 + return
  19 + }
  20 +
  21 + l := tool.NewMiniHealthLogic(r.Context(), svcCtx)
  22 + resp, err := l.MiniHealth(&req)
  23 + result.HttpResult(r, w, resp, err)
  24 + }
  25 +}
  1 +package tool
  2 +
  3 +import (
  4 + "context"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
  6 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/xerr"
  7 +
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/types"
  10 +
  11 + "github.com/zeromicro/go-zero/core/logx"
  12 +)
  13 +
  14 +type MiniHealthLogic struct {
  15 + logx.Logger
  16 + ctx context.Context
  17 + svcCtx *svc.ServiceContext
  18 +}
  19 +
  20 +func NewMiniHealthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MiniHealthLogic {
  21 + return &MiniHealthLogic{
  22 + Logger: logx.WithContext(ctx),
  23 + ctx: ctx,
  24 + svcCtx: svcCtx,
  25 + }
  26 +}
  27 +
  28 +func (l *MiniHealthLogic) MiniHealth(req *types.MiniHealthRequest) (resp *types.MiniHealthResposne, err error) {
  29 + // 普通查询
  30 + var conn = l.svcCtx.DefaultDBConn()
  31 + l.svcCtx.CommentRepository.FindOne(l.ctx, conn, 0)
  32 +
  33 + // 事务查询
  34 + if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
  35 + l.svcCtx.CommentRepository.FindOne(ctx, l.svcCtx.DefaultDBConn(), 0)
  36 + return nil
  37 + }, true); err != nil {
  38 + return nil, xerr.NewErrMsgErr("健康检查失败", err)
  39 + }
  40 + return
  41 +}
  1 +package svc
  2 +
  3 +import (
  4 + "github.com/zeromicro/go-zero/core/stores/redis"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/config"
  6 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/repository"
  7 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/cache"
  10 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/database"
  11 + "gorm.io/gorm"
  12 +)
  13 +
  14 +type ServiceContext struct {
  15 + Config config.Config
  16 + DB *gorm.DB
  17 + Redis *redis.Redis
  18 +
  19 + CommentRepository domain.CommentRepository
  20 +}
  21 +
  22 +func NewServiceContext(c config.Config) *ServiceContext {
  23 +
  24 + db := database.OpenGormPGDB(c.DB.DataSource, c.Log.Mode)
  25 + mlCache := cache.NewMultiLevelCache([]string{c.Redis.Host}, c.Redis.Pass)
  26 + redis, _ := redis.NewRedis(redis.RedisConf{Host: c.Redis.Host, Pass: c.Redis.Pass, Type: "node"})
  27 +
  28 + return &ServiceContext{
  29 + Config: c,
  30 + DB: db,
  31 + Redis: redis,
  32 + CommentRepository: repository.NewCommentRepository(cache.NewCachedRepository(mlCache)),
  33 + }
  34 +}
  35 +
  36 +func (svc *ServiceContext) DefaultDBConn() transaction.Conn {
  37 + return transaction.NewTransactionContext(svc.DB)
  38 +}
  1 +// Code generated by goctl. DO NOT EDIT.
  2 +package types
  3 +
  4 +type MiniHealthRequest struct {
  5 +}
  6 +
  7 +type MiniHealthResposne struct {
  8 + Ok bool `json:"ok"`
  9 +}
  1 +
  2 +syntax = "v1"
  3 +
  4 +info(
  5 + title: "xx实例"
  6 + desc: "xx实例"
  7 + author: "author"
  8 + email: "email"
  9 + version: "v1"
  10 +)
  11 +
  12 +@server(
  13 + prefix: comment/v1
  14 + group: comment
  15 + jwt: JwtAuth
  16 +)
  17 +service Core {
  18 + @handler getComment
  19 + post /comment/:id (CommentGetRequest) returns (CommentGetResponse)
  20 + @handler saveComment
  21 + post /comment (CommentSaveRequest) returns (CommentSaveResponse)
  22 + @handler deleteComment
  23 + delete /comment/:id (CommentDeleteRequest) returns (CommentDeleteResponse)
  24 + @handler updateComment
  25 + put /comment/:id (CommentUpdateRequest) returns (CommentUpdateResponse)
  26 + @handler searchComment
  27 + post /comment/search (CommentSearchRequest) returns (CommentSearchResponse)
  28 +}
  29 +
  30 +type (
  31 + CommentGetRequest {
  32 + Id int64 `path:"id"`
  33 + }
  34 + CommentGetResponse struct{
  35 + Comment CommentItem `json:"comment"`
  36 + }
  37 +
  38 + CommentSaveRequest struct{
  39 + Comment CommentItem `json:"comment"`
  40 + }
  41 + CommentSaveResponse struct{}
  42 +
  43 + CommentDeleteRequest struct{
  44 + Id int64 `path:"id"`
  45 + }
  46 + CommentDeleteResponse struct{}
  47 +
  48 + CommentUpdateRequest struct{
  49 + Id int64 `path:"id"`
  50 + Comment CommentItem `json:"comment"`
  51 + }
  52 + CommentUpdateResponse struct{}
  53 +
  54 + CommentSearchRequest struct{
  55 + Page int `json:"page"`
  56 + Size int `json:"size"`
  57 + }
  58 + CommentSearchResponse{
  59 + List []CommentItem `json:"list"`
  60 + Total int64 `json:"total"`
  61 + }
  62 + CommentItem struct{
  63 +
  64 + }
  65 +)
  1 +
  2 +syntax = "proto3";
  3 +
  4 +option go_package ="./pb";
  5 +
  6 +package pb;
  7 +
  8 +message CommentGetReq {
  9 + int64 Id = 1;
  10 +}
  11 +message CommentGetResp{
  12 + CommentItem User = 1;
  13 +}
  14 +
  15 +message CommentSaveReq {
  16 +
  17 +}
  18 +message CommentSaveResp{
  19 +
  20 +}
  21 +
  22 +message CommentDeleteReq {
  23 + int64 Id = 1;
  24 +}
  25 +message CommentDeleteResp{
  26 +
  27 +}
  28 +
  29 +message CommentUpdateReq {
  30 + int64 Id = 1;
  31 +}
  32 +message CommentUpdateResp{
  33 +
  34 +}
  35 +
  36 +message CommentSearchReq {
  37 + int64 PageNumber = 1;
  38 + int64 PageSize = 2;
  39 +}
  40 +message CommentSearchResp{
  41 + repeated CommentItem List =1;
  42 + int64 Total =2;
  43 +}
  44 +message CommentItem {
  45 +
  46 +}
  47 +
  48 +service CommentService {
  49 + rpc CommentGet(CommentGetReq) returns(CommentGetResp);
  50 + rpc CommentSave(CommentSaveReq) returns(CommentSaveResp);
  51 + rpc CommentDelete(CommentDeleteReq) returns(CommentDeleteResp);
  52 + rpc CommentUpdate(CommentUpdateReq) returns(CommentUpdateResp);
  53 + rpc CommentSearch(CommentSearchReq) returns(CommentSearchResp);
  54 +}
  1 +package db
  2 +
  3 +import (
  4 + "gorm.io/gorm"
  5 +)
  6 +
  7 +func Migrate(db *gorm.DB) {
  8 + db.AutoMigrate()
  9 +}
  1 +package models
  2 +
  3 +import (
  4 + "fmt"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  6 + "gorm.io/gorm"
  7 + "gorm.io/plugin/soft_delete"
  8 + "time"
  9 +)
  10 +
  11 +type Comment struct {
  12 + Id int64 // 唯一标识
  13 +
  14 + CreatedAt int64 `json:",omitempty"`
  15 + UpdatedAt int64 `json:",omitempty"`
  16 + DeletedAt int64 `json:",omitempty"`
  17 + Version int `json:",omitempty"`
  18 + IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"`
  19 +}
  20 +
  21 +func (m *Comment) TableName() string {
  22 + return "t_comment"
  23 +}
  24 +
  25 +func (m *Comment) BeforeCreate(tx *gorm.DB) (err error) {
  26 + m.CreatedAt = time.Now().Unix()
  27 + m.UpdatedAt = time.Now().Unix()
  28 + return
  29 +}
  30 +
  31 +func (m *Comment) BeforeUpdate(tx *gorm.DB) (err error) {
  32 + m.UpdatedAt = time.Now().Unix()
  33 + return
  34 +}
  35 +
  36 +func (m *Comment) CacheKeyFunc() string {
  37 + if m.Id == 0 {
  38 + return ""
  39 + }
  40 + return fmt.Sprintf("%v:cache:%v:id:%v", domain.ProjectName, m.TableName(), m.Id)
  41 +}
  42 +
  43 +func (m *Comment) CacheKeyFuncByObject(obj interface{}) string {
  44 + if v, ok := obj.(*Comment); ok {
  45 + return v.CacheKeyFunc()
  46 + }
  47 + return ""
  48 +}
  49 +
  50 +func (m *Comment) CachePrimaryKeyFunc() string {
  51 + if len("") == 0 {
  52 + return ""
  53 + }
  54 + return fmt.Sprintf("%v:cache:%v:primarykey:%v", domain.ProjectName, m.TableName(), "key")
  55 +}
  1 +package repository
  2 +
  3 +import (
  4 + "context"
  5 + "github.com/jinzhu/copier"
  6 + "github.com/pkg/errors"
  7 + "github.com/tiptok/gocomm/pkg/cache"
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/models"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
  10 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  11 + "gorm.io/gorm"
  12 +)
  13 +
  14 +type CommentRepository struct {
  15 + *cache.CachedRepository
  16 +}
  17 +
  18 +func (repository *CommentRepository) Insert(ctx context.Context, conn transaction.Conn, dm *domain.Comment) (*domain.Comment, error) {
  19 + var (
  20 + err error
  21 + m = &models.Comment{}
  22 + tx = conn.DB()
  23 + )
  24 + if m, err = repository.DomainModelToModel(dm); err != nil {
  25 + return nil, err
  26 + }
  27 + if tx = tx.Model(m).Save(m); tx.Error != nil {
  28 + return nil, tx.Error
  29 + }
  30 + dm.Id = m.Id
  31 + return repository.ModelToDomainModel(m)
  32 +
  33 +}
  34 +
  35 +func (repository *CommentRepository) Update(ctx context.Context, conn transaction.Conn, dm *domain.Comment) (*domain.Comment, error) {
  36 + var (
  37 + err error
  38 + m *models.Comment
  39 + tx = conn.DB()
  40 + )
  41 + if m, err = repository.DomainModelToModel(dm); err != nil {
  42 + return nil, err
  43 + }
  44 + queryFunc := func() (interface{}, error) {
  45 + tx = tx.Model(m).Updates(m)
  46 + return nil, tx.Error
  47 + }
  48 + if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
  49 + return nil, err
  50 + }
  51 + return repository.ModelToDomainModel(m)
  52 +}
  53 +
  54 +func (repository *CommentRepository) UpdateWithVersion(ctx context.Context, transaction transaction.Conn, dm *domain.Comment) (*domain.Comment, error) {
  55 + var (
  56 + err error
  57 + m *models.Comment
  58 + tx = transaction.DB()
  59 + )
  60 + if m, err = repository.DomainModelToModel(dm); err != nil {
  61 + return nil, err
  62 + }
  63 + oldVersion := dm.Version
  64 + m.Version += 1
  65 + queryFunc := func() (interface{}, error) {
  66 + tx = tx.Model(m).Select("*").Where("id = ?", m.Id).Where("version = ?", oldVersion).Updates(m)
  67 + if tx.RowsAffected == 0 {
  68 + return nil, domain.ErrUpdateFail
  69 + }
  70 + return nil, tx.Error
  71 + }
  72 + if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
  73 + return nil, err
  74 + }
  75 + return repository.ModelToDomainModel(m)
  76 +}
  77 +
  78 +func (repository *CommentRepository) Delete(ctx context.Context, conn transaction.Conn, dm *domain.Comment) (*domain.Comment, error) {
  79 + var (
  80 + tx = conn.DB()
  81 + m = &models.Comment{Id: dm.Identify().(int64)}
  82 + )
  83 + queryFunc := func() (interface{}, error) {
  84 + tx = tx.Where("id = ?", m.Id).Delete(m)
  85 + return m, tx.Error
  86 + }
  87 + if _, err := repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
  88 + return dm, err
  89 + }
  90 + return repository.ModelToDomainModel(m)
  91 +}
  92 +
  93 +func (repository *CommentRepository) FindOne(ctx context.Context, conn transaction.Conn, id int64) (*domain.Comment, error) {
  94 + var (
  95 + err error
  96 + tx = conn.DB()
  97 + m = new(models.Comment)
  98 + )
  99 + queryFunc := func() (interface{}, error) {
  100 + tx = tx.Model(m).Where("id = ?", id).First(m)
  101 + if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
  102 + return nil, domain.ErrNotFound
  103 + }
  104 + return m, tx.Error
  105 + }
  106 + cacheModel := new(models.Comment)
  107 + cacheModel.Id = id
  108 + if err = repository.QueryCache(cacheModel.CacheKeyFunc, m, queryFunc); err != nil {
  109 + return nil, err
  110 + }
  111 + return repository.ModelToDomainModel(m)
  112 +}
  113 +
  114 +func (repository *CommentRepository) Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*domain.Comment, error) {
  115 + var (
  116 + tx = conn.DB()
  117 + ms []*models.Comment
  118 + dms = make([]*domain.Comment, 0)
  119 + total int64
  120 + )
  121 + queryFunc := func() (interface{}, error) {
  122 + tx = tx.Model(&ms).Order("id desc")
  123 + if total, tx = transaction.PaginationAndCount(ctx, tx, queryOptions, &ms); tx.Error != nil {
  124 + return dms, tx.Error
  125 + }
  126 + return dms, nil
  127 + }
  128 +
  129 + if _, err := repository.Query(queryFunc); err != nil {
  130 + return 0, nil, err
  131 + }
  132 +
  133 + for _, item := range ms {
  134 + if dm, err := repository.ModelToDomainModel(item); err != nil {
  135 + return 0, dms, err
  136 + } else {
  137 + dms = append(dms, dm)
  138 + }
  139 + }
  140 + return total, dms, nil
  141 +}
  142 +
  143 +func (repository *CommentRepository) ModelToDomainModel(from *models.Comment) (*domain.Comment, error) {
  144 + to := &domain.Comment{}
  145 + err := copier.Copy(to, from)
  146 + return to, err
  147 +}
  148 +
  149 +func (repository *CommentRepository) DomainModelToModel(from *domain.Comment) (*models.Comment, error) {
  150 + to := &models.Comment{}
  151 + err := copier.Copy(to, from)
  152 + return to, err
  153 +}
  154 +
  155 +func NewCommentRepository(cache *cache.CachedRepository) domain.CommentRepository {
  156 + return &CommentRepository{CachedRepository: cache}
  157 +}
  1 +package transaction
  2 +
  3 +import (
  4 + "context"
  5 + "fmt"
  6 + "gorm.io/gorm"
  7 + "sync"
  8 +)
  9 +
  10 +type Context struct {
  11 + //启用事务标识
  12 + beginTransFlag bool
  13 + db *gorm.DB
  14 + session *gorm.DB
  15 + lock sync.Mutex
  16 +}
  17 +
  18 +func (transactionContext *Context) Begin() error {
  19 + transactionContext.lock.Lock()
  20 + defer transactionContext.lock.Unlock()
  21 + transactionContext.beginTransFlag = true
  22 + tx := transactionContext.db.Begin()
  23 + transactionContext.session = tx
  24 + return nil
  25 +}
  26 +
  27 +func (transactionContext *Context) Commit() error {
  28 + transactionContext.lock.Lock()
  29 + defer transactionContext.lock.Unlock()
  30 + if !transactionContext.beginTransFlag {
  31 + return nil
  32 + }
  33 + tx := transactionContext.session.Commit()
  34 + return tx.Error
  35 +}
  36 +
  37 +func (transactionContext *Context) Rollback() error {
  38 + transactionContext.lock.Lock()
  39 + defer transactionContext.lock.Unlock()
  40 + if !transactionContext.beginTransFlag {
  41 + return nil
  42 + }
  43 + tx := transactionContext.session.Rollback()
  44 + return tx.Error
  45 +}
  46 +
  47 +func (transactionContext *Context) DB() *gorm.DB {
  48 + if transactionContext.beginTransFlag && transactionContext.session != nil {
  49 + return transactionContext.session
  50 + }
  51 + return transactionContext.db
  52 +}
  53 +
  54 +func NewTransactionContext(db *gorm.DB) *Context {
  55 + return &Context{
  56 + db: db,
  57 + }
  58 +}
  59 +
  60 +type Conn interface {
  61 + Begin() error
  62 + Commit() error
  63 + Rollback() error
  64 + DB() *gorm.DB
  65 +}
  66 +
  67 +// UseTrans when beginTrans is true , it will begin a new transaction
  68 +// to execute the function, recover when panic happen
  69 +func UseTrans(ctx context.Context,
  70 + db *gorm.DB,
  71 + fn func(context.Context, Conn) error, beginTrans bool) (err error) {
  72 + var tx Conn
  73 + tx = NewTransactionContext(db)
  74 + if beginTrans {
  75 + if err = tx.Begin(); err != nil {
  76 + return
  77 + }
  78 + }
  79 + defer func() {
  80 + if p := recover(); p != nil {
  81 + if e := tx.Rollback(); e != nil {
  82 + err = fmt.Errorf("recover from %#v, rollback failed: %w", p, e)
  83 + } else {
  84 + err = fmt.Errorf("recoveer from %#v", p)
  85 + }
  86 + } else if err != nil {
  87 + if e := tx.Rollback(); e != nil {
  88 + err = fmt.Errorf("transaction failed: %s, rollback failed: %w", err, e)
  89 + }
  90 + } else {
  91 + err = tx.Commit()
  92 + }
  93 + }()
  94 +
  95 + return fn(ctx, tx)
  96 +}
  97 +
  98 +func PaginationAndCount(ctx context.Context, tx *gorm.DB, params map[string]interface{}, dst interface{}) (int64, *gorm.DB) {
  99 + var total int64
  100 + // 只返回数量
  101 + if v, ok := params["countOnly"]; ok && v.(bool) {
  102 + tx = tx.Count(&total)
  103 + return total, tx
  104 + }
  105 + // 只返回记录
  106 + if v, ok := params["findOnly"]; ok && v.(bool) {
  107 + if v, ok := params["offset"]; ok {
  108 + tx.Offset(v.(int))
  109 + }
  110 + if v, ok := params["limit"]; ok {
  111 + tx.Limit(v.(int))
  112 + }
  113 + if tx = tx.Find(dst); tx.Error != nil {
  114 + return 0, tx
  115 + }
  116 + return total, tx
  117 + }
  118 + tx = tx.Count(&total)
  119 + if tx.Error != nil {
  120 + return total, tx
  121 + }
  122 + if v, ok := params["offset"]; ok {
  123 + tx.Offset(v.(int))
  124 + }
  125 + if v, ok := params["limit"]; ok {
  126 + tx.Limit(v.(int))
  127 + }
  128 + if tx = tx.Find(dst); tx.Error != nil {
  129 + return 0, tx
  130 + }
  131 + return total, tx
  132 +}
  1 +package domain
  2 +
  3 +import (
  4 + "context"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
  6 +)
  7 +
  8 +type Comment struct {
  9 + Id int64 // 唯一标识
  10 +
  11 + CreatedAt int64 `json:",omitempty"`
  12 + UpdatedAt int64 `json:",omitempty"`
  13 + DeletedAt int64 `json:",omitempty"`
  14 + Version int `json:",omitempty"`
  15 +}
  16 +
  17 +type CommentRepository interface {
  18 + Insert(ctx context.Context, conn transaction.Conn, dm *Comment) (*Comment, error)
  19 + Update(ctx context.Context, conn transaction.Conn, dm *Comment) (*Comment, error)
  20 + UpdateWithVersion(ctx context.Context, conn transaction.Conn, dm *Comment) (*Comment, error)
  21 + Delete(ctx context.Context, conn transaction.Conn, dm *Comment) (*Comment, error)
  22 + FindOne(ctx context.Context, conn transaction.Conn, id int64) (*Comment, error)
  23 + Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*Comment, error)
  24 +}
  25 +
  26 +func (m *Comment) Identify() interface{} {
  27 + if m.Id == 0 {
  28 + return nil
  29 + }
  30 + return m.Id
  31 +}
  1 +package domain
  2 +
  3 +import (
  4 + "reflect"
  5 +)
  6 +
  7 +func OffsetLimit(page, size int) (offset int, limit int) {
  8 + if page == 0 {
  9 + page = 1
  10 + }
  11 + if size == 0 {
  12 + size = 20
  13 + }
  14 + offset = (page - 1) * size
  15 + limit = size
  16 + return
  17 +}
  18 +
  19 +type QueryOptions map[string]interface{}
  20 +
  21 +func NewQueryOptions() QueryOptions {
  22 + options := make(map[string]interface{})
  23 + return options
  24 +}
  25 +func (options QueryOptions) WithOffsetLimit(page, size int) QueryOptions {
  26 + offset, limit := OffsetLimit(page, size)
  27 + options["offset"] = offset
  28 + options["limit"] = limit
  29 + return options
  30 +}
  31 +
  32 +func (options QueryOptions) WithKV(key string, value interface{}) QueryOptions {
  33 + if reflect.ValueOf(value).IsZero() {
  34 + return options
  35 + }
  36 + options[key] = value
  37 + return options
  38 +}
  39 +
  40 +func (options QueryOptions) MustWithKV(key string, value interface{}) QueryOptions {
  41 + options[key] = value
  42 + return options
  43 +}
  44 +
  45 +func (options QueryOptions) Copy() QueryOptions {
  46 + newOptions := NewQueryOptions()
  47 + for k, v := range options {
  48 + newOptions[k] = v
  49 + }
  50 + return newOptions
  51 +}
  52 +
  53 +type IndexQueryOptionFunc func() QueryOptions
  54 +
  55 +// 自定义的一些查询条件
  56 +
  57 +func (options QueryOptions) WithCountOnly() QueryOptions {
  58 + options["countOnly"] = true
  59 + return options
  60 +}
  61 +func (options QueryOptions) WithFindOnly() QueryOptions {
  62 + options["findOnly"] = true
  63 + return options
  64 +}
  1 +package domain
  2 +
  3 +import (
  4 + "errors"
  5 + "github.com/zeromicro/go-zero/core/stores/sqlx"
  6 +)
  7 +
  8 +var (
  9 + ErrNotFound = sqlx.ErrNotFound
  10 + ErrUpdateFail = errors.New("sql: no rows affected")
  11 +)
  12 +
  13 +var ProjectName = "project"
  1 +CREATE TABLE `comment` (
  2 + `id` int(0) NOT NULL COMMENT '唯一标识',
  3 + PRIMARY KEY (`id`) USING BTREE
  4 +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
  1 +module gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss
  2 +
  3 +go 1.19
  4 +
  5 +require (
  6 + github.com/golang-jwt/jwt/v4 v4.5.0
  7 + github.com/jinzhu/copier v0.4.0
  8 + github.com/jinzhu/now v1.1.5
  9 + github.com/pkg/errors v0.9.1
  10 + github.com/stretchr/testify v1.8.4
  11 + github.com/tiptok/gocomm v1.0.14
  12 + github.com/zeromicro/go-zero v1.5.5
  13 + google.golang.org/grpc v1.57.0
  14 + gorm.io/driver/mysql v1.5.1
  15 + gorm.io/driver/postgres v1.5.2
  16 + gorm.io/gorm v1.25.4
  17 + gorm.io/plugin/soft_delete v1.2.1
  18 +)
  19 +
  20 +require (
  21 + github.com/Shopify/sarama v1.37.2 // indirect
  22 + github.com/beego/beego/v2 v2.0.1 // indirect
  23 + github.com/beorn7/perks v1.0.1 // indirect
  24 + github.com/cenkalti/backoff/v4 v4.2.0 // indirect
  25 + github.com/cespare/xxhash/v2 v2.2.0 // indirect
  26 + github.com/coreos/go-semver v0.3.1 // indirect
  27 + github.com/coreos/go-systemd/v22 v22.5.0 // indirect
  28 + github.com/davecgh/go-spew v1.1.1 // indirect
  29 + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
  30 + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
  31 + github.com/eapache/go-resiliency v1.3.0 // indirect
  32 + github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
  33 + github.com/eapache/queue v1.1.0 // indirect
  34 + github.com/emicklei/go-restful/v3 v3.9.0 // indirect
  35 + github.com/fatih/color v1.15.0 // indirect
  36 + github.com/fsnotify/fsnotify v1.4.9 // indirect
  37 + github.com/garyburd/redigo v1.6.3 // indirect
  38 + github.com/gin-contrib/sse v0.1.0 // indirect
  39 + github.com/gin-gonic/gin v1.5.0 // indirect
  40 + github.com/go-logr/logr v1.2.3 // indirect
  41 + github.com/go-logr/stdr v1.2.2 // indirect
  42 + github.com/go-openapi/jsonpointer v0.19.6 // indirect
  43 + github.com/go-openapi/jsonreference v0.20.1 // indirect
  44 + github.com/go-openapi/swag v0.22.3 // indirect
  45 + github.com/go-playground/locales v0.12.1 // indirect
  46 + github.com/go-playground/universal-translator v0.16.0 // indirect
  47 + github.com/go-redis/redis/v8 v8.11.5 // indirect
  48 + github.com/go-sql-driver/mysql v1.7.1 // indirect
  49 + github.com/gogo/protobuf v1.3.2 // indirect
  50 + github.com/golang/mock v1.6.0 // indirect
  51 + github.com/golang/protobuf v1.5.3 // indirect
  52 + github.com/golang/snappy v0.0.4 // indirect
  53 + github.com/google/gnostic v0.5.7-v3refs // indirect
  54 + github.com/google/go-cmp v0.5.9 // indirect
  55 + github.com/google/gofuzz v1.2.0 // indirect
  56 + github.com/google/uuid v1.3.0 // indirect
  57 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 // indirect
  58 + github.com/hashicorp/errwrap v1.1.0 // indirect
  59 + github.com/hashicorp/go-multierror v1.1.1 // indirect
  60 + github.com/hashicorp/go-uuid v1.0.3 // indirect
  61 + github.com/hashicorp/hcl v1.0.0 // indirect
  62 + github.com/jackc/pgpassfile v1.0.0 // indirect
  63 + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
  64 + github.com/jackc/pgx/v5 v5.4.3 // indirect
  65 + github.com/jcmturner/aescts/v2 v2.0.0 // indirect
  66 + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
  67 + github.com/jcmturner/gofork v1.7.6 // indirect
  68 + github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect
  69 + github.com/jcmturner/rpc/v2 v2.0.3 // indirect
  70 + github.com/jinzhu/inflection v1.0.0 // indirect
  71 + github.com/josharian/intern v1.0.0 // indirect
  72 + github.com/json-iterator/go v1.1.12 // indirect
  73 + github.com/klauspost/compress v1.15.15 // indirect
  74 + github.com/leodido/go-urn v1.1.0 // indirect
  75 + github.com/magiconair/properties v1.8.0 // indirect
  76 + github.com/mailru/easyjson v0.7.7 // indirect
  77 + github.com/mattn/go-colorable v0.1.13 // indirect
  78 + github.com/mattn/go-isatty v0.0.17 // indirect
  79 + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
  80 + github.com/mitchellh/mapstructure v1.3.3 // indirect
  81 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
  82 + github.com/modern-go/reflect2 v1.0.2 // indirect
  83 + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
  84 + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
  85 + github.com/openzipkin/zipkin-go v0.4.1 // indirect
  86 + github.com/pelletier/go-toml v1.8.1 // indirect
  87 + github.com/pelletier/go-toml/v2 v2.0.9 // indirect
  88 + github.com/pierrec/lz4/v4 v4.1.17 // indirect
  89 + github.com/pmezard/go-difflib v1.0.0 // indirect
  90 + github.com/prometheus/client_golang v1.16.0 // indirect
  91 + github.com/prometheus/client_model v0.3.0 // indirect
  92 + github.com/prometheus/common v0.42.0 // indirect
  93 + github.com/prometheus/procfs v0.10.1 // indirect
  94 + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
  95 + github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
  96 + github.com/spaolacci/murmur3 v1.1.0 // indirect
  97 + github.com/spf13/afero v1.2.2 // indirect
  98 + github.com/spf13/cast v1.3.0 // indirect
  99 + github.com/spf13/jwalterweatherman v1.0.0 // indirect
  100 + github.com/spf13/pflag v1.0.5 // indirect
  101 + github.com/spf13/viper v1.4.0 // indirect
  102 + github.com/ugorji/go/codec v1.1.7 // indirect
  103 + go.etcd.io/etcd/api/v3 v3.5.9 // indirect
  104 + go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
  105 + go.etcd.io/etcd/client/v3 v3.5.9 // indirect
  106 + go.opentelemetry.io/otel v1.14.0 // indirect
  107 + go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect
  108 + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
  109 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
  110 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect
  111 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0 // indirect
  112 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 // indirect
  113 + go.opentelemetry.io/otel/exporters/zipkin v1.14.0 // indirect
  114 + go.opentelemetry.io/otel/sdk v1.14.0 // indirect
  115 + go.opentelemetry.io/otel/trace v1.14.0 // indirect
  116 + go.opentelemetry.io/proto/otlp v0.19.0 // indirect
  117 + go.uber.org/atomic v1.10.0 // indirect
  118 + go.uber.org/automaxprocs v1.5.3 // indirect
  119 + go.uber.org/multierr v1.9.0 // indirect
  120 + go.uber.org/zap v1.24.0 // indirect
  121 + golang.org/x/crypto v0.12.0 // indirect
  122 + golang.org/x/net v0.14.0 // indirect
  123 + golang.org/x/oauth2 v0.7.0 // indirect
  124 + golang.org/x/sys v0.11.0 // indirect
  125 + golang.org/x/term v0.11.0 // indirect
  126 + golang.org/x/text v0.12.0 // indirect
  127 + golang.org/x/time v0.3.0 // indirect
  128 + google.golang.org/appengine v1.6.7 // indirect
  129 + google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
  130 + google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
  131 + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
  132 + google.golang.org/protobuf v1.31.0 // indirect
  133 + gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
  134 + gopkg.in/inf.v0 v0.9.1 // indirect
  135 + gopkg.in/yaml.v2 v2.4.0 // indirect
  136 + gopkg.in/yaml.v3 v3.0.1 // indirect
  137 + k8s.io/api v0.26.3 // indirect
  138 + k8s.io/apimachinery v0.27.0-alpha.3 // indirect
  139 + k8s.io/client-go v0.26.3 // indirect
  140 + k8s.io/klog/v2 v2.90.1 // indirect
  141 + k8s.io/kube-openapi v0.0.0-20230307230338-69ee2d25a840 // indirect
  142 + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
  143 + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
  144 + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
  145 + sigs.k8s.io/yaml v1.3.0 // indirect
  146 +)
  1 +package cache
  2 +
  3 +import (
  4 + "github.com/tiptok/gocomm/pkg/cache"
  5 + "github.com/tiptok/gocomm/pkg/cache/gzcache"
  6 + "github.com/tiptok/gocomm/pkg/log"
  7 + "github.com/zeromicro/go-zero/core/logx"
  8 +)
  9 +
  10 +func NewMultiLevelCache(hosts []string, password string) *cache.MultiLevelCache {
  11 + logx.Infof("starting multi level cache...")
  12 + mlCache := cache.NewMultiLevelCacheNew(cache.WithDebugLog(true, func() log.Log {
  13 + return log.DefaultLog
  14 + }))
  15 + mlCache.RegisterCache(gzcache.NewClusterCache(hosts, password))
  16 + return mlCache
  17 +}
  18 +
  19 +func NewCachedRepository(c *cache.MultiLevelCache, options ...cache.QueryOption) *cache.CachedRepository {
  20 + return cache.NewCachedRepository(c, options...)
  21 +}
  1 +package config
  2 +
  3 +import (
  4 + "github.com/zeromicro/go-zero/core/stores/cache"
  5 + "github.com/zeromicro/go-zero/zrpc"
  6 + "time"
  7 +)
  8 +
  9 +type JWT struct {
  10 + Secret string `json:",optional"`
  11 + Expires time.Duration `json:",optional"`
  12 +}
  13 +type JwtAuth struct {
  14 + AccessSecret string
  15 + Expire int64
  16 +}
  17 +type Config struct {
  18 + JwtAuth JwtAuth `json:",optional"`
  19 + UserRpc zrpc.RpcClientConf `json:",optional"`
  20 + AuthRpc zrpc.RpcClientConf `json:",optional"`
  21 + PostRpc zrpc.RpcClientConf `json:",optional"`
  22 + CommentRpc zrpc.RpcClientConf `json:",optional"`
  23 + JWT JWT `json:",optional"`
  24 + DB struct {
  25 + DataSource string
  26 + } `json:",optional"`
  27 + Cache cache.CacheConf `json:",optional"`
  28 + DTM DTM `json:",optional"`
  29 + Sms Sms `json:",optional"`
  30 + Oss Oss `json:",optional"`
  31 + Wechat Wechat `json:",optional"` // 学员端微信
  32 + CoachClient Wechat `json:",optional"` // 教练端微信
  33 + OfficialAccount Wechat `json:",optional"`
  34 + ThirdWechatApps []Wechat `json:",optional"`
  35 +}
  36 +
  37 +type DTM struct {
  38 + Server Server `json:",optional"`
  39 +}
  40 +
  41 +type Server struct {
  42 + Name string `json:",optional"`
  43 + Host string `json:",optional"`
  44 + GRPC GRPC `json:",optional"`
  45 + HTTP HTTP `json:",optional"`
  46 + Metrics Metrics `json:",optional"`
  47 +}
  48 +
  49 +type HTTP struct {
  50 + Port string
  51 +}
  52 +
  53 +type GRPC struct {
  54 + Port string
  55 +}
  56 +
  57 +type Metrics struct {
  58 + Port string
  59 +}
  60 +
  61 +type Sms struct {
  62 + Debug bool
  63 + DebugCode string
  64 + Expire int `json:",default=180"`
  65 + MaxSendTime int `json:",default=5"`
  66 + CompanyName string
  67 + SecretId string
  68 + SecretKey string
  69 + SmsAppId string
  70 + Sign string
  71 + TemplateId string
  72 +}
  73 +
  74 +type Oss struct {
  75 + OssEndPoint string
  76 + AccessKeyID string
  77 + AccessKeySecret string
  78 + BuckName string
  79 +
  80 + RegionID string
  81 + RoleArn string
  82 +
  83 + CDN CDN
  84 +}
  85 +
  86 +type Wechat struct {
  87 + AppName string `json:",optional"`
  88 + AppID string
  89 + AppSecret string
  90 + MsgTemplates []Template `json:",optional"`
  91 +}
  92 +
  93 +func (wx Wechat) GetTemplate(code string) (Template, bool) {
  94 + for _, temp := range wx.MsgTemplates {
  95 + if temp.Code == code {
  96 + return temp, true
  97 + }
  98 + }
  99 + return Template{}, false
  100 +}
  101 +
  102 +type CDN struct {
  103 + HostPairs []string
  104 +}
  105 +
  106 +type Template struct {
  107 + ID string // 模板ID
  108 + Name string // 模板名称
  109 + Code string // 模板编码
  110 +}
  1 +package contextdata
  2 +
  3 +import (
  4 + "context"
  5 +)
  6 +
  7 +func GetTenantFromCtx(ctx context.Context) int64 {
  8 + userToken := GetUserTokenFromCtx(ctx)
  9 + if userToken.CompanyId == 0 {
  10 + return 0
  11 + }
  12 + return userToken.CompanyId
  13 +}
  1 +package contextdata
  2 +
  3 +import (
  4 + "context"
  5 + "encoding/json"
  6 + "github.com/golang-jwt/jwt/v4"
  7 + "github.com/zeromicro/go-zero/core/logx"
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/config"
  9 + "time"
  10 +)
  11 +
  12 +var (
  13 + CtxKeyJwtUserId = "userId"
  14 + CtxKeyJwtCompanyId = "companyId"
  15 +)
  16 +
  17 +func GetInt64FromCtx(ctx context.Context, key string) int64 {
  18 + var uid int64
  19 + if jsonUid, ok := ctx.Value(key).(json.Number); ok {
  20 + if int64Uid, err := jsonUid.Int64(); err == nil {
  21 + uid = int64Uid
  22 + } else {
  23 + logx.WithContext(ctx).Errorf("GetUidFromCtx err : %+v", err)
  24 + }
  25 + }
  26 + return uid
  27 +}
  28 +
  29 +func getStringFromCtx(ctx context.Context, key string) string {
  30 + var uid string
  31 + if jsonUid, ok := ctx.Value(key).(string); ok {
  32 + return jsonUid
  33 + }
  34 + return uid
  35 +}
  36 +
  37 +func getArrayInt64FromCtx(ctx context.Context, key string) []int64 {
  38 + values := ctx.Value(key)
  39 + var ids = make([]int64, 0)
  40 + if values == nil {
  41 + return ids
  42 + }
  43 + if list, ok := values.([]interface{}); ok {
  44 + for _, item := range list {
  45 + if jsonId, ok := item.(json.Number); ok {
  46 + id, _ := jsonId.Int64()
  47 + ids = append(ids, id)
  48 + }
  49 + }
  50 + }
  51 + return ids
  52 +}
  53 +
  54 +func GetUserTokenFromCtx(ctx context.Context) UserToken {
  55 + return UserToken{
  56 + UserId: GetInt64FromCtx(ctx, CtxKeyJwtUserId),
  57 + CompanyId: GetInt64FromCtx(ctx, CtxKeyJwtCompanyId),
  58 + }
  59 +}
  60 +
  61 +type UserToken struct {
  62 + UserId int64 `json:"userId"`
  63 + CompanyId int64 `json:"companyId"`
  64 +}
  65 +
  66 +func (tk UserToken) GenerateToken(jwtConfig config.JwtAuth) (string, error) {
  67 + claims := make(jwt.MapClaims)
  68 + claims["exp"] = time.Now().Unix() + jwtConfig.Expire
  69 + claims["iat"] = time.Now().Unix()
  70 + claims["UserId"] = tk.UserId
  71 + token := jwt.New(jwt.SigningMethodHS256)
  72 + token.Claims = claims
  73 +
  74 + return token.SignedString([]byte(jwtConfig.AccessSecret))
  75 +}
  76 +
  77 +func (tk *UserToken) ParseToken(jwtConfig config.JWT, str string) error {
  78 + return nil
  79 +}
  80 +
  81 +// CheckUserInfo 如果UserToken有效 返回:true 否则返回false
  82 +func (tk *UserToken) CheckUserInfo() bool {
  83 + return !(tk.UserId > 100000000 || tk.UserId <= 0)
  84 +}
  1 +package database
  2 +
  3 +import (
  4 + "context"
  5 + "fmt"
  6 + "github.com/zeromicro/go-zero/core/logx"
  7 + "gorm.io/driver/mysql"
  8 + "gorm.io/driver/postgres"
  9 + "gorm.io/gorm"
  10 + "gorm.io/gorm/logger"
  11 + "log"
  12 + "os"
  13 + "time"
  14 +)
  15 +
  16 +func OpenGormDB(source string) *gorm.DB {
  17 + newLogger := logger.New(
  18 + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
  19 + logger.Config{
  20 + SlowThreshold: time.Second, // Slow SQL threshold
  21 + LogLevel: logger.Info, // Log level
  22 + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
  23 + Colorful: false, // Disable color
  24 + },
  25 + )
  26 + fmt.Println("starting db...")
  27 + db, err := gorm.Open(mysql.Open(source), &gorm.Config{
  28 + Logger: newLogger,
  29 + })
  30 + if err != nil {
  31 + panic(err)
  32 + }
  33 + return db
  34 +}
  35 +
  36 +func OpenGormPGDB(source string, logMode string) *gorm.DB {
  37 + logx.Infof("starting db...")
  38 + db, err := gorm.Open(postgres.New(postgres.Config{
  39 + DSN: source,
  40 + PreferSimpleProtocol: true, // disables implicit prepared statement usage
  41 + }), &gorm.Config{
  42 + Logger: NewLogger(logMode), //newLogger,
  43 + })
  44 + if err != nil {
  45 + panic(err)
  46 + }
  47 + return db
  48 +}
  49 +
  50 +func NewLogger(logType string) logger.Interface {
  51 + if logType == "console" {
  52 + return logger.New(
  53 + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
  54 + logger.Config{
  55 + SlowThreshold: time.Second, // Slow SQL threshold
  56 + LogLevel: logger.Info, // Log level
  57 + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
  58 + Colorful: false, // Disable color
  59 + },
  60 + )
  61 + }
  62 + return ZeroLog{}
  63 +}
  64 +
  65 +type ZeroLog struct {
  66 +}
  67 +
  68 +func (l ZeroLog) LogMode(logger.LogLevel) logger.Interface {
  69 + return l
  70 +}
  71 +func (l ZeroLog) Info(ctx context.Context, s string, values ...interface{}) {
  72 + logx.Infof(s, values...)
  73 +}
  74 +func (l ZeroLog) Warn(ctx context.Context, s string, values ...interface{}) {
  75 + logx.Errorf(s, values...)
  76 +}
  77 +func (l ZeroLog) Error(ctx context.Context, s string, values ...interface{}) {
  78 + logx.Errorf(s, values...)
  79 +}
  80 +func (l ZeroLog) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
  81 + now := time.Now()
  82 + sql, rows := fc()
  83 + logx.Infof("[%v] [rows:%v] %s", now.Sub(begin).String(), rows, sql)
  84 +}
  1 +package database
  2 +
  3 +import (
  4 + "fmt"
  5 + "github.com/jinzhu/now"
  6 + "github.com/zeromicro/go-zero/core/stores/redis"
  7 + "gorm.io/gorm"
  8 + "gorm.io/gorm/clause"
  9 + "gorm.io/gorm/migrator"
  10 + "gorm.io/gorm/schema"
  11 + "strings"
  12 + "time"
  13 +)
  14 +
  15 +var (
  16 + // PartitionByRangeTime 按unix时间戳分区
  17 + PartitionByRangeTime = 1
  18 + // PartitionByHash 按系统的hash值分区
  19 + PartitionByHash = 2
  20 + // PartitionByList 按List包含值分区
  21 + PartitionByList = 3
  22 +)
  23 +
  24 +type PartitionTable interface {
  25 + TableName() string
  26 +}
  27 +
  28 +type PartitionMigrator struct {
  29 + ServiceName string
  30 + DB *gorm.DB
  31 + Redis *redis.Redis
  32 +}
  33 +
  34 +func NewPartitionMigrator(serviceName string, db *gorm.DB, redis *redis.Redis) *PartitionMigrator {
  35 + return &PartitionMigrator{
  36 + DB: db,
  37 + ServiceName: serviceName,
  38 + Redis: redis,
  39 + }
  40 +}
  41 +
  42 +func (c *PartitionMigrator) AutoMigrate(t PartitionTable, option ...PartitionOptionFunc) error {
  43 + options := NewPartitionOptions()
  44 + for i := range option {
  45 + option[i](options)
  46 + }
  47 +
  48 + tableName := t.TableName()
  49 + if !c.DB.Migrator().HasTable(tableName) {
  50 + migrator := Migrator{migrator.Migrator{
  51 + migrator.Config{
  52 + CreateIndexAfterCreateTable: true,
  53 + DB: c.DB,
  54 + Dialector: c.DB.Dialector,
  55 + },
  56 + }}
  57 + if err := migrator.CreatePartitionTable(options, t); err != nil {
  58 + panic(err)
  59 + }
  60 + }
  61 +
  62 + rk := fmt.Sprintf("%s:auto-partition:%s", c.ServiceName, tableName)
  63 + lock := redis.NewRedisLock(c.Redis, rk)
  64 + ok, err := lock.Acquire()
  65 + if !ok || err != nil {
  66 + return nil
  67 + }
  68 + defer lock.Release()
  69 + switch options.Type {
  70 + case PartitionByRangeTime:
  71 + begin := options.TimeBegin
  72 + end := options.TimeEnd
  73 + for {
  74 + if begin.Unix() > end.Unix() {
  75 + break
  76 + }
  77 + pTable := fmt.Sprintf("%s_%s", tableName, options.FormatTimeSubFunc(begin))
  78 + sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES FROM (%d) TO (%d);",
  79 + pTable, tableName, begin.Unix(), begin.AddDate(0, options.TimeSpanMonth, 0).Unix())
  80 + tx := c.DB.Exec(sql)
  81 + if tx.Error != nil {
  82 + return tx.Error
  83 + }
  84 + c.log(t, pTable)
  85 + begin = begin.AddDate(0, options.TimeSpanMonth, 0)
  86 + }
  87 + break
  88 + case PartitionByHash:
  89 + for i := 0; i < options.Modulus; i++ {
  90 + pTable := fmt.Sprintf("%s_%d", tableName, i)
  91 + sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES WITH (MODULUS %d, REMAINDER %d);",
  92 + pTable, tableName, options.Modulus, i)
  93 + tx := c.DB.Exec(sql)
  94 + if tx.Error != nil {
  95 + return tx.Error
  96 + }
  97 + c.log(t, pTable)
  98 + }
  99 + break
  100 + case PartitionByList:
  101 + for i := 0; i < len(options.ListRange); i++ {
  102 + pTable := fmt.Sprintf("%s_%d", tableName, i)
  103 + sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES IN %s;",
  104 + pTable, tableName, InArgs(options.ListRange[i]))
  105 + tx := c.DB.Exec(sql)
  106 + if tx.Error != nil {
  107 + return tx.Error
  108 + }
  109 + c.log(t, pTable)
  110 + }
  111 + break
  112 + default:
  113 + return nil
  114 + }
  115 +
  116 + return nil
  117 +}
  118 +
  119 +func (c *PartitionMigrator) log(t PartitionTable, pTable string) {
  120 + fmt.Println("【自动分区】 create partition table", pTable, "on table", t.TableName())
  121 +}
  122 +
  123 +type PartitionOptions struct {
  124 + // 分区类型 1:Hash 2:RangeTime
  125 + Type int
  126 + // 分区列
  127 + Column string
  128 +
  129 + // Hash分区
  130 + Modulus int
  131 +
  132 + // List 范围
  133 + ListRange []interface{}
  134 +
  135 + // Range时间分区
  136 + TimeBegin time.Time
  137 + TimeEnd time.Time
  138 + TimeSpanMonth int
  139 + FormatTimeSubFunc func(time.Time) string
  140 +
  141 + // 禁用PrimaryKey生成
  142 + // 分区字段有函数表达式的,需要禁用掉PrimaryKey,使用自定义的唯一ID生成规则
  143 + DisablePrimaryKey bool
  144 +}
  145 +
  146 +func NewPartitionOptions() *PartitionOptions {
  147 + return &PartitionOptions{
  148 + Type: PartitionByRangeTime,
  149 + FormatTimeSubFunc: func(t time.Time) string {
  150 + return t.Format("200601")
  151 + },
  152 + }
  153 +}
  154 +
  155 +func (c *PartitionOptions) Sql() string {
  156 + if c.Type == PartitionByHash {
  157 + return fmt.Sprintf("PARTITION BY HASH(%s)", c.Column)
  158 + }
  159 + if c.Type == PartitionByRangeTime {
  160 + return fmt.Sprintf("PARTITION BY RANGE(%s)", c.Column)
  161 + }
  162 + if c.Type == PartitionByList {
  163 + return fmt.Sprintf("PARTITION BY LIST(%s)", c.Column)
  164 + }
  165 + return ""
  166 +}
  167 +
  168 +type PartitionOptionFunc func(*PartitionOptions)
  169 +
  170 +func WithPartitionType(t int) PartitionOptionFunc {
  171 + return func(options *PartitionOptions) {
  172 + options.Type = t
  173 + }
  174 +}
  175 +
  176 +func WithPartitionColumn(c string) PartitionOptionFunc {
  177 + return func(options *PartitionOptions) {
  178 + options.Column = c
  179 + }
  180 +}
  181 +
  182 +func WithPartitionHash(modulus int) PartitionOptionFunc {
  183 + return func(options *PartitionOptions) {
  184 + options.Modulus = modulus
  185 + }
  186 +}
  187 +
  188 +func WithPartitionRangeTime(begin, end time.Time, spanMonth int) PartitionOptionFunc {
  189 + return func(options *PartitionOptions) {
  190 + options.TimeBegin = begin
  191 + options.TimeEnd = end
  192 + options.TimeSpanMonth = spanMonth
  193 + }
  194 +}
  195 +
  196 +func WithPartitionList(list ...interface{}) PartitionOptionFunc {
  197 + return func(options *PartitionOptions) {
  198 + options.ListRange = list
  199 + }
  200 +}
  201 +
  202 +func WithDisablePrimaryKey(disablePrimaryKey bool) PartitionOptionFunc {
  203 + return func(options *PartitionOptions) {
  204 + options.DisablePrimaryKey = disablePrimaryKey
  205 + }
  206 +}
  207 +
  208 +func Date(date string) time.Time {
  209 + return now.MustParse(date)
  210 +}
  211 +
  212 +type Migrator struct {
  213 + migrator.Migrator
  214 +}
  215 +
  216 +// CreatePartitionTable create table in database for values
  217 +func (m Migrator) CreatePartitionTable(options *PartitionOptions, values ...interface{}) error {
  218 + for _, value := range m.ReorderModels(values, false) {
  219 + tx := m.DB.Session(&gorm.Session{})
  220 + if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) {
  221 + var (
  222 + createTableSQL = "CREATE TABLE ? ("
  223 + values = []interface{}{m.CurrentTable(stmt)}
  224 + hasPrimaryKeyInDataType bool
  225 + )
  226 +
  227 + for _, dbName := range stmt.Schema.DBNames {
  228 + field := stmt.Schema.FieldsByDBName[dbName]
  229 + if !field.IgnoreMigration {
  230 + createTableSQL += "? ?"
  231 + hasPrimaryKeyInDataType = hasPrimaryKeyInDataType || strings.Contains(strings.ToUpper(string(field.DataType)), "PRIMARY KEY")
  232 + values = append(values, clause.Column{Name: dbName}, m.DB.Migrator().FullDataTypeOf(field))
  233 + createTableSQL += ","
  234 + }
  235 + }
  236 +
  237 + if !hasPrimaryKeyInDataType && len(stmt.Schema.PrimaryFields) > 0 && !options.DisablePrimaryKey {
  238 + createTableSQL += "PRIMARY KEY ?,"
  239 + primaryKeys := []interface{}{}
  240 + for _, field := range stmt.Schema.PrimaryFields {
  241 + primaryKeys = append(primaryKeys, clause.Column{Name: field.DBName})
  242 + }
  243 +
  244 + values = append(values, primaryKeys)
  245 + }
  246 +
  247 + for _, idx := range stmt.Schema.ParseIndexes() {
  248 + if m.CreateIndexAfterCreateTable {
  249 + defer func(value interface{}, name string) {
  250 + if errr == nil {
  251 + errr = tx.Migrator().CreateIndex(value, name)
  252 + }
  253 + }(value, idx.Name)
  254 + } else {
  255 + if idx.Class != "" {
  256 + createTableSQL += idx.Class + " "
  257 + }
  258 + createTableSQL += "INDEX ? ?"
  259 +
  260 + if idx.Comment != "" {
  261 + createTableSQL += fmt.Sprintf(" COMMENT '%s'", idx.Comment)
  262 + }
  263 +
  264 + if idx.Option != "" {
  265 + createTableSQL += " " + idx.Option
  266 + }
  267 +
  268 + createTableSQL += ","
  269 + values = append(values, clause.Column{Name: idx.Name}, tx.Migrator().(migrator.BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt))
  270 + }
  271 + }
  272 +
  273 + if !m.DB.DisableForeignKeyConstraintWhenMigrating && !m.DB.IgnoreRelationshipsWhenMigrating {
  274 + for _, rel := range stmt.Schema.Relationships.Relations {
  275 + if rel.Field.IgnoreMigration {
  276 + continue
  277 + }
  278 + if constraint := rel.ParseConstraint(); constraint != nil {
  279 + if constraint.Schema == stmt.Schema {
  280 + sql, vars := buildConstraint(constraint)
  281 + createTableSQL += sql + ","
  282 + values = append(values, vars...)
  283 + }
  284 + }
  285 + }
  286 + }
  287 +
  288 + for _, chk := range stmt.Schema.ParseCheckConstraints() {
  289 + createTableSQL += "CONSTRAINT ? CHECK (?),"
  290 + values = append(values, clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint})
  291 + }
  292 +
  293 + createTableSQL = strings.TrimSuffix(createTableSQL, ",")
  294 +
  295 + createTableSQL += ")"
  296 +
  297 + if options != nil {
  298 + createTableSQL += options.Sql()
  299 + }
  300 +
  301 + if tableOption, ok := m.DB.Get("gorm:table_options"); ok {
  302 + createTableSQL += fmt.Sprint(tableOption)
  303 + }
  304 +
  305 + errr = tx.Exec(createTableSQL, values...).Error
  306 + return errr
  307 + }); err != nil {
  308 + return err
  309 + }
  310 + }
  311 + return nil
  312 +}
  313 +
  314 +func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) {
  315 + sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??"
  316 + if constraint.OnDelete != "" {
  317 + sql += " ON DELETE " + constraint.OnDelete
  318 + }
  319 +
  320 + if constraint.OnUpdate != "" {
  321 + sql += " ON UPDATE " + constraint.OnUpdate
  322 + }
  323 +
  324 + var foreignKeys, references []interface{}
  325 + for _, field := range constraint.ForeignKeys {
  326 + foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName})
  327 + }
  328 +
  329 + for _, field := range constraint.References {
  330 + references = append(references, clause.Column{Name: field.DBName})
  331 + }
  332 + results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references)
  333 + return
  334 +}
  1 +package database
  2 +
  3 +import (
  4 + "github.com/zeromicro/go-zero/core/mapping"
  5 + "reflect"
  6 +)
  7 +
  8 +func InArgs(args interface{}) string {
  9 + bytes := make([]byte, 0)
  10 + bytes = appendIn(bytes, reflect.ValueOf(args))
  11 + return string(bytes)
  12 +}
  13 +
  14 +func Arg(args interface{}) string {
  15 + bytes := make([]byte, 0)
  16 + v := reflect.ValueOf(args)
  17 + bytes = appendValue(bytes, v)
  18 + return string(bytes)
  19 +}
  20 +
  21 +func appendIn(b []byte, slice reflect.Value) []byte {
  22 + sliceLen := slice.Len()
  23 + b = append(b, '(')
  24 + for i := 0; i < sliceLen; i++ {
  25 + if i > 0 {
  26 + b = append(b, ',')
  27 + }
  28 +
  29 + elem := slice.Index(i)
  30 + if elem.Kind() == reflect.Interface {
  31 + elem = elem.Elem()
  32 + }
  33 + if elem.Kind() == reflect.Slice {
  34 + //b = appendIn(b, elem)
  35 + } else {
  36 + b = appendValue(b, elem)
  37 + }
  38 + }
  39 + b = append(b, ')')
  40 + return b
  41 +}
  42 +
  43 +func appendValue(b []byte, v reflect.Value) []byte {
  44 + if v.Kind() == reflect.Ptr && v.IsNil() {
  45 +
  46 + return append(b, "NULL"...)
  47 + }
  48 + if v.Kind() == reflect.Int || v.Kind() == reflect.Int64 || v.Kind() == reflect.Float64 {
  49 + return append(b, []byte(mapping.Repr(v.Interface()))...)
  50 + }
  51 + b = append(b, []byte("'")...)
  52 + b = append(b, []byte(mapping.Repr(v.Interface()))...)
  53 + b = append(b, []byte("'")...)
  54 + return b
  55 +}
  1 +package result
  2 +
  3 +import (
  4 + "fmt"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/xerr"
  6 + "net/http"
  7 +
  8 + "github.com/pkg/errors"
  9 + "github.com/zeromicro/go-zero/core/logx"
  10 + "github.com/zeromicro/go-zero/rest/httpx"
  11 + "google.golang.org/grpc/status"
  12 +)
  13 +
  14 +// http返回
  15 +func HttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
  16 +
  17 + if err == nil {
  18 + //成功返回
  19 + r := Success(resp)
  20 + httpx.WriteJson(w, http.StatusOK, r)
  21 + } else {
  22 + //错误返回
  23 + errcode := xerr.SERVER_COMMON_ERROR
  24 + errmsg := "服务器开小差啦,稍后再来试一试"
  25 + internalErr := ""
  26 + causeErr := errors.Cause(err) // err类型
  27 + if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型
  28 + //自定义CodeError
  29 + errcode = e.GetErrCode()
  30 + errmsg = e.GetErrMsg()
  31 + if e.InternalError != nil {
  32 + internalErr = e.InternalError.Error()
  33 + }
  34 + } else {
  35 + if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
  36 + grpcCode := uint32(gstatus.Code())
  37 + if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端
  38 + errcode = grpcCode
  39 + errmsg = gstatus.Message()
  40 + }
  41 + }
  42 + }
  43 +
  44 + logx.WithContext(r.Context()).Errorf("【API-ERR】 : %+v ", err)
  45 + response := Error(errcode, errmsg)
  46 + response.Error = internalErr
  47 + httpx.WriteJson(w, http.StatusOK, response)
  48 + }
  49 +}
  50 +
  51 +// 授权的http方法
  52 +func AuthHttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
  53 +
  54 + if err == nil {
  55 + //成功返回
  56 + r := Success(resp)
  57 + httpx.WriteJson(w, http.StatusOK, r)
  58 + } else {
  59 + //错误返回
  60 + errcode := xerr.SERVER_COMMON_ERROR
  61 + errmsg := "服务器开小差啦,稍后再来试一试"
  62 +
  63 + causeErr := errors.Cause(err) // err类型
  64 + if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型
  65 + //自定义CodeError
  66 + errcode = e.GetErrCode()
  67 + errmsg = e.GetErrMsg()
  68 + } else {
  69 + if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
  70 + grpcCode := uint32(gstatus.Code())
  71 + if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端
  72 + errcode = grpcCode
  73 + errmsg = gstatus.Message()
  74 + }
  75 + }
  76 + }
  77 +
  78 + logx.WithContext(r.Context()).Errorf("【GATEWAY-ERR】 : %+v ", err)
  79 +
  80 + httpx.WriteJson(w, http.StatusUnauthorized, Error(errcode, errmsg))
  81 + }
  82 +}
  83 +
  84 +// http 参数错误返回
  85 +func ParamErrorResult(r *http.Request, w http.ResponseWriter, err error) {
  86 + errMsg := fmt.Sprintf("%s ,%s", xerr.MapErrMsg(xerr.REUQEST_PARAM_ERROR), err.Error())
  87 + httpx.WriteJson(w, http.StatusBadRequest, Error(xerr.REUQEST_PARAM_ERROR, errMsg))
  88 +}
  1 +package result
  2 +
  3 +import (
  4 + "context"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/xerr"
  6 +
  7 + "github.com/pkg/errors"
  8 + "github.com/zeromicro/go-zero/core/logx"
  9 + "google.golang.org/grpc/status"
  10 +)
  11 +
  12 +// job返回
  13 +func JobResult(ctx context.Context, resp interface{}, err error) {
  14 + if err == nil {
  15 + // 成功返回 ,只有dev环境下才会打印info,线上不显示
  16 + if resp != nil {
  17 + logx.Infof("resp: %+v", resp)
  18 + }
  19 + return
  20 + } else {
  21 + errCode := xerr.SERVER_COMMON_ERROR
  22 + errMsg := "服务器开小差啦,稍后再来试一试"
  23 +
  24 + // 错误返回
  25 + causeErr := errors.Cause(err) // err类型
  26 + if e, ok := causeErr.(*xerr.CodeError); ok { // 自定义错误类型
  27 + // 自定义CodeError
  28 + errCode = e.GetErrCode()
  29 + errMsg = e.GetErrMsg()
  30 + } else {
  31 + if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
  32 + grpcCode := uint32(gstatus.Code())
  33 + if xerr.IsCodeErr(grpcCode) { // 区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端
  34 + errCode = grpcCode
  35 + errMsg = gstatus.Message()
  36 + }
  37 + }
  38 + }
  39 +
  40 + logx.WithContext(ctx).Errorf("【JOB-ERR】 : %+v ,errCode:%d , errMsg:%s ", err, errCode, errMsg)
  41 + return
  42 + }
  43 +}
  1 +package result
  2 +
  3 +type ResponseSuccessBean struct {
  4 + Code uint32 `json:"code"`
  5 + Msg string `json:"msg"`
  6 + Data interface{} `json:"data"`
  7 +}
  8 +type NullJson struct{}
  9 +
  10 +func Success(data interface{}) *ResponseSuccessBean {
  11 + return &ResponseSuccessBean{Code: 0, Msg: "OK", Data: data}
  12 +}
  13 +
  14 +type ResponseErrorBean struct {
  15 + Code uint32 `json:"code"`
  16 + Msg string `json:"msg"`
  17 + Error string `json:"err"`
  18 +}
  19 +
  20 +func Error(errCode uint32, errMsg string) *ResponseErrorBean {
  21 + return &ResponseErrorBean{Code: errCode, Msg: errMsg}
  22 +}
  1 +package tool
  2 +
  3 +import (
  4 + "crypto/md5"
  5 + "fmt"
  6 + "io"
  7 +)
  8 +
  9 +/** 加密方式 **/
  10 +
  11 +func Md5ByString(str string) string {
  12 + m := md5.New()
  13 + _, err := io.WriteString(m, str)
  14 + if err != nil {
  15 + panic(err)
  16 + }
  17 + arr := m.Sum(nil)
  18 + return fmt.Sprintf("%x", arr)
  19 +}
  20 +
  21 +func Md5ByBytes(b []byte) string {
  22 + return fmt.Sprintf("%x", md5.Sum(b))
  23 +}
  1 +package tool
  2 +
  3 +import (
  4 + "path/filepath"
  5 + "strings"
  6 +)
  7 +
  8 +const (
  9 + Image = "image"
  10 + Video = "video"
  11 +)
  12 +
  13 +var TypeMap = map[string]string{
  14 + "jpg": Image,
  15 + "png": Image,
  16 + "gif": Image,
  17 + "webp": Image,
  18 + "cr2": Image,
  19 + "tif": Image,
  20 + "bmp": Image,
  21 + "heif": Image,
  22 + "jxr": Image,
  23 + "psd": Image,
  24 + "ico": Image,
  25 + "dwg": Image,
  26 + "avif": Image,
  27 +
  28 + "mp4": Video,
  29 + "m4v": Video,
  30 + "mkv": Video,
  31 + "webm": Video,
  32 + "mov": Video,
  33 + "avi": Video,
  34 + "wmv": Video,
  35 + "mpg": Video,
  36 + "flv": Video,
  37 + "3gp": Video,
  38 +}
  39 +var DefaultFileTypeDetector = FileTypeDetector{}
  40 +
  41 +type FileTypeDetector struct {
  42 +}
  43 +
  44 +func (c FileTypeDetector) Classify(medias []string, mediaType string) []string {
  45 + result := make([]string, 0)
  46 + for _, media := range medias {
  47 + v, ok := TypeMap[strings.Trim(filepath.Ext(media), ".")]
  48 + if !ok {
  49 + continue
  50 + }
  51 + if v == mediaType {
  52 + result = append(result, media)
  53 + }
  54 + }
  55 + return result
  56 +}
  1 +package tool
  2 +
  3 +import (
  4 + jwt "github.com/golang-jwt/jwt/v4"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/config"
  6 + "time"
  7 +)
  8 +
  9 +type UserToken struct {
  10 + UserId int64 `json:"userId"`
  11 + CoachId int64 `json:"coach_id"`
  12 + AdminId int64 `json:"adminId"`
  13 + ClientType string `json:"clientType"`
  14 + AccessShops []int64 `json:"accessShops"`
  15 +}
  16 +
  17 +func (tk UserToken) GenerateToken(jwtConfig config.JwtAuth) (string, error) {
  18 + claims := make(jwt.MapClaims)
  19 + claims["exp"] = time.Now().Unix() + jwtConfig.Expire
  20 + claims["iat"] = time.Now().Unix()
  21 + claims["UserId"] = tk.UserId
  22 + claims["CoachId"] = tk.CoachId
  23 + claims["AdminId"] = tk.AdminId
  24 + claims["ClientType"] = tk.ClientType
  25 + claims["AccessShops"] = tk.AccessShops
  26 + token := jwt.New(jwt.SigningMethodHS256)
  27 + token.Claims = claims
  28 +
  29 + return token.SignedString([]byte(jwtConfig.AccessSecret))
  30 +}
  31 +
  32 +func (tk *UserToken) ParseToken(jwtConfig config.JWT, str string) error {
  33 + //tokenClaims, err := jwt.ParseWithClaims(
  34 + // str,
  35 + // tk,
  36 + // func(token *jwt.Token) (interface{}, error) {
  37 + // return []byte(jwtConfig.Secret), nil
  38 + // })
  39 + //if err != nil {
  40 + // return err
  41 + //}
  42 + //if claim, ok := tokenClaims.Claims.(*UserToken); ok && tokenClaims.Valid {
  43 + // *tk = *claim
  44 + // return nil
  45 + //}
  46 + //return errors.New("token 解析失败")
  47 + return nil
  48 +}
  49 +
  50 +// CheckUserInfo 如果UserToken有效 返回:true 否则返回false
  51 +func (tk *UserToken) CheckUserInfo() bool {
  52 + return !(tk.UserId > 100000000 || tk.UserId <= 0)
  53 +}
  1 +package tool
  2 +
  3 +import (
  4 + "math/rand"
  5 + "time"
  6 +)
  7 +
  8 +const (
  9 + KC_RAND_KIND_NUM = 0 // 纯数字
  10 + KC_RAND_KIND_LOWER = 1 // 小写字母
  11 + KC_RAND_KIND_UPPER = 2 // 大写字母
  12 + KC_RAND_KIND_ALL = 3 // 数字、大小写字母
  13 +)
  14 +
  15 +// 随机字符串
  16 +func Krand(size int, kind int) string {
  17 + ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
  18 + is_all := kind > 2 || kind < 0
  19 + rand.Seed(time.Now().UnixNano())
  20 + for i := 0; i < size; i++ {
  21 + if is_all { // random ikind
  22 + ikind = rand.Intn(3)
  23 + }
  24 + scope, base := kinds[ikind][0], kinds[ikind][1]
  25 + result[i] = uint8(base + rand.Intn(scope))
  26 + }
  27 + return string(result)
  28 +}
  1 +package xcollection
  2 +
  3 +type TreeNode interface {
  4 + PID() string
  5 + ID() string
  6 +}
  7 +
  8 +type Tree struct {
  9 + Node TreeNode `json:"chart"`
  10 + Nodes []*Tree `json:"charts"`
  11 +}
  12 +
  13 +func NewTree(nodes []TreeNode) *Tree {
  14 + var tree = &Tree{
  15 + Node: nil,
  16 + Nodes: make([]*Tree, 0),
  17 + }
  18 + for i := range nodes {
  19 + match := traverseAdd(tree, nodes[i])
  20 + if !match {
  21 + tree.Nodes = append(tree.Nodes, newTree(nodes[i]))
  22 + }
  23 + }
  24 + return tree
  25 +}
  26 +
  27 +func newTree(node TreeNode) *Tree {
  28 + return &Tree{
  29 + Node: node,
  30 + Nodes: make([]*Tree, 0),
  31 + }
  32 +}
  33 +
  34 +func (tree *Tree) Root() TreeNode {
  35 + if tree.Node != nil {
  36 + return tree.Node
  37 + }
  38 + if len(tree.Nodes) > 0 {
  39 + return tree.Nodes[0].Node
  40 + }
  41 + return nil
  42 +}
  43 +
  44 +// TreeNodePaths returns all the parents of the current node 1->5->7 , use time n*O(n)(need performance optimization)
  45 +func (tree *Tree) TreeNodePaths(node TreeNode) []TreeNode {
  46 + treeNode := node
  47 + result := make([]TreeNode, 0)
  48 + for {
  49 + if treeNode == nil {
  50 + break
  51 + }
  52 + tmp := tree.find(treeNode, func(a, b TreeNode) bool {
  53 + if a.ID() == b.PID() {
  54 + return true
  55 + }
  56 + return false
  57 + })
  58 + result = append(result, treeNode)
  59 + if tmp == nil {
  60 + break
  61 + }
  62 + treeNode = tmp.Node
  63 + }
  64 + reserveResult := make([]TreeNode, 0)
  65 + for i := len(result) - 1; i >= 0; i-- {
  66 + reserveResult = append(reserveResult, result[i])
  67 + }
  68 + return reserveResult
  69 +}
  70 +
  71 +// Add adds a node to the first matching parent tree if add success it return true
  72 +func (tree *Tree) Add(node TreeNode) bool {
  73 + return traverseAdd(tree, node)
  74 +}
  75 +
  76 +// AllChildNode returns all child nodes under Node, including itself
  77 +func (tree *Tree) AllChildNode(node TreeNode) []TreeNode {
  78 + treeNode := tree.find(node, nil)
  79 + if treeNode == nil {
  80 + return []TreeNode{}
  81 + }
  82 + return tree.allChildNode(treeNode, nil)
  83 +}
  84 +
  85 +// AllLeafNode returns all leaf node under Node ,if node is nil returns all leaf node under tree
  86 +func (tree *Tree) AllLeafNode(node TreeNode) []TreeNode {
  87 + treeNode := tree
  88 + if node != nil {
  89 + treeNode = tree.find(node, nil)
  90 + }
  91 + if treeNode == nil {
  92 + return []TreeNode{}
  93 + }
  94 + return tree.allChildNode(treeNode, func(node *Tree) bool {
  95 + if len(node.Nodes) == 0 {
  96 + return true
  97 + }
  98 + return false
  99 + })
  100 +}
  101 +
  102 +// Depth returns all child nodes under depth depth=[1:n]
  103 +func (tree *Tree) Depth(depth int) []TreeNode {
  104 + treeNode := tree.find(tree.Root(), nil)
  105 + if treeNode == nil {
  106 + return []TreeNode{}
  107 + }
  108 + return tree.allChildByDepth(treeNode, depth)
  109 +}
  110 +
  111 +// AllChildNodeByDepth returns all child nodes under depth Node
  112 +func (tree *Tree) AllChildNodeByDepth(node TreeNode, depth int) []TreeNode {
  113 + treeNode := tree.find(node, nil)
  114 + if treeNode == nil {
  115 + return []TreeNode{}
  116 + }
  117 + return tree.allChildByDepth(treeNode, depth)
  118 +}
  119 +
  120 +// Find query the node in this tree
  121 +func (tree *Tree) Find(node TreeNode, compared func(a, b TreeNode) bool) *Tree {
  122 + return tree.find(node, compared)
  123 +}
  124 +
  125 +// find query the node in this tree
  126 +func (tree *Tree) find(node TreeNode, compared func(a, b TreeNode) bool) *Tree {
  127 + var stack []*Tree
  128 + stack = append(stack, tree)
  129 + var find *Tree
  130 + for {
  131 + if len(stack) == 0 {
  132 + break
  133 + }
  134 + pop := stack[0]
  135 + stack = stack[1:]
  136 + stack = append(stack, pop.Nodes...)
  137 + if pop == nil || pop.Node == nil {
  138 + continue
  139 + }
  140 + if compared != nil {
  141 + if compared(pop.Node, node) {
  142 + find = pop
  143 + break
  144 + }
  145 + continue
  146 + }
  147 + if pop.Node.ID() == node.ID() {
  148 + find = pop
  149 + break
  150 + }
  151 + }
  152 + return find
  153 +}
  154 +
  155 +// allChildNode 返回treeNode下所有子节点
  156 +func (tree *Tree) allChildNode(treeNode *Tree, filter func(node *Tree) bool) []TreeNode {
  157 + var stack []*Tree
  158 + stack = append(stack, treeNode)
  159 + var res []TreeNode
  160 + for {
  161 + if len(stack) == 0 {
  162 + break
  163 + }
  164 + pop := stack[0]
  165 + stack = stack[1:]
  166 + stack = append(stack, pop.Nodes...)
  167 + if filter != nil && !filter(pop) {
  168 + continue
  169 + }
  170 + res = append(res, pop.Node)
  171 + }
  172 + return res
  173 +}
  174 +
  175 +// traverseAdd 递归添加
  176 +//
  177 +// tree 当前树
  178 +// node 判断的节点
  179 +func traverseAdd(tree *Tree, node TreeNode) bool {
  180 + list := tree.Nodes
  181 + var match bool = false
  182 + for i := range list {
  183 + id, pid := list[i].Node.ID(), node.PID()
  184 + if pid == id {
  185 + list[i].Nodes = append(list[i].Nodes, newTree(node))
  186 + return true
  187 + }
  188 + if match || traverseAdd(list[i], node) {
  189 + match = true
  190 + break
  191 + }
  192 + }
  193 + return match
  194 +}
  195 +
  196 +// allChildByDepth 返回treeNode下指定深度的所有子节点 depth=[1:n]
  197 +func (tree *Tree) allChildByDepth(treeNode *Tree, depth int) []TreeNode {
  198 + var stack []*Tree
  199 + stack = append(stack, treeNode)
  200 + var res []TreeNode
  201 + if depth <= 0 {
  202 + return res
  203 + }
  204 + if treeNode.Root() != nil && depth == 1 {
  205 + return []TreeNode{treeNode.Root()}
  206 + }
  207 + curDepth := 1
  208 + var depthStack []*Tree
  209 + for {
  210 + if len(stack) == 0 {
  211 + break
  212 + }
  213 + pop := stack[0]
  214 + stack = stack[1:]
  215 + depthStack = append(depthStack, pop.Nodes...)
  216 + if len(stack) == 0 {
  217 + curDepth++
  218 + stack = depthStack[:]
  219 + depthStack = []*Tree{}
  220 + if curDepth == depth {
  221 + for i := range stack {
  222 + res = append(res, stack[i].Node)
  223 + }
  224 + break
  225 + }
  226 + }
  227 + }
  228 + return res
  229 +}
  1 +package xcollection
  2 +
  3 +import (
  4 + "github.com/stretchr/testify/assert"
  5 + "strconv"
  6 + "testing"
  7 +)
  8 +
  9 +func prepare() []struct {
  10 + Input []TreeNode
  11 + Text string
  12 + Except []string
  13 + Except2 []string
  14 +} {
  15 + return []struct {
  16 + Input []TreeNode
  17 + Text string
  18 + Except []string
  19 + Except2 []string
  20 + }{
  21 + {
  22 + Input: []TreeNode{
  23 + &st{Id: 1, Pid: 0},
  24 + &st{Id: 2, Pid: 1}, &st{Id: 3, Pid: 1}, &st{Id: 4, Pid: 1},
  25 + &st{Id: 5, Pid: 3},
  26 + &st{Id: 6, Pid: 5}, &st{Id: 7, Pid: 5}},
  27 + Text: `
  28 +树形结构:
  29 + 1
  30 +2 3 4
  31 + 5
  32 + 6 7
  33 +`,
  34 + Except: []string{"5", "6", "7"},
  35 + Except2: []string{"2", "4", "6", "7"},
  36 + },
  37 + }
  38 +}
  39 +
  40 +func Test_Tree(t *testing.T) {
  41 + table := prepare()
  42 + for i := range table {
  43 + tree := NewTree(table[i].Input)
  44 + out := tree.AllChildNode(&st{Id: 5, Pid: 3})
  45 + var res []string = treeNodeResults(out)
  46 + assert.Equal(t, res, table[i].Except)
  47 +
  48 + out = tree.AllLeafNode(nil) //tree.Root()
  49 + res = treeNodeResults(out)
  50 + assert.Equal(t, res, table[i].Except2)
  51 +
  52 + root := tree.Root()
  53 + assert.Equal(t, root.ID(), "1")
  54 +
  55 + //tree.Add(&st{Id:10,Pid: 7})
  56 + //
  57 + //out = tree.AllLeafNode(tree.Root())
  58 + //res = treeNodeResults(out)
  59 + //assert.Equal(t, res, []string{"2", "4", "6", "10"})
  60 +
  61 + out = tree.TreeNodePaths(&st{Id: 7, Pid: 5})
  62 + res = treeNodeResults(out)
  63 + assert.Equal(t, res, []string{"1", "3", "5", "7"})
  64 +
  65 + }
  66 +}
  67 +
  68 +func Test_TreeNodeByDepth(t *testing.T) {
  69 + input := []TreeNode{
  70 + &st{Id: 1, Pid: 0},
  71 + &st{Id: 2, Pid: 1}, &st{Id: 3, Pid: 1}, &st{Id: 4, Pid: 1},
  72 + &st{Id: 5, Pid: 3},
  73 + &st{Id: 6, Pid: 5}, &st{Id: 7, Pid: 5},
  74 + &st{Id: 8, Pid: 6}, &st{Id: 9, Pid: 6}, &st{Id: 10, Pid: 6}, &st{Id: 11, Pid: 7}, &st{Id: 12, Pid: 7},
  75 + }
  76 +
  77 + tree := NewTree(input)
  78 + /*
  79 + 树形结构:
  80 + 1
  81 + 2 3 4
  82 + 5
  83 + 6 7
  84 + 8 9 10 11 12
  85 + */
  86 + var out []TreeNode
  87 + var res []string
  88 + out = tree.AllChildNodeByDepth(&st{Id: 5, Pid: 3}, 2)
  89 + res = treeNodeResults(out)
  90 + assert.Equal(t, []string{"6", "7"}, res)
  91 + out = tree.AllChildNodeByDepth(tree.Root(), 1)
  92 + res = treeNodeResults(out)
  93 + assert.Equal(t, []string{"1"}, res)
  94 + out = tree.AllChildNodeByDepth(tree.Root(), 2)
  95 + res = treeNodeResults(out)
  96 + assert.Equal(t, []string{"2", "3", "4"}, res)
  97 + out = tree.AllChildNodeByDepth(tree.Root(), 3)
  98 + res = treeNodeResults(out)
  99 + assert.Equal(t, []string{"5"}, res)
  100 + out = tree.AllChildNodeByDepth(tree.Root(), 4)
  101 + res = treeNodeResults(out)
  102 + assert.Equal(t, []string{"6", "7"}, res)
  103 + out = tree.AllChildNodeByDepth(tree.Root(), 5)
  104 + res = treeNodeResults(out)
  105 + assert.Equal(t, []string{"8", "9", "10", "11", "12"}, res)
  106 +}
  107 +
  108 +type st struct {
  109 + Id int
  110 + Pid int
  111 +}
  112 +
  113 +func (t *st) PID() string {
  114 + return strconv.Itoa(t.Pid)
  115 +}
  116 +func (t *st) ID() string {
  117 + return strconv.Itoa(t.Id)
  118 +}
  119 +
  120 +func treeNodeResults(nodes []TreeNode) []string {
  121 + var res []string
  122 + for i := range nodes {
  123 + res = append(res, nodes[i].ID())
  124 + }
  125 + return res
  126 +}
  1 +package xerr
  2 +
  3 +// 成功返回
  4 +const OK uint32 = 200
  5 +
  6 +/**(前3位代表业务,后三位代表具体功能)**/
  7 +
  8 +// 全局错误码
  9 +const SERVER_COMMON_ERROR uint32 = 100001
  10 +const REUQEST_PARAM_ERROR uint32 = 100002
  11 +const TOKEN_EXPIRE_ERROR uint32 = 100003
  12 +const TOKEN_GENERATE_ERROR uint32 = 100004
  13 +const DB_ERROR uint32 = 100005
  14 +const DB_UPDATE_AFFECTED_ZERO_ERROR uint32 = 100006
  15 +
  16 +const REQUEST_ARGS_ERROR = 200001
  17 +
  18 +// 微信模块
  19 +const ErrWxMiniAuthFailError uint32 = 500001
  20 +const ErrUserNoAuth uint32 = 500002
  1 +package xerr
  2 +
  3 +var message map[uint32]string
  4 +
  5 +func init() {
  6 + message = make(map[uint32]string)
  7 + message[OK] = "SUCCESS"
  8 + message[SERVER_COMMON_ERROR] = "服务器开小差啦,稍后再来试一试"
  9 + message[REUQEST_PARAM_ERROR] = "参数错误"
  10 + message[TOKEN_EXPIRE_ERROR] = "token失效,请重新登陆"
  11 + message[TOKEN_GENERATE_ERROR] = "生成token失败"
  12 + message[DB_ERROR] = "数据库繁忙,请稍后再试"
  13 + message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0"
  14 + message[ErrUserNoAuth] = "无权限"
  15 + message[ErrWxMiniAuthFailError] = "微信授权失败"
  16 +}
  17 +
  18 +func MapErrMsg(errcode uint32) string {
  19 + if msg, ok := message[errcode]; ok {
  20 + return msg
  21 + } else {
  22 + return "服务器开小差啦,稍后再来试一试"
  23 + }
  24 +}
  25 +
  26 +func IsCodeErr(errcode uint32) bool {
  27 + if _, ok := message[errcode]; ok {
  28 + return true
  29 + } else {
  30 + return false
  31 + }
  32 +}
  1 +package xerr
  2 +
  3 +import (
  4 + "fmt"
  5 +)
  6 +
  7 +/**
  8 +常用通用固定错误
  9 +*/
  10 +
  11 +type CodeError struct {
  12 + errCode uint32
  13 + errMsg string
  14 + InternalError error
  15 +}
  16 +
  17 +// GetErrCode 返回给前端的错误码
  18 +func (e *CodeError) GetErrCode() uint32 {
  19 + return e.errCode
  20 +}
  21 +
  22 +// GetErrMsg 返回给前端显示端错误信息
  23 +func (e *CodeError) GetErrMsg() string {
  24 + if e.errMsg == "" {
  25 + return MapErrMsg(e.errCode)
  26 + }
  27 + return e.errMsg
  28 +}
  29 +
  30 +func (e *CodeError) Error() string {
  31 + if e.InternalError != nil {
  32 + return fmt.Sprintf("ErrCode:%d,ErrMsg:%s InternalError:%s", e.errCode, e.errMsg, e.InternalError.Error())
  33 + }
  34 + return fmt.Sprintf("ErrCode:%d,ErrMsg:%s", e.errCode, e.errMsg)
  35 +}
  36 +
  37 +/*
  38 + 指定错误码的错误
  39 +*/
  40 +
  41 +func NewCodeErr(errCode uint32, err error) *CodeError {
  42 + return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode), InternalError: err}
  43 +}
  44 +
  45 +func NewCodeErrMsg(errCode uint32, err error, msg string) *CodeError {
  46 + return &CodeError{errCode: errCode, errMsg: msg, InternalError: err}
  47 +}
  48 +
  49 +/*
  50 + 默认的服务错误
  51 +*/
  52 +
  53 +func NewErr(err error) *CodeError {
  54 + return &CodeError{errCode: SERVER_COMMON_ERROR, InternalError: err}
  55 +}
  56 +
  57 +func NewErrMsg(errMsg string) *CodeError {
  58 + return &CodeError{errCode: SERVER_COMMON_ERROR, errMsg: errMsg}
  59 +}
  60 +
  61 +func NewErrMsgErr(errMsg string, err error) *CodeError {
  62 + return &CodeError{errCode: SERVER_COMMON_ERROR, errMsg: errMsg, InternalError: err}
  63 +}