作者 yangfu

init

正在显示 56 个修改的文件 包含 2842 行增加0 行删除
# Compiled Object codefiles, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
.log
.idea
.vscode
app.log
go.sum
lastupdate.tmp
*.log
public/*
logs/
\ No newline at end of file
... ...
syntax = "v1"
info(
title: "xx实例"
desc: "xx实例"
author: "author"
email: "email"
version: "v1"
)
@server(
prefix: comment/v1
group: comment
jwt: JwtAuth
)
service Core {
@handler getComment
post /comment/:id (CommentGetRequest) returns (CommentGetResponse)
@handler saveComment
post /comment (CommentSaveRequest) returns (CommentSaveResponse)
@handler deleteComment
delete /comment/:id (CommentDeleteRequest) returns (CommentDeleteResponse)
@handler updateComment
put /comment/:id (CommentUpdateRequest) returns (CommentUpdateResponse)
@handler searchComment
post /comment/search (CommentSearchRequest) returns (CommentSearchResponse)
}
type (
CommentGetRequest {
Id int64 `path:"id"`
}
CommentGetResponse struct{
Comment CommentItem `json:"comment"`
}
CommentSaveRequest struct{
Comment CommentItem `json:"comment"`
}
CommentSaveResponse struct{}
CommentDeleteRequest struct{
Id int64 `path:"id"`
}
CommentDeleteResponse struct{}
CommentUpdateRequest struct{
Id int64 `path:"id"`
Comment CommentItem `json:"comment"`
}
CommentUpdateResponse struct{}
CommentSearchRequest struct{
Page int `json:"page"`
Size int `json:"size"`
}
CommentSearchResponse{
List []CommentItem `json:"list"`
Total int64 `json:"total"`
}
CommentItem struct{
}
)
... ...
syntax = "proto3";
option go_package ="./pb";
package pb;
message CommentGetReq {
int64 Id = 1;
}
message CommentGetResp{
CommentItem User = 1;
}
message CommentSaveReq {
}
message CommentSaveResp{
}
message CommentDeleteReq {
int64 Id = 1;
}
message CommentDeleteResp{
}
message CommentUpdateReq {
int64 Id = 1;
}
message CommentUpdateResp{
}
message CommentSearchReq {
int64 PageNumber = 1;
int64 PageSize = 2;
}
message CommentSearchResp{
repeated CommentItem List =1;
int64 Total =2;
}
message CommentItem {
}
service CommentService {
rpc CommentGet(CommentGetReq) returns(CommentGetResp);
rpc CommentSave(CommentSaveReq) returns(CommentSaveResp);
rpc CommentDelete(CommentDeleteReq) returns(CommentDeleteResp);
rpc CommentUpdate(CommentUpdateReq) returns(CommentUpdateResp);
rpc CommentSearch(CommentSearchReq) returns(CommentSearchResp);
}
... ...
package db
import (
"gorm.io/gorm"
)
func Migrate(db *gorm.DB) {
db.AutoMigrate()
}
... ...
package models
import (
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
"gorm.io/gorm"
"gorm.io/plugin/soft_delete"
"time"
)
type Comment struct {
Id int64 // 唯一标识
CreatedAt int64 `json:",omitempty"`
UpdatedAt int64 `json:",omitempty"`
DeletedAt int64 `json:",omitempty"`
Version int `json:",omitempty"`
IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"`
}
func (m *Comment) TableName() string {
return "t_comment"
}
func (m *Comment) BeforeCreate(tx *gorm.DB) (err error) {
m.CreatedAt = time.Now().Unix()
m.UpdatedAt = time.Now().Unix()
return
}
func (m *Comment) BeforeUpdate(tx *gorm.DB) (err error) {
m.UpdatedAt = time.Now().Unix()
return
}
func (m *Comment) CacheKeyFunc() string {
if m.Id == 0 {
return ""
}
return fmt.Sprintf("%v:cache:%v:id:%v", domain.ProjectName, m.TableName(), m.Id)
}
func (m *Comment) CacheKeyFuncByObject(obj interface{}) string {
if v, ok := obj.(*Comment); ok {
return v.CacheKeyFunc()
}
return ""
}
func (m *Comment) 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-discuss/cmd/discuss/interanl/pkg/db/models"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
"gorm.io/gorm"
)
type CommentRepository struct {
*cache.CachedRepository
}
func (repository *CommentRepository) Insert(ctx context.Context, conn transaction.Conn, dm *domain.Comment) (*domain.Comment, error) {
var (
err error
m = &models.Comment{}
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 *CommentRepository) Update(ctx context.Context, conn transaction.Conn, dm *domain.Comment) (*domain.Comment, error) {
var (
err error
m *models.Comment
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 *CommentRepository) UpdateWithVersion(ctx context.Context, transaction transaction.Conn, dm *domain.Comment) (*domain.Comment, error) {
var (
err error
m *models.Comment
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 *CommentRepository) Delete(ctx context.Context, conn transaction.Conn, dm *domain.Comment) (*domain.Comment, error) {
var (
tx = conn.DB()
m = &models.Comment{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 *CommentRepository) FindOne(ctx context.Context, conn transaction.Conn, id int64) (*domain.Comment, error) {
var (
err error
tx = conn.DB()
m = new(models.Comment)
)
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.Comment)
cacheModel.Id = id
if err = repository.QueryCache(cacheModel.CacheKeyFunc, m, queryFunc); err != nil {
return nil, err
}
return repository.ModelToDomainModel(m)
}
func (repository *CommentRepository) Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*domain.Comment, error) {
var (
tx = conn.DB()
ms []*models.Comment
dms = make([]*domain.Comment, 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 *CommentRepository) ModelToDomainModel(from *models.Comment) (*domain.Comment, error) {
to := &domain.Comment{}
err := copier.Copy(to, from)
return to, err
}
func (repository *CommentRepository) DomainModelToModel(from *domain.Comment) (*models.Comment, error) {
to := &models.Comment{}
err := copier.Copy(to, from)
return to, err
}
func NewCommentRepository(cache *cache.CachedRepository) domain.CommentRepository {
return &CommentRepository{CachedRepository: cache}
}
... ...
package transaction
import (
"context"
"fmt"
"gorm.io/gorm"
"sync"
)
type Context struct {
//启用事务标识
beginTransFlag bool
db *gorm.DB
session *gorm.DB
lock sync.Mutex
}
func (transactionContext *Context) Begin() error {
transactionContext.lock.Lock()
defer transactionContext.lock.Unlock()
transactionContext.beginTransFlag = true
tx := transactionContext.db.Begin()
transactionContext.session = tx
return nil
}
func (transactionContext *Context) Commit() error {
transactionContext.lock.Lock()
defer transactionContext.lock.Unlock()
if !transactionContext.beginTransFlag {
return nil
}
tx := transactionContext.session.Commit()
return tx.Error
}
func (transactionContext *Context) Rollback() error {
transactionContext.lock.Lock()
defer transactionContext.lock.Unlock()
if !transactionContext.beginTransFlag {
return nil
}
tx := transactionContext.session.Rollback()
return tx.Error
}
func (transactionContext *Context) DB() *gorm.DB {
if transactionContext.beginTransFlag && transactionContext.session != nil {
return transactionContext.session
}
return transactionContext.db
}
func NewTransactionContext(db *gorm.DB) *Context {
return &Context{
db: db,
}
}
type Conn interface {
Begin() error
Commit() error
Rollback() error
DB() *gorm.DB
}
// UseTrans when beginTrans is true , it will begin a new transaction
// to execute the function, recover when panic happen
func UseTrans(ctx context.Context,
db *gorm.DB,
fn func(context.Context, Conn) error, beginTrans bool) (err error) {
var tx Conn
tx = NewTransactionContext(db)
if beginTrans {
if err = tx.Begin(); err != nil {
return
}
}
defer func() {
if p := recover(); p != nil {
if e := tx.Rollback(); e != nil {
err = fmt.Errorf("recover from %#v, rollback failed: %w", p, e)
} else {
err = fmt.Errorf("recoveer from %#v", p)
}
} else if err != nil {
if e := tx.Rollback(); e != nil {
err = fmt.Errorf("transaction failed: %s, rollback failed: %w", err, e)
}
} else {
err = tx.Commit()
}
}()
return fn(ctx, tx)
}
func PaginationAndCount(ctx context.Context, tx *gorm.DB, params map[string]interface{}, 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 domain
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
)
type Comment struct {
Id int64 // 唯一标识
CreatedAt int64 `json:",omitempty"`
UpdatedAt int64 `json:",omitempty"`
DeletedAt int64 `json:",omitempty"`
Version int `json:",omitempty"`
}
type CommentRepository interface {
Insert(ctx context.Context, conn transaction.Conn, dm *Comment) (*Comment, error)
Update(ctx context.Context, conn transaction.Conn, dm *Comment) (*Comment, error)
UpdateWithVersion(ctx context.Context, conn transaction.Conn, dm *Comment) (*Comment, error)
Delete(ctx context.Context, conn transaction.Conn, dm *Comment) (*Comment, error)
FindOne(ctx context.Context, conn transaction.Conn, id int64) (*Comment, error)
Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*Comment, error)
}
func (m *Comment) Identify() interface{} {
if m.Id == 0 {
return nil
}
return m.Id
}
... ...
package domain
import (
"reflect"
)
func OffsetLimit(page, size int) (offset int, limit int) {
if page == 0 {
page = 1
}
if size == 0 {
size = 20
}
offset = (page - 1) * size
limit = size
return
}
type QueryOptions map[string]interface{}
func NewQueryOptions() QueryOptions {
options := make(map[string]interface{})
return options
}
func (options QueryOptions) WithOffsetLimit(page, size int) QueryOptions {
offset, limit := OffsetLimit(page, size)
options["offset"] = offset
options["limit"] = limit
return options
}
func (options QueryOptions) WithKV(key string, value interface{}) QueryOptions {
if reflect.ValueOf(value).IsZero() {
return options
}
options[key] = value
return options
}
func (options QueryOptions) 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
}
... ...
package domain
import (
"errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var (
ErrNotFound = sqlx.ErrNotFound
ErrUpdateFail = errors.New("sql: no rows affected")
)
var ProjectName = "project"
... ...
## 生成模型
```
goctl model mysql ddl -s .\doc\dsl\model\table.sql -d cmd/chart-server
```
## api生成
```
goctl api go -api .\cmd\discuss\mini\dsl\core.api -dir cmd/discuss/mini -style go_zero
```
## swagger 生成
```
goctl api plugin -plugin goctl-swagger="swagger -filename core.json" -api .\cmd\discuss\mini\dsl\core.api -dir .\cmd\discuss\mini\dsl
```
\ No newline at end of file
... ...
package main
import (
"flag"
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/config"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/handler"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
var configFile = flag.String("f", "etc/core.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}
... ...
import "core/test.api"
\ No newline at end of file
... ...
{
"swagger": "2.0",
"info": {
"title": "",
"version": ""
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"v1/health": {
"post": {
"summary": "健康",
"operationId": "miniHealth",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/MiniHealthResposne"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/MiniHealthRequest"
}
}
],
"requestBody": {},
"tags": [
"tool"
]
}
}
},
"definitions": {
"MiniHealthRequest": {
"type": "object",
"title": "MiniHealthRequest"
},
"MiniHealthResposne": {
"type": "object",
"properties": {
"ok": {
"type": "boolean",
"format": "boolean"
}
},
"title": "MiniHealthResposne",
"required": [
"ok"
]
}
},
"securityDefinitions": {
"apiKey": {
"type": "apiKey",
"description": "Enter JWT Bearer token **_only_**",
"name": "Authorization",
"in": "header"
}
},
"security": [
{
"apiKey": []
}
]
}
... ...
syntax = "v1"
info(
title: "天联鹰蜓"
desc: "天联鹰蜓"
author: "小火箭"
email: "email"
version: "v1"
)
@server(
prefix: v1
group: tool
)
service Core {
@doc "健康"
@handler miniHealth
get /health (MiniHealthRequest) returns (MiniHealthResposne)
}
type(
MiniHealthRequest struct{
}
MiniHealthResposne struct{
Ok bool `json:"ok"`
}
)
\ No newline at end of file
... ...
Name: chart
Host: 0.0.0.0
Port: 8081
Verbose: true
Log:
#Mode: file
Encoding: plain
Level: debug # info
MaxSize: 1 # 2MB
TimeFormat: 2006-01-02 15:04:05.000
JwtAuth:
AccessSecret: digital-platform
Expire: 360000
Redis:
Host: 127.0.0.1:6379
Type: node
Pass:
DB:
DataSource: host=114.55.200.59 user=postgres password=eagle1010 dbname=sumifcc-bchart-dev port=31543 sslmode=disable TimeZone=Asia/Shanghai
ByteMetadata:
Name: ApiByteMetadata
Host1: http://127.0.0.1:8080
Host: http://character-library-metadata-bastion-test.fjmaimaimai.com
Timeout: 0s
\ 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-discuss/pkg/config"
)
type Config struct {
rest.RestConf
config.Config
Redis redis.RedisConf `json:",optional"`
}
... ...
// Code generated by goctl. DO NOT EDIT.
package handler
import (
"net/http"
tool "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/handler/tool"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodGet,
Path: "/health",
Handler: tool.MiniHealthHandler(serverCtx),
},
},
rest.WithPrefix("/v1"),
)
}
... ...
package tool
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/result"
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/logic/tool"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/types"
)
func MiniHealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.MiniHealthRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := tool.NewMiniHealthLogic(r.Context(), svcCtx)
resp, err := l.MiniHealth(&req)
result.HttpResult(r, w, resp, err)
}
}
... ...
package tool
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type MiniHealthLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewMiniHealthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MiniHealthLogic {
return &MiniHealthLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *MiniHealthLogic) MiniHealth(req *types.MiniHealthRequest) (resp *types.MiniHealthResposne, err error) {
l.svcCtx.CommentRepository.FindOne(l.ctx, l.svcCtx.DefaultDBConn(), 0)
return
}
... ...
package svc
import (
"github.com/zeromicro/go-zero/core/stores/redis"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/repository"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/mini/internal/config"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/cache"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/database"
"gorm.io/gorm"
)
type ServiceContext struct {
Config config.Config
DB *gorm.DB
Redis *redis.Redis
CommentRepository domain.CommentRepository
}
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"})
return &ServiceContext{
Config: c,
DB: db,
Redis: redis,
CommentRepository: repository.NewCommentRepository(cache.NewCachedRepository(mlCache)),
}
}
func (svc *ServiceContext) DefaultDBConn() transaction.Conn {
return transaction.NewTransactionContext(svc.DB)
}
... ...
// Code generated by goctl. DO NOT EDIT.
package types
type MiniHealthRequest struct {
}
type MiniHealthResposne struct {
Ok bool `json:"ok"`
}
... ...
## 生成模型
```
goctl model mysql ddl -s .\doc\dsl\model\table.sql -d cmd/chart-server
```
## api生成
```
goctl api go -api .\cmd\discuss\system\dsl\core.api -dir cmd/discuss/system -style go_zero
```
## swagger 生成
```
goctl api plugin -plugin goctl-swagger="swagger -filename core.json" -api .\cmd\discuss\system\dsl\core.api -dir .\cmd\discuss\system\dsl
```
\ No newline at end of file
... ...
package main
import (
"flag"
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/config"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/handler"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
var configFile = flag.String("f", "etc/core.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}
... ...
import "core/test.api"
\ No newline at end of file
... ...
{
"swagger": "2.0",
"info": {
"title": "",
"version": ""
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"v1/health": {
"post": {
"summary": "健康",
"operationId": "miniHealth",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/MiniHealthResposne"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/MiniHealthRequest"
}
}
],
"requestBody": {},
"tags": [
"tool"
]
}
}
},
"definitions": {
"MiniHealthRequest": {
"type": "object",
"title": "MiniHealthRequest"
},
"MiniHealthResposne": {
"type": "object",
"properties": {
"ok": {
"type": "boolean",
"format": "boolean"
}
},
"title": "MiniHealthResposne",
"required": [
"ok"
]
}
},
"securityDefinitions": {
"apiKey": {
"type": "apiKey",
"description": "Enter JWT Bearer token **_only_**",
"name": "Authorization",
"in": "header"
}
},
"security": [
{
"apiKey": []
}
]
}
... ...
syntax = "v1"
info(
title: "天联鹰蜓"
desc: "天联鹰蜓"
author: "小火箭"
email: "email"
version: "v1"
)
@server(
prefix: v1
group: tool
)
service Core {
@doc "健康"
@handler miniHealth
post /health (MiniHealthRequest) returns (MiniHealthResposne)
}
type(
MiniHealthRequest struct{
}
MiniHealthResposne struct{
Ok bool `json:"ok"`
}
)
\ No newline at end of file
... ...
Name: Core
Host: 0.0.0.0
Port: 8888
... ...
package config
import "github.com/zeromicro/go-zero/rest"
type Config struct {
rest.RestConf
}
... ...
// Code generated by goctl. DO NOT EDIT.
package handler
import (
"net/http"
tool "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/handler/tool"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodPost,
Path: "/health",
Handler: tool.MiniHealthHandler(serverCtx),
},
},
rest.WithPrefix("/v1"),
)
}
... ...
package tool
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/logic/tool"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/types"
)
func MiniHealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.MiniHealthRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := tool.NewMiniHealthLogic(r.Context(), svcCtx)
resp, err := l.MiniHealth(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
... ...
package tool
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/svc"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type MiniHealthLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewMiniHealthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MiniHealthLogic {
return &MiniHealthLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *MiniHealthLogic) MiniHealth(req *types.MiniHealthRequest) (resp *types.MiniHealthResposne, err error) {
// todo: add your logic here and delete this line
return
}
... ...
package svc
import (
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/system/internal/config"
)
type ServiceContext struct {
Config config.Config
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
}
}
... ...
// Code generated by goctl. DO NOT EDIT.
package types
type MiniHealthRequest struct {
}
type MiniHealthResposne struct {
Ok bool `json:"ok"`
}
... ...
CREATE TABLE `comment` (
`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
... ...
module gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss
go 1.19
require (
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/jinzhu/copier v0.4.0
github.com/jinzhu/now v1.1.5
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.4
github.com/tiptok/gocomm v1.0.14
github.com/zeromicro/go-zero v1.5.5
google.golang.org/grpc v1.57.0
gorm.io/driver/mysql v1.5.1
gorm.io/driver/postgres v1.5.2
gorm.io/gorm v1.25.4
gorm.io/plugin/soft_delete v1.2.1
)
require (
github.com/Shopify/sarama v1.37.2 // indirect
github.com/beego/beego/v2 v2.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/eapache/go-resiliency v1.3.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/garyburd/redigo v1.6.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.5.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.1 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.4.3 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.7.6 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/leodido/go-urn v1.1.0 // indirect
github.com/magiconair/properties v1.8.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openzipkin/zipkin-go v0.4.1 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.0 // indirect
github.com/spf13/jwalterweatherman v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.4.0 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
go.etcd.io/etcd/api/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/v3 v3.5.9 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.14.0 // indirect
go.opentelemetry.io/otel/sdk v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/automaxprocs v1.5.3 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/term v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.26.3 // indirect
k8s.io/apimachinery v0.27.0-alpha.3 // indirect
k8s.io/client-go v0.26.3 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230307230338-69ee2d25a840 // indirect
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
... ...
package cache
import (
"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"
"github.com/zeromicro/go-zero/zrpc"
"time"
)
type JWT struct {
Secret string `json:",optional"`
Expires time.Duration `json:",optional"`
}
type JwtAuth struct {
AccessSecret string
Expire int64
}
type Config struct {
JwtAuth JwtAuth `json:",optional"`
UserRpc zrpc.RpcClientConf `json:",optional"`
AuthRpc zrpc.RpcClientConf `json:",optional"`
PostRpc zrpc.RpcClientConf `json:",optional"`
CommentRpc zrpc.RpcClientConf `json:",optional"`
JWT JWT `json:",optional"`
DB struct {
DataSource string
} `json:",optional"`
Cache cache.CacheConf `json:",optional"`
DTM DTM `json:",optional"`
Sms Sms `json:",optional"`
Oss Oss `json:",optional"`
Wechat Wechat `json:",optional"` // 学员端微信
CoachClient Wechat `json:",optional"` // 教练端微信
OfficialAccount Wechat `json:",optional"`
ThirdWechatApps []Wechat `json:",optional"`
}
type DTM struct {
Server Server `json:",optional"`
}
type Server struct {
Name string `json:",optional"`
Host string `json:",optional"`
GRPC GRPC `json:",optional"`
HTTP HTTP `json:",optional"`
Metrics Metrics `json:",optional"`
}
type HTTP struct {
Port string
}
type GRPC struct {
Port string
}
type Metrics struct {
Port string
}
type Sms struct {
Debug bool
DebugCode string
Expire int `json:",default=180"`
MaxSendTime int `json:",default=5"`
CompanyName string
SecretId string
SecretKey string
SmsAppId string
Sign string
TemplateId string
}
type Oss struct {
OssEndPoint string
AccessKeyID string
AccessKeySecret string
BuckName string
RegionID string
RoleArn string
CDN CDN
}
type Wechat struct {
AppName string `json:",optional"`
AppID string
AppSecret string
MsgTemplates []Template `json:",optional"`
}
func (wx Wechat) GetTemplate(code string) (Template, bool) {
for _, temp := range wx.MsgTemplates {
if temp.Code == code {
return temp, true
}
}
return Template{}, false
}
type CDN struct {
HostPairs []string
}
type Template struct {
ID string // 模板ID
Name string // 模板名称
Code string // 模板编码
}
... ...
package contextdata
import (
"context"
)
func GetTenantFromCtx(ctx context.Context) int64 {
userToken := GetUserTokenFromCtx(ctx)
if userToken.CompanyId == 0 {
return 0
}
return userToken.CompanyId
}
... ...
package contextdata
import (
"context"
"encoding/json"
"github.com/golang-jwt/jwt/v4"
"github.com/zeromicro/go-zero/core/logx"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/config"
"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 `json:"userId"`
CompanyId int64 `json:"companyId"`
}
func (tk UserToken) GenerateToken(jwtConfig config.JwtAuth) (string, error) {
claims := make(jwt.MapClaims)
claims["exp"] = time.Now().Unix() + jwtConfig.Expire
claims["iat"] = time.Now().Unix()
claims["UserId"] = tk.UserId
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = claims
return token.SignedString([]byte(jwtConfig.AccessSecret))
}
func (tk *UserToken) ParseToken(jwtConfig config.JWT, str string) error {
return nil
}
// CheckUserInfo 如果UserToken有效 返回:true 否则返回false
func (tk *UserToken) CheckUserInfo() bool {
return !(tk.UserId > 100000000 || tk.UserId <= 0)
}
... ...
package database
import (
"context"
"fmt"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
func OpenGormDB(source string) *gorm.DB {
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Info, // Log level
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
Colorful: false, // Disable color
},
)
fmt.Println("starting db...")
db, err := gorm.Open(mysql.Open(source), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
return db
}
func OpenGormPGDB(source string, 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 result
import (
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/xerr"
"net/http"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpx"
"google.golang.org/grpc/status"
)
// http返回
func HttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
if err == nil {
//成功返回
r := Success(resp)
httpx.WriteJson(w, http.StatusOK, r)
} else {
//错误返回
errcode := xerr.SERVER_COMMON_ERROR
errmsg := "服务器开小差啦,稍后再来试一试"
internalErr := ""
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型
//自定义CodeError
errcode = e.GetErrCode()
errmsg = e.GetErrMsg()
if e.InternalError != nil {
internalErr = e.InternalError.Error()
}
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端
errcode = grpcCode
errmsg = gstatus.Message()
}
}
}
logx.WithContext(r.Context()).Errorf("【API-ERR】 : %+v ", err)
response := Error(errcode, errmsg)
response.Error = internalErr
httpx.WriteJson(w, http.StatusOK, response)
}
}
// 授权的http方法
func AuthHttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
if err == nil {
//成功返回
r := Success(resp)
httpx.WriteJson(w, http.StatusOK, r)
} else {
//错误返回
errcode := xerr.SERVER_COMMON_ERROR
errmsg := "服务器开小差啦,稍后再来试一试"
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型
//自定义CodeError
errcode = e.GetErrCode()
errmsg = e.GetErrMsg()
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端
errcode = grpcCode
errmsg = gstatus.Message()
}
}
}
logx.WithContext(r.Context()).Errorf("【GATEWAY-ERR】 : %+v ", err)
httpx.WriteJson(w, http.StatusUnauthorized, Error(errcode, errmsg))
}
}
// http 参数错误返回
func ParamErrorResult(r *http.Request, w http.ResponseWriter, err error) {
errMsg := fmt.Sprintf("%s ,%s", xerr.MapErrMsg(xerr.REUQEST_PARAM_ERROR), err.Error())
httpx.WriteJson(w, http.StatusBadRequest, Error(xerr.REUQEST_PARAM_ERROR, errMsg))
}
... ...
package result
import (
"context"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc/status"
)
// job返回
func JobResult(ctx context.Context, resp interface{}, err error) {
if err == nil {
// 成功返回 ,只有dev环境下才会打印info,线上不显示
if resp != nil {
logx.Infof("resp: %+v", resp)
}
return
} else {
errCode := xerr.SERVER_COMMON_ERROR
errMsg := "服务器开小差啦,稍后再来试一试"
// 错误返回
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { // 自定义错误类型
// 自定义CodeError
errCode = e.GetErrCode()
errMsg = e.GetErrMsg()
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { // 区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端
errCode = grpcCode
errMsg = gstatus.Message()
}
}
}
logx.WithContext(ctx).Errorf("【JOB-ERR】 : %+v ,errCode:%d , errMsg:%s ", err, errCode, errMsg)
return
}
}
... ...
package result
type ResponseSuccessBean struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
type NullJson struct{}
func Success(data interface{}) *ResponseSuccessBean {
return &ResponseSuccessBean{Code: 0, Msg: "OK", Data: data}
}
type ResponseErrorBean struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
Error string `json:"err"`
}
func Error(errCode uint32, errMsg string) *ResponseErrorBean {
return &ResponseErrorBean{Code: errCode, Msg: errMsg}
}
... ...
package tool
import (
"crypto/md5"
"fmt"
"io"
)
/** 加密方式 **/
func Md5ByString(str string) string {
m := md5.New()
_, err := io.WriteString(m, str)
if err != nil {
panic(err)
}
arr := m.Sum(nil)
return fmt.Sprintf("%x", arr)
}
func Md5ByBytes(b []byte) string {
return fmt.Sprintf("%x", md5.Sum(b))
}
... ...
package tool
import (
"path/filepath"
"strings"
)
const (
Image = "image"
Video = "video"
)
var TypeMap = map[string]string{
"jpg": Image,
"png": Image,
"gif": Image,
"webp": Image,
"cr2": Image,
"tif": Image,
"bmp": Image,
"heif": Image,
"jxr": Image,
"psd": Image,
"ico": Image,
"dwg": Image,
"avif": Image,
"mp4": Video,
"m4v": Video,
"mkv": Video,
"webm": Video,
"mov": Video,
"avi": Video,
"wmv": Video,
"mpg": Video,
"flv": Video,
"3gp": Video,
}
var DefaultFileTypeDetector = FileTypeDetector{}
type FileTypeDetector struct {
}
func (c FileTypeDetector) Classify(medias []string, mediaType string) []string {
result := make([]string, 0)
for _, media := range medias {
v, ok := TypeMap[strings.Trim(filepath.Ext(media), ".")]
if !ok {
continue
}
if v == mediaType {
result = append(result, media)
}
}
return result
}
... ...
package tool
import (
jwt "github.com/golang-jwt/jwt/v4"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/config"
"time"
)
type UserToken struct {
UserId int64 `json:"userId"`
CoachId int64 `json:"coach_id"`
AdminId int64 `json:"adminId"`
ClientType string `json:"clientType"`
AccessShops []int64 `json:"accessShops"`
}
func (tk UserToken) GenerateToken(jwtConfig config.JwtAuth) (string, error) {
claims := make(jwt.MapClaims)
claims["exp"] = time.Now().Unix() + jwtConfig.Expire
claims["iat"] = time.Now().Unix()
claims["UserId"] = tk.UserId
claims["CoachId"] = tk.CoachId
claims["AdminId"] = tk.AdminId
claims["ClientType"] = tk.ClientType
claims["AccessShops"] = tk.AccessShops
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = claims
return token.SignedString([]byte(jwtConfig.AccessSecret))
}
func (tk *UserToken) ParseToken(jwtConfig config.JWT, str string) error {
//tokenClaims, err := jwt.ParseWithClaims(
// str,
// tk,
// func(token *jwt.Token) (interface{}, error) {
// return []byte(jwtConfig.Secret), nil
// })
//if err != nil {
// return err
//}
//if claim, ok := tokenClaims.Claims.(*UserToken); ok && tokenClaims.Valid {
// *tk = *claim
// return nil
//}
//return errors.New("token 解析失败")
return nil
}
// CheckUserInfo 如果UserToken有效 返回:true 否则返回false
func (tk *UserToken) CheckUserInfo() bool {
return !(tk.UserId > 100000000 || tk.UserId <= 0)
}
... ...
package tool
import (
"math/rand"
"time"
)
const (
KC_RAND_KIND_NUM = 0 // 纯数字
KC_RAND_KIND_LOWER = 1 // 小写字母
KC_RAND_KIND_UPPER = 2 // 大写字母
KC_RAND_KIND_ALL = 3 // 数字、大小写字母
)
// 随机字符串
func Krand(size int, kind int) string {
ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
is_all := kind > 2 || kind < 0
rand.Seed(time.Now().UnixNano())
for i := 0; i < size; i++ {
if is_all { // random ikind
ikind = rand.Intn(3)
}
scope, base := kinds[ikind][0], kinds[ikind][1]
result[i] = uint8(base + rand.Intn(scope))
}
return string(result)
}
... ...
package xcollection
type TreeNode interface {
PID() string
ID() string
}
type Tree struct {
Node TreeNode `json:"chart"`
Nodes []*Tree `json:"charts"`
}
func NewTree(nodes []TreeNode) *Tree {
var tree = &Tree{
Node: nil,
Nodes: make([]*Tree, 0),
}
for i := range nodes {
match := traverseAdd(tree, nodes[i])
if !match {
tree.Nodes = append(tree.Nodes, newTree(nodes[i]))
}
}
return tree
}
func newTree(node TreeNode) *Tree {
return &Tree{
Node: node,
Nodes: make([]*Tree, 0),
}
}
func (tree *Tree) Root() TreeNode {
if tree.Node != nil {
return tree.Node
}
if len(tree.Nodes) > 0 {
return tree.Nodes[0].Node
}
return nil
}
// TreeNodePaths returns all the parents of the current node 1->5->7 , use time n*O(n)(need performance optimization)
func (tree *Tree) TreeNodePaths(node TreeNode) []TreeNode {
treeNode := node
result := make([]TreeNode, 0)
for {
if treeNode == nil {
break
}
tmp := tree.find(treeNode, func(a, b TreeNode) bool {
if a.ID() == b.PID() {
return true
}
return false
})
result = append(result, treeNode)
if tmp == nil {
break
}
treeNode = tmp.Node
}
reserveResult := make([]TreeNode, 0)
for i := len(result) - 1; i >= 0; i-- {
reserveResult = append(reserveResult, result[i])
}
return reserveResult
}
// Add adds a node to the first matching parent tree if add success it return true
func (tree *Tree) Add(node TreeNode) bool {
return traverseAdd(tree, node)
}
// AllChildNode returns all child nodes under Node, including itself
func (tree *Tree) AllChildNode(node TreeNode) []TreeNode {
treeNode := tree.find(node, nil)
if treeNode == nil {
return []TreeNode{}
}
return tree.allChildNode(treeNode, nil)
}
// AllLeafNode returns all leaf node under Node ,if node is nil returns all leaf node under tree
func (tree *Tree) AllLeafNode(node TreeNode) []TreeNode {
treeNode := tree
if node != nil {
treeNode = tree.find(node, nil)
}
if treeNode == nil {
return []TreeNode{}
}
return tree.allChildNode(treeNode, func(node *Tree) bool {
if len(node.Nodes) == 0 {
return true
}
return false
})
}
// Depth returns all child nodes under depth depth=[1:n]
func (tree *Tree) Depth(depth int) []TreeNode {
treeNode := tree.find(tree.Root(), nil)
if treeNode == nil {
return []TreeNode{}
}
return tree.allChildByDepth(treeNode, depth)
}
// AllChildNodeByDepth returns all child nodes under depth Node
func (tree *Tree) AllChildNodeByDepth(node TreeNode, depth int) []TreeNode {
treeNode := tree.find(node, nil)
if treeNode == nil {
return []TreeNode{}
}
return tree.allChildByDepth(treeNode, depth)
}
// Find query the node in this tree
func (tree *Tree) Find(node TreeNode, compared func(a, b TreeNode) bool) *Tree {
return tree.find(node, compared)
}
// find query the node in this tree
func (tree *Tree) find(node TreeNode, compared func(a, b TreeNode) bool) *Tree {
var stack []*Tree
stack = append(stack, tree)
var find *Tree
for {
if len(stack) == 0 {
break
}
pop := stack[0]
stack = stack[1:]
stack = append(stack, pop.Nodes...)
if pop == nil || pop.Node == nil {
continue
}
if compared != nil {
if compared(pop.Node, node) {
find = pop
break
}
continue
}
if pop.Node.ID() == node.ID() {
find = pop
break
}
}
return find
}
// allChildNode 返回treeNode下所有子节点
func (tree *Tree) allChildNode(treeNode *Tree, filter func(node *Tree) bool) []TreeNode {
var stack []*Tree
stack = append(stack, treeNode)
var res []TreeNode
for {
if len(stack) == 0 {
break
}
pop := stack[0]
stack = stack[1:]
stack = append(stack, pop.Nodes...)
if filter != nil && !filter(pop) {
continue
}
res = append(res, pop.Node)
}
return res
}
// traverseAdd 递归添加
//
// tree 当前树
// node 判断的节点
func traverseAdd(tree *Tree, node TreeNode) bool {
list := tree.Nodes
var match bool = false
for i := range list {
id, pid := list[i].Node.ID(), node.PID()
if pid == id {
list[i].Nodes = append(list[i].Nodes, newTree(node))
return true
}
if match || traverseAdd(list[i], node) {
match = true
break
}
}
return match
}
// allChildByDepth 返回treeNode下指定深度的所有子节点 depth=[1:n]
func (tree *Tree) allChildByDepth(treeNode *Tree, depth int) []TreeNode {
var stack []*Tree
stack = append(stack, treeNode)
var res []TreeNode
if depth <= 0 {
return res
}
if treeNode.Root() != nil && depth == 1 {
return []TreeNode{treeNode.Root()}
}
curDepth := 1
var depthStack []*Tree
for {
if len(stack) == 0 {
break
}
pop := stack[0]
stack = stack[1:]
depthStack = append(depthStack, pop.Nodes...)
if len(stack) == 0 {
curDepth++
stack = depthStack[:]
depthStack = []*Tree{}
if curDepth == depth {
for i := range stack {
res = append(res, stack[i].Node)
}
break
}
}
}
return res
}
... ...
package xcollection
import (
"github.com/stretchr/testify/assert"
"strconv"
"testing"
)
func prepare() []struct {
Input []TreeNode
Text string
Except []string
Except2 []string
} {
return []struct {
Input []TreeNode
Text string
Except []string
Except2 []string
}{
{
Input: []TreeNode{
&st{Id: 1, Pid: 0},
&st{Id: 2, Pid: 1}, &st{Id: 3, Pid: 1}, &st{Id: 4, Pid: 1},
&st{Id: 5, Pid: 3},
&st{Id: 6, Pid: 5}, &st{Id: 7, Pid: 5}},
Text: `
树形结构:
1
2 3 4
5
6 7
`,
Except: []string{"5", "6", "7"},
Except2: []string{"2", "4", "6", "7"},
},
}
}
func Test_Tree(t *testing.T) {
table := prepare()
for i := range table {
tree := NewTree(table[i].Input)
out := tree.AllChildNode(&st{Id: 5, Pid: 3})
var res []string = treeNodeResults(out)
assert.Equal(t, res, table[i].Except)
out = tree.AllLeafNode(nil) //tree.Root()
res = treeNodeResults(out)
assert.Equal(t, res, table[i].Except2)
root := tree.Root()
assert.Equal(t, root.ID(), "1")
//tree.Add(&st{Id:10,Pid: 7})
//
//out = tree.AllLeafNode(tree.Root())
//res = treeNodeResults(out)
//assert.Equal(t, res, []string{"2", "4", "6", "10"})
out = tree.TreeNodePaths(&st{Id: 7, Pid: 5})
res = treeNodeResults(out)
assert.Equal(t, res, []string{"1", "3", "5", "7"})
}
}
func Test_TreeNodeByDepth(t *testing.T) {
input := []TreeNode{
&st{Id: 1, Pid: 0},
&st{Id: 2, Pid: 1}, &st{Id: 3, Pid: 1}, &st{Id: 4, Pid: 1},
&st{Id: 5, Pid: 3},
&st{Id: 6, Pid: 5}, &st{Id: 7, Pid: 5},
&st{Id: 8, Pid: 6}, &st{Id: 9, Pid: 6}, &st{Id: 10, Pid: 6}, &st{Id: 11, Pid: 7}, &st{Id: 12, Pid: 7},
}
tree := NewTree(input)
/*
树形结构:
1
2 3 4
5
6 7
8 9 10 11 12
*/
var out []TreeNode
var res []string
out = tree.AllChildNodeByDepth(&st{Id: 5, Pid: 3}, 2)
res = treeNodeResults(out)
assert.Equal(t, []string{"6", "7"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 1)
res = treeNodeResults(out)
assert.Equal(t, []string{"1"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 2)
res = treeNodeResults(out)
assert.Equal(t, []string{"2", "3", "4"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 3)
res = treeNodeResults(out)
assert.Equal(t, []string{"5"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 4)
res = treeNodeResults(out)
assert.Equal(t, []string{"6", "7"}, res)
out = tree.AllChildNodeByDepth(tree.Root(), 5)
res = treeNodeResults(out)
assert.Equal(t, []string{"8", "9", "10", "11", "12"}, res)
}
type st struct {
Id int
Pid int
}
func (t *st) PID() string {
return strconv.Itoa(t.Pid)
}
func (t *st) ID() string {
return strconv.Itoa(t.Id)
}
func treeNodeResults(nodes []TreeNode) []string {
var res []string
for i := range nodes {
res = append(res, nodes[i].ID())
}
return res
}
... ...
package xerr
// 成功返回
const OK uint32 = 200
/**(前3位代表业务,后三位代表具体功能)**/
// 全局错误码
const SERVER_COMMON_ERROR uint32 = 100001
const REUQEST_PARAM_ERROR uint32 = 100002
const TOKEN_EXPIRE_ERROR uint32 = 100003
const TOKEN_GENERATE_ERROR uint32 = 100004
const DB_ERROR uint32 = 100005
const DB_UPDATE_AFFECTED_ZERO_ERROR uint32 = 100006
const REQUEST_ARGS_ERROR = 200001
// 微信模块
const ErrWxMiniAuthFailError uint32 = 500001
const ErrUserNoAuth uint32 = 500002
... ...
package xerr
var message map[uint32]string
func init() {
message = make(map[uint32]string)
message[OK] = "SUCCESS"
message[SERVER_COMMON_ERROR] = "服务器开小差啦,稍后再来试一试"
message[REUQEST_PARAM_ERROR] = "参数错误"
message[TOKEN_EXPIRE_ERROR] = "token失效,请重新登陆"
message[TOKEN_GENERATE_ERROR] = "生成token失败"
message[DB_ERROR] = "数据库繁忙,请稍后再试"
message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0"
message[ErrUserNoAuth] = "无权限"
message[ErrWxMiniAuthFailError] = "微信授权失败"
}
func MapErrMsg(errcode uint32) string {
if msg, ok := message[errcode]; ok {
return msg
} else {
return "服务器开小差啦,稍后再来试一试"
}
}
func IsCodeErr(errcode uint32) bool {
if _, ok := message[errcode]; ok {
return true
} else {
return false
}
}
... ...
package xerr
import (
"fmt"
)
/**
常用通用固定错误
*/
type CodeError struct {
errCode uint32
errMsg string
InternalError error
}
// GetErrCode 返回给前端的错误码
func (e *CodeError) GetErrCode() uint32 {
return e.errCode
}
// GetErrMsg 返回给前端显示端错误信息
func (e *CodeError) GetErrMsg() string {
if e.errMsg == "" {
return MapErrMsg(e.errCode)
}
return e.errMsg
}
func (e *CodeError) Error() string {
if e.InternalError != nil {
return fmt.Sprintf("ErrCode:%d,ErrMsg:%s InternalError:%s", e.errCode, e.errMsg, e.InternalError.Error())
}
return fmt.Sprintf("ErrCode:%d,ErrMsg:%s", e.errCode, e.errMsg)
}
/*
指定错误码的错误
*/
func NewCodeErr(errCode uint32, err error) *CodeError {
return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode), InternalError: err}
}
func NewCodeErrMsg(errCode uint32, err error, msg string) *CodeError {
return &CodeError{errCode: errCode, errMsg: msg, InternalError: err}
}
/*
默认的服务错误
*/
func NewErr(err error) *CodeError {
return &CodeError{errCode: SERVER_COMMON_ERROR, InternalError: err}
}
func NewErrMsg(errMsg string) *CodeError {
return &CodeError{errCode: SERVER_COMMON_ERROR, errMsg: errMsg}
}
func NewErrMsgErr(errMsg string, err error) *CodeError {
return &CodeError{errCode: SERVER_COMMON_ERROR, errMsg: errMsg, InternalError: err}
}
... ...