commit cca66faed5526041a2682ab60e4bc9def77e92c9 Author: liangzai <2440983361@qq.com> Date: Sun Nov 3 15:28:10 2024 +0800 first diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e935659 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# idea +.idea +.idea/ +*.iml +.DS_Store +**/.DS_Store + +#deploy data + +data/* +!data/.gitkeep + +# gitlab ci +.cache + +#vscode +.vscode +.vscode/ + + diff --git a/app/user/cmd/api/.air.toml b/app/user/cmd/api/.air.toml new file mode 100644 index 0000000..5c90bb6 --- /dev/null +++ b/app/user/cmd/api/.air.toml @@ -0,0 +1,6 @@ +root = "." +[build] +cmd = "go build -o ./tmp/main ./app/user/cmd/api/user.go" # 指定 user-api 的入口文件 +bin = "./tmp/main" +include_ext = ["go", "tmpl", "html"] +exclude_dir = ["tmp", "deploy", "data"] diff --git a/app/user/cmd/api/Dockerfile b/app/user/cmd/api/Dockerfile new file mode 100644 index 0000000..08c50df --- /dev/null +++ b/app/user/cmd/api/Dockerfile @@ -0,0 +1,31 @@ +FROM golang:1.22.4-alpine AS builder + +LABEL stage=gobuilder + +ENV CGO_ENABLED 0 +ENV GOPROXY https://goproxy.cn,direct +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories + +RUN apk update --no-cache && apk add --no-cache tzdata + +WORKDIR /build + +ADD go.mod . +ADD go.sum . +RUN go mod download +COPY . . +COPY app/user/cmd/api/etc /app/etc +RUN go build -ldflags="-s -w" -o /app/user app/user/cmd/api/.\user.go + + +FROM scratch + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai +ENV TZ Asia/Shanghai + +WORKDIR /app +COPY --from=builder /app/user /app/user +COPY --from=builder /app/etc /app/etc + +CMD ["./user", "-f", "etc/user.yaml"] diff --git a/app/user/cmd/api/desc/auth/auth.api b/app/user/cmd/api/desc/auth/auth.api new file mode 100644 index 0000000..d63683c --- /dev/null +++ b/app/user/cmd/api/desc/auth/auth.api @@ -0,0 +1,16 @@ +syntax = "v1" + +info ( + title: "认证服务" + desc: "认证服务" + author: "Liangzai" + email: "2440983361@qq.com" +) + +type ( + sendSmsReq { + Mobile string `json:"mobile" validate:"required,mobile"` + actionType string `json:"actionType" validate:"required"` + } +) + diff --git a/app/user/cmd/api/desc/user.api b/app/user/cmd/api/desc/user.api new file mode 100644 index 0000000..e09be06 --- /dev/null +++ b/app/user/cmd/api/desc/user.api @@ -0,0 +1,58 @@ +syntax = "v1" + +info ( + title: "用户中心服务" + desc: "用户中心服务" + author: "Liangzai" + email: "2440983361@qq.com" + version: "v1" +) + +import ( + "user/user.api" + "auth/auth.api" +) + +//============================> user v1 <============================ +//no need login +@server ( + prefix: user/v1 + group: user +) +service user { + @doc "register" + @handler register + post /user/register (RegisterReq) returns (RegisterResp) + + @doc "login" + @handler login + post /user/login (LoginReq) returns (LoginResp) +} + +//need login +@server ( + prefix: user/v1 + group: user + jwt: JwtAuth +) +service user { + @doc "get user info" + @handler detail + post /user/detail (UserInfoReq) returns (UserInfoResp) + + @doc "wechat mini auth" + @handler wxMiniAuth + post /user/wxMiniAuth (WXMiniAuthReq) returns (WXMiniAuthResp) +} + +//============================> auth v1 <============================ +@server ( + prefix: auth/v1 + group: auth +) +service user { + @doc "get mobile verify code" + @handler sendSms + post /auth/sendSms (sendSmsReq) +} + diff --git a/app/user/cmd/api/desc/user/user.api b/app/user/cmd/api/desc/user/user.api new file mode 100644 index 0000000..d8292ae --- /dev/null +++ b/app/user/cmd/api/desc/user/user.api @@ -0,0 +1,58 @@ +syntax = "v1" + +info ( + title: "用户实例" + desc: "用户实例" + author: "Liangzai" + email: "2440983361@qq.com" +) + +type User { + Id int64 `json:"id"` + Mobile string `json:"mobile"` +} + +type ( + RegisterReq { + Mobile string `json:"mobile" validate:"required,mobile"` + Password string `json:"password" validate:"required"` + } + RegisterResp { + AccessToken string `json:"accessToken"` + AccessExpire int64 `json:"accessExpire"` + RefreshAfter int64 `json:"refreshAfter"` + } +) + +type ( + LoginReq { + Mobile string `json:"mobile"` + Password string `json:"password"` + } + LoginResp { + AccessToken string `json:"accessToken"` + AccessExpire int64 `json:"accessExpire"` + RefreshAfter int64 `json:"refreshAfter"` + } +) + +type ( + WXMiniAuthReq { + Code string `json:"code"` + IV string `json:"iv"` + EncryptedData string `json:"encryptedData"` + } + WXMiniAuthResp { + AccessToken string `json:"accessToken"` + AccessExpire int64 `json:"accessExpire"` + RefreshAfter int64 `json:"refreshAfter"` + } +) + +type ( + UserInfoReq {} + UserInfoResp { + UserInfo User `json:"userInfo"` + } +) + diff --git a/app/user/cmd/api/etc/user.yaml b/app/user/cmd/api/etc/user.yaml new file mode 100644 index 0000000..fee31fe --- /dev/null +++ b/app/user/cmd/api/etc/user.yaml @@ -0,0 +1,19 @@ +Name: user +Host: 0.0.0.0 +Port: 8888 +DataSource: "qnc:V3j@K9!qL5#wX4$p@tcp(127.0.0.1:20001)/qnc?charset=utf8mb4&parseTime=True&loc=Local" +CacheRedis: + - Host: "127.0.0.1:20002" + Pass: "A!02Myl>H}ZQfpxC" # Redis 密码,如果未设置则留空 + Type: "node" # 单节点模式 +JwtAuth: + AccessSecret: "WUvoIwL-FK0qnlxhvxR9tV6SjfOpeJMpKmY2QvT99lA" + AccessExpire: 86400 # JWT过期时间 + RefreshAfter: 43200 # 更新时间 +VerifyCode: + AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9" + AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65" + EndpointURL: "dysmsapi.aliyuncs.com" + SignName: "天远数据" + TemplateCode: "SMS_474525324" + ValidTime: 300 \ No newline at end of file diff --git a/app/user/cmd/api/internal/config/config.go b/app/user/cmd/api/internal/config/config.go new file mode 100644 index 0000000..192ae21 --- /dev/null +++ b/app/user/cmd/api/internal/config/config.go @@ -0,0 +1,29 @@ +package config + +import ( + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/rest" +) + +type Config struct { + rest.RestConf + DataSource string + CacheRedis cache.CacheConf + JwtAuth JwtAuth // JWT 鉴权相关配置 + VerifyCode VerifyCode +} + +// JwtAuth 用于 JWT 鉴权配置 +type JwtAuth struct { + AccessSecret string // JWT 密钥,用于签发 Token + AccessExpire int64 // Token 过期时间,单位为秒 + RefreshAfter int64 +} +type VerifyCode struct { + AccessKeyID string + AccessKeySecret string + EndpointURL string + SignName string + TemplateCode string + ValidTime int +} diff --git a/app/user/cmd/api/internal/handler/auth/sendsmshandler.go b/app/user/cmd/api/internal/handler/auth/sendsmshandler.go new file mode 100644 index 0000000..02b65e3 --- /dev/null +++ b/app/user/cmd/api/internal/handler/auth/sendsmshandler.go @@ -0,0 +1,29 @@ +package auth + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/auth" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func SendSmsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.SendSmsReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := auth.NewSendSmsLogic(r.Context(), svcCtx) + err := l.SendSms(&req) + result.HttpResult(r, w, nil, err) + } +} diff --git a/app/user/cmd/api/internal/handler/routes.go b/app/user/cmd/api/internal/handler/routes.go new file mode 100644 index 0000000..5e045aa --- /dev/null +++ b/app/user/cmd/api/internal/handler/routes.go @@ -0,0 +1,63 @@ +// Code generated by goctl. DO NOT EDIT. +package handler + +import ( + "net/http" + + auth "qnc-server/app/user/cmd/api/internal/handler/auth" + user "qnc-server/app/user/cmd/api/internal/handler/user" + "qnc-server/app/user/cmd/api/internal/svc" + + "github.com/zeromicro/go-zero/rest" +) + +func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { + server.AddRoutes( + []rest.Route{ + { + // get mobile verify code + Method: http.MethodPost, + Path: "/auth/sendSms", + Handler: auth.SendSmsHandler(serverCtx), + }, + }, + rest.WithPrefix("/auth/v1"), + ) + + server.AddRoutes( + []rest.Route{ + { + // login + Method: http.MethodPost, + Path: "/user/login", + Handler: user.LoginHandler(serverCtx), + }, + { + // register + Method: http.MethodPost, + Path: "/user/register", + Handler: user.RegisterHandler(serverCtx), + }, + }, + rest.WithPrefix("/user/v1"), + ) + + server.AddRoutes( + []rest.Route{ + { + // get user info + Method: http.MethodPost, + Path: "/user/detail", + Handler: user.DetailHandler(serverCtx), + }, + { + // wechat mini auth + Method: http.MethodPost, + Path: "/user/wxMiniAuth", + Handler: user.WxMiniAuthHandler(serverCtx), + }, + }, + rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret), + rest.WithPrefix("/user/v1"), + ) +} diff --git a/app/user/cmd/api/internal/handler/user/detailhandler.go b/app/user/cmd/api/internal/handler/user/detailhandler.go new file mode 100644 index 0000000..1d10a34 --- /dev/null +++ b/app/user/cmd/api/internal/handler/user/detailhandler.go @@ -0,0 +1,29 @@ +package user + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/user" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func DetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.UserInfoReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := user.NewDetailLogic(r.Context(), svcCtx) + resp, err := l.Detail(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/user/loginhandler.go b/app/user/cmd/api/internal/handler/user/loginhandler.go new file mode 100644 index 0000000..da20595 --- /dev/null +++ b/app/user/cmd/api/internal/handler/user/loginhandler.go @@ -0,0 +1,29 @@ +package user + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/user" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.LoginReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := user.NewLoginLogic(r.Context(), svcCtx) + resp, err := l.Login(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/user/registerhandler.go b/app/user/cmd/api/internal/handler/user/registerhandler.go new file mode 100644 index 0000000..12db94e --- /dev/null +++ b/app/user/cmd/api/internal/handler/user/registerhandler.go @@ -0,0 +1,29 @@ +package user + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/user" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func RegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.RegisterReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := user.NewRegisterLogic(r.Context(), svcCtx) + resp, err := l.Register(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/handler/user/wxminiauthhandler.go b/app/user/cmd/api/internal/handler/user/wxminiauthhandler.go new file mode 100644 index 0000000..d53d5fe --- /dev/null +++ b/app/user/cmd/api/internal/handler/user/wxminiauthhandler.go @@ -0,0 +1,29 @@ +package user + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "qnc-server/app/user/cmd/api/internal/logic/user" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" +) + +func WxMiniAuthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.WXMiniAuthReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := user.NewWxMiniAuthLogic(r.Context(), svcCtx) + resp, err := l.WxMiniAuth(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/user/cmd/api/internal/logic/auth/sendsmslogic.go b/app/user/cmd/api/internal/logic/auth/sendsmslogic.go new file mode 100644 index 0000000..85765eb --- /dev/null +++ b/app/user/cmd/api/internal/logic/auth/sendsmslogic.go @@ -0,0 +1,98 @@ +package auth + +import ( + "context" + "fmt" + "github.com/pkg/errors" + "math/rand" + "qnc-server/common/xerr" + "time" + + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v3/client" + "github.com/alibabacloud-go/tea-utils/v2/service" + "github.com/alibabacloud-go/tea/tea" + "github.com/zeromicro/go-zero/core/logx" +) + +type SendSmsLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLogic { + return &SendSmsLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error { + // 检查手机号是否在一分钟内已发送过验证码 + redisKey := fmt.Sprintf("%s:%s", req.ActionType, req.Mobile) + exists, err := l.svcCtx.Redis.Exists(redisKey) + if err != nil { + return err + } + + if exists { + // 如果 Redis 中已经存在标记,说明在 1 分钟内请求过,返回错误 + return errors.Wrapf(xerr.NewErrMsg("一分钟内不能重复发送验证码"), "手机号1分钟内重复请求发送二维码, 手机号: %s", req.Mobile) + } + + code := fmt.Sprintf("%06d", rand.New(rand.NewSource(time.Now().UnixNano())).Intn(1000000)) + + // 发送短信 + smsResp, err := l.sendSmsRequest(req.Mobile, code) + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送失败, 调用阿里客户端失败: %+v", err) + } + if *smsResp.Body.Code != "OK" { + return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送失败, 阿里客户端响应失败: %s", *smsResp.Body.Message) + } + + // 将验证码保存到 Redis,设置过期时间 + err = l.svcCtx.Redis.Setex(req.Mobile, code, l.svcCtx.Config.VerifyCode.ValidTime) // 验证码有效期5分钟 + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "缓存失败, 验证码设置过期时间失败: %+v", err) + } + // 在 Redis 中设置 1 分钟的标记,限制重复请求 + err = l.svcCtx.Redis.Setex(redisKey, code, 60) // 标记 1 分钟内不能重复请求 + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "缓存失败, 验证码设置限制重复请求失败: %+v", err) + } + return nil +} + +// CreateClient 创建阿里云短信客户端 +func (l *SendSmsLogic) CreateClient() (*dysmsapi.Client, error) { + config := &openapi.Config{ + AccessKeyId: &l.svcCtx.Config.VerifyCode.AccessKeyID, + AccessKeySecret: &l.svcCtx.Config.VerifyCode.AccessKeySecret, + } + config.Endpoint = tea.String(l.svcCtx.Config.VerifyCode.EndpointURL) + return dysmsapi.NewClient(config) +} + +// sendSmsRequest 发送短信请求 +func (l *SendSmsLogic) sendSmsRequest(mobile, code string) (*dysmsapi.SendSmsResponse, error) { + // 初始化阿里云短信客户端 + cli, err := l.CreateClient() + if err != nil { + return nil, err + } + + request := &dysmsapi.SendSmsRequest{ + SignName: tea.String(l.svcCtx.Config.VerifyCode.SignName), + TemplateCode: tea.String(l.svcCtx.Config.VerifyCode.TemplateCode), + PhoneNumbers: tea.String(mobile), + TemplateParam: tea.String(fmt.Sprintf("{\"code\":\"%s\"}", code)), + } + runtime := &service.RuntimeOptions{} + return cli.SendSmsWithOptions(request, runtime) +} diff --git a/app/user/cmd/api/internal/logic/user/detaillogic.go b/app/user/cmd/api/internal/logic/user/detaillogic.go new file mode 100644 index 0000000..caf242d --- /dev/null +++ b/app/user/cmd/api/internal/logic/user/detaillogic.go @@ -0,0 +1,30 @@ +package user + +import ( + "context" + + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type DetailLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DetailLogic { + return &DetailLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *DetailLogic) Detail(req *types.UserInfoReq) (resp *types.UserInfoResp, err error) { + // todo: add your logic here and delete this line + + return +} diff --git a/app/user/cmd/api/internal/logic/user/loginlogic.go b/app/user/cmd/api/internal/logic/user/loginlogic.go new file mode 100644 index 0000000..6ce5926 --- /dev/null +++ b/app/user/cmd/api/internal/logic/user/loginlogic.go @@ -0,0 +1,30 @@ +package user + +import ( + "context" + + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type LoginLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic { + return &LoginLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) { + // todo: add your logic here and delete this line + + return +} diff --git a/app/user/cmd/api/internal/logic/user/registerlogic.go b/app/user/cmd/api/internal/logic/user/registerlogic.go new file mode 100644 index 0000000..0b2f562 --- /dev/null +++ b/app/user/cmd/api/internal/logic/user/registerlogic.go @@ -0,0 +1,83 @@ +package user + +import ( + "context" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + "qnc-server/app/user/model" + jwtx "qnc-server/common/jwt" + "qnc-server/common/tool" + "qnc-server/common/xerr" + + "github.com/zeromicro/go-zero/core/logx" +) + +var ErrUserAlreadyRegisterError = xerr.NewErrMsg("该手机号码已注册") + +type RegisterLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic { + return &RegisterLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterResp, err error) { + hasUser, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, req.Mobile) + if findUserErr != nil && findUserErr != model.ErrNotFound { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "mobile:%s,err:%v", req.Mobile, err) + } + if hasUser != nil { + return nil, errors.Wrapf(ErrUserAlreadyRegisterError, "Register user exists mobile:%s,err:%v", req.Mobile, err) + } + var userId int64 + if transErr := l.svcCtx.UserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { + user := new(model.User) + user.Mobile = req.Mobile + if len(user.Nickname) == 0 { + user.Nickname = tool.Krand(8, tool.KC_RAND_KIND_ALL) + } + if len(req.Password) > 0 { + user.Password = tool.Md5ByString(req.Password) + } + insertResult, userInsertErr := l.svcCtx.UserModel.Insert(ctx, session, user) + if userInsertErr != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Register db user Insert err:%v,user:%+v", userInsertErr, user) + } + lastId, lastInsertIdErr := insertResult.LastInsertId() + if lastInsertIdErr != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Register db user insertResult.LastInsertId err:%v,user:%+v", lastInsertIdErr, user) + } + userId = lastId + + userAuth := new(model.UserAuth) + userAuth.UserId = lastId + userAuth.AuthKey = req.Mobile + userAuth.AuthType = model.UserAuthTypeAppMobile + if _, userAuthInsertErr := l.svcCtx.UserAuthModel.Insert(ctx, session, userAuth); err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Register db user_auth Insert err:%v,userAuth:%v", err, userAuthInsertErr) + } + return nil + }); transErr != nil { + return nil, transErr + } + + token, generaErr := jwtx.GenerateJwtToken(userId, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire) + if generaErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "GenerateToken error userId : %d", userId) + } + + return &types.RegisterResp{ + AccessToken: token, + AccessExpire: l.svcCtx.Config.JwtAuth.AccessExpire, + RefreshAfter: l.svcCtx.Config.JwtAuth.RefreshAfter, + }, nil +} diff --git a/app/user/cmd/api/internal/logic/user/wxminiauthlogic.go b/app/user/cmd/api/internal/logic/user/wxminiauthlogic.go new file mode 100644 index 0000000..d268e62 --- /dev/null +++ b/app/user/cmd/api/internal/logic/user/wxminiauthlogic.go @@ -0,0 +1,30 @@ +package user + +import ( + "context" + + "qnc-server/app/user/cmd/api/internal/svc" + "qnc-server/app/user/cmd/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type WxMiniAuthLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewWxMiniAuthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WxMiniAuthLogic { + return &WxMiniAuthLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *WxMiniAuthLogic) WxMiniAuth(req *types.WXMiniAuthReq) (resp *types.WXMiniAuthResp, err error) { + // todo: add your logic here and delete this line + + return +} diff --git a/app/user/cmd/api/internal/svc/servicecontext.go b/app/user/cmd/api/internal/svc/servicecontext.go new file mode 100644 index 0000000..e9b095e --- /dev/null +++ b/app/user/cmd/api/internal/svc/servicecontext.go @@ -0,0 +1,30 @@ +package svc + +import ( + "github.com/zeromicro/go-zero/core/stores/redis" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "qnc-server/app/user/cmd/api/internal/config" + "qnc-server/app/user/model" +) + +type ServiceContext struct { + Config config.Config + Redis *redis.Redis + UserModel model.UserModel + UserAuthModel model.UserAuthModel +} + +func NewServiceContext(c config.Config) *ServiceContext { + db := sqlx.NewMysql(c.DataSource) // 创建数据库连接 + redisConf := redis.RedisConf{ + Host: c.CacheRedis[0].Host, + Pass: c.CacheRedis[0].Pass, + Type: c.CacheRedis[0].Type, // Redis 节点类型,如 "node" + } + return &ServiceContext{ + Config: c, + Redis: redis.MustNewRedis(redisConf), + UserModel: model.NewUserModel(db, c.CacheRedis), + UserAuthModel: model.NewUserAuthModel(db, c.CacheRedis), + } +} diff --git a/app/user/cmd/api/internal/types/types.go b/app/user/cmd/api/internal/types/types.go new file mode 100644 index 0000000..68a3718 --- /dev/null +++ b/app/user/cmd/api/internal/types/types.go @@ -0,0 +1,53 @@ +// Code generated by goctl. DO NOT EDIT. +package types + +type LoginReq struct { + Mobile string `json:"mobile"` + Password string `json:"password"` +} + +type LoginResp struct { + AccessToken string `json:"accessToken"` + AccessExpire int64 `json:"accessExpire"` + RefreshAfter int64 `json:"refreshAfter"` +} + +type RegisterReq struct { + Mobile string `json:"mobile" validate:"required,mobile"` + Password string `json:"password" validate:"required"` +} + +type RegisterResp struct { + AccessToken string `json:"accessToken"` + AccessExpire int64 `json:"accessExpire"` + RefreshAfter int64 `json:"refreshAfter"` +} + +type User struct { + Id int64 `json:"id"` + Mobile string `json:"mobile"` +} + +type UserInfoReq struct { +} + +type UserInfoResp struct { + UserInfo User `json:"userInfo"` +} + +type WXMiniAuthReq struct { + Code string `json:"code"` + IV string `json:"iv"` + EncryptedData string `json:"encryptedData"` +} + +type WXMiniAuthResp struct { + AccessToken string `json:"accessToken"` + AccessExpire int64 `json:"accessExpire"` + RefreshAfter int64 `json:"refreshAfter"` +} + +type SendSmsReq struct { + Mobile string `json:"mobile" validate:"required,mobile"` + ActionType string `json:"actionType" validate:"required"` +} diff --git a/app/user/cmd/api/user.go b/app/user/cmd/api/user.go new file mode 100644 index 0000000..cf25b5d --- /dev/null +++ b/app/user/cmd/api/user.go @@ -0,0 +1,31 @@ +package main + +import ( + "flag" + "fmt" + "qnc-server/app/user/cmd/api/internal/config" + "qnc-server/app/user/cmd/api/internal/handler" + "qnc-server/app/user/cmd/api/internal/svc" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/rest" +) + +var configFile = flag.String("f", "etc/user.yaml", "the config file") + +func main() { + flag.Parse() + + var c config.Config + conf.MustLoad(*configFile, &c) + + ctx := svc.NewServiceContext(c) + server := rest.MustNewServer(c.RestConf) + + defer server.Stop() + + handler.RegisterHandlers(server, ctx) + + fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) + server.Start() +} diff --git a/app/user/model/userauthmodel.go b/app/user/model/userauthmodel.go new file mode 100644 index 0000000..0812e1d --- /dev/null +++ b/app/user/model/userauthmodel.go @@ -0,0 +1,27 @@ +package model + +import ( + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +var _ UserAuthModel = (*customUserAuthModel)(nil) + +type ( + // UserAuthModel is an interface to be customized, add more methods here, + // and implement the added methods in customUserAuthModel. + UserAuthModel interface { + userAuthModel + } + + customUserAuthModel struct { + *defaultUserAuthModel + } +) + +// NewUserAuthModel returns a model for the database table. +func NewUserAuthModel(conn sqlx.SqlConn, c cache.CacheConf) UserAuthModel { + return &customUserAuthModel{ + defaultUserAuthModel: newUserAuthModel(conn, c), + } +} diff --git a/app/user/model/userauthmodel_gen.go b/app/user/model/userauthmodel_gen.go new file mode 100644 index 0000000..833d9b6 --- /dev/null +++ b/app/user/model/userauthmodel_gen.go @@ -0,0 +1,435 @@ +// Code generated by goctl. DO NOT EDIT! + +package model + +import ( + "context" + "database/sql" + "fmt" + "strings" + + "time" + + "github.com/Masterminds/squirrel" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlc" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" + "qnc-server/common/globalkey" +) + +var ( + userAuthFieldNames = builder.RawFieldNames(&UserAuth{}) + userAuthRows = strings.Join(userAuthFieldNames, ",") + userAuthRowsExpectAutoSet = strings.Join(stringx.Remove(userAuthFieldNames, "`id`", "`create_time`", "`update_time`"), ",") + userAuthRowsWithPlaceHolder = strings.Join(stringx.Remove(userAuthFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" + + cacheUserAuthIdPrefix = "cache:userAuth:id:" + cacheUserAuthAuthTypeAuthKeyPrefix = "cache:userAuth:authType:authKey:" + cacheUserAuthUserIdAuthTypePrefix = "cache:userAuth:userId:authType:" +) + +type ( + userAuthModel interface { + Insert(ctx context.Context, session sqlx.Session, data *UserAuth) (sql.Result, error) + FindOne(ctx context.Context, id int64) (*UserAuth, error) + FindOneByAuthTypeAuthKey(ctx context.Context, authType string, authKey string) (*UserAuth, error) + FindOneByUserIdAuthType(ctx context.Context, userId int64, authType string) (*UserAuth, error) + Update(ctx context.Context, session sqlx.Session, data *UserAuth) (sql.Result, error) + UpdateWithVersion(ctx context.Context, session sqlx.Session, data *UserAuth) error + Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error + SelectBuilder() squirrel.SelectBuilder + DeleteSoft(ctx context.Context, session sqlx.Session, data *UserAuth) error + FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error) + FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error) + FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*UserAuth, error) + FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserAuth, error) + FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserAuth, int64, error) + FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*UserAuth, error) + FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*UserAuth, error) + Delete(ctx context.Context, session sqlx.Session, id int64) error + } + + defaultUserAuthModel struct { + sqlc.CachedConn + table string + } + + UserAuth struct { + Id int64 `db:"id"` + CreateTime time.Time `db:"create_time"` + UpdateTime time.Time `db:"update_time"` + DeleteTime time.Time `db:"delete_time"` + DelState int64 `db:"del_state"` + Version int64 `db:"version"` // 版本号 + UserId int64 `db:"user_id"` + AuthKey string `db:"auth_key"` // 平台唯一id + AuthType string `db:"auth_type"` // 平台类型 + } +) + +func newUserAuthModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserAuthModel { + return &defaultUserAuthModel{ + CachedConn: sqlc.NewConn(conn, c), + table: "`user_auth`", + } +} + +func (m *defaultUserAuthModel) Insert(ctx context.Context, session sqlx.Session, data *UserAuth) (sql.Result, error) { + data.DeleteTime = time.Unix(0, 0) + data.DelState = globalkey.DelStateNo + userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey) + userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, data.Id) + userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, data.UserId, data.AuthType) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?)", m.table, userAuthRowsExpectAutoSet) + if session != nil { + return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.AuthKey, data.AuthType) + } + return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.AuthKey, data.AuthType) + }, userAuthAuthTypeAuthKeyKey, userAuthIdKey, userAuthUserIdAuthTypeKey) +} + +func (m *defaultUserAuthModel) FindOne(ctx context.Context, id int64) (*UserAuth, error) { + userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, id) + var resp UserAuth + err := m.QueryRowCtx(ctx, &resp, userAuthIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userAuthRows, m.table) + return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultUserAuthModel) FindOneByAuthTypeAuthKey(ctx context.Context, authType string, authKey string) (*UserAuth, error) { + userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, authType, authKey) + var resp UserAuth + err := m.QueryRowIndexCtx(ctx, &resp, userAuthAuthTypeAuthKeyKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where `auth_type` = ? and `auth_key` = ? and del_state = ? limit 1", userAuthRows, m.table) + if err := conn.QueryRowCtx(ctx, &resp, query, authType, authKey, globalkey.DelStateNo); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultUserAuthModel) FindOneByUserIdAuthType(ctx context.Context, userId int64, authType string) (*UserAuth, error) { + userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, userId, authType) + var resp UserAuth + err := m.QueryRowIndexCtx(ctx, &resp, userAuthUserIdAuthTypeKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where `user_id` = ? and `auth_type` = ? and del_state = ? limit 1", userAuthRows, m.table) + if err := conn.QueryRowCtx(ctx, &resp, query, userId, authType, globalkey.DelStateNo); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultUserAuthModel) Update(ctx context.Context, session sqlx.Session, newData *UserAuth) (sql.Result, error) { + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return nil, err + } + userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey) + userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, data.Id) + userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, data.UserId, data.AuthType) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userAuthRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.UserId, newData.AuthKey, newData.AuthType, newData.Id) + } + return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.UserId, newData.AuthKey, newData.AuthType, newData.Id) + }, userAuthAuthTypeAuthKeyKey, userAuthIdKey, userAuthUserIdAuthTypeKey) +} + +func (m *defaultUserAuthModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *UserAuth) error { + + oldVersion := newData.Version + newData.Version += 1 + + var sqlResult sql.Result + var err error + + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return err + } + userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey) + userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, data.Id) + userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, data.UserId, data.AuthType) + sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, userAuthRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.UserId, newData.AuthKey, newData.AuthType, newData.Id, oldVersion) + } + return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.UserId, newData.AuthKey, newData.AuthType, newData.Id, oldVersion) + }, userAuthAuthTypeAuthKeyKey, userAuthIdKey, userAuthUserIdAuthTypeKey) + if err != nil { + return err + } + updateCount, err := sqlResult.RowsAffected() + if err != nil { + return err + } + if updateCount == 0 { + return ErrNoRowsUpdate + } + + return nil +} + +func (m *defaultUserAuthModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *UserAuth) error { + data.DelState = globalkey.DelStateYes + data.DeleteTime = time.Now() + if err := m.UpdateWithVersion(ctx, session, data); err != nil { + return errors.Wrapf(errors.New("delete soft failed "), "UserAuthModel delete err : %+v", err) + } + return nil +} + +func (m *defaultUserAuthModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field") + } + + builder = builder.Columns("IFNULL(SUM(" + field + "),0)") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp float64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultUserAuthModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field") + } + + builder = builder.Columns("COUNT(" + field + ")") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp int64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultUserAuthModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*UserAuth, error) { + + builder = builder.Columns(userAuthRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return nil, err + } + + var resp []*UserAuth + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultUserAuthModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserAuth, error) { + + builder = builder.Columns(userAuthRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*UserAuth + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultUserAuthModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserAuth, int64, error) { + + total, err := m.FindCount(ctx, builder, "id") + if err != nil { + return nil, 0, err + } + + builder = builder.Columns(userAuthRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, total, err + } + + var resp []*UserAuth + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, total, nil + default: + return nil, total, err + } +} + +func (m *defaultUserAuthModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*UserAuth, error) { + + builder = builder.Columns(userAuthRows) + + if preMinId > 0 { + builder = builder.Where(" id < ? ", preMinId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*UserAuth + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultUserAuthModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*UserAuth, error) { + + builder = builder.Columns(userAuthRows) + + if preMaxId > 0 { + builder = builder.Where(" id > ? ", preMaxId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*UserAuth + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultUserAuthModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error { + + return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error { + return fn(ctx, session) + }) + +} + +func (m *defaultUserAuthModel) SelectBuilder() squirrel.SelectBuilder { + return squirrel.Select().From(m.table) +} +func (m *defaultUserAuthModel) Delete(ctx context.Context, session sqlx.Session, id int64) error { + data, err := m.FindOne(ctx, id) + if err != nil { + return err + } + + userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey) + userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, id) + userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, data.UserId, data.AuthType) + _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("delete from %s where `id` = ?", m.table) + if session != nil { + return session.ExecCtx(ctx, query, id) + } + return conn.ExecCtx(ctx, query, id) + }, userAuthAuthTypeAuthKeyKey, userAuthIdKey, userAuthUserIdAuthTypeKey) + return err +} +func (m *defaultUserAuthModel) formatPrimary(primary interface{}) string { + return fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, primary) +} +func (m *defaultUserAuthModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userAuthRows, m.table) + return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo) +} + +func (m *defaultUserAuthModel) tableName() string { + return m.table +} diff --git a/app/user/model/usermodel.go b/app/user/model/usermodel.go new file mode 100644 index 0000000..8123712 --- /dev/null +++ b/app/user/model/usermodel.go @@ -0,0 +1,27 @@ +package model + +import ( + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +var _ UserModel = (*customUserModel)(nil) + +type ( + // UserModel is an interface to be customized, add more methods here, + // and implement the added methods in customUserModel. + UserModel interface { + userModel + } + + customUserModel struct { + *defaultUserModel + } +) + +// NewUserModel returns a model for the database table. +func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel { + return &customUserModel{ + defaultUserModel: newUserModel(conn, c), + } +} diff --git a/app/user/model/usermodel_gen.go b/app/user/model/usermodel_gen.go new file mode 100644 index 0000000..77ebe19 --- /dev/null +++ b/app/user/model/usermodel_gen.go @@ -0,0 +1,410 @@ +// Code generated by goctl. DO NOT EDIT! + +package model + +import ( + "context" + "database/sql" + "fmt" + "strings" + + "time" + + "github.com/Masterminds/squirrel" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlc" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" + "qnc-server/common/globalkey" +) + +var ( + userFieldNames = builder.RawFieldNames(&User{}) + userRows = strings.Join(userFieldNames, ",") + userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), ",") + userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" + + cacheUserIdPrefix = "cache:user:id:" + cacheUserMobilePrefix = "cache:user:mobile:" +) + +type ( + userModel interface { + Insert(ctx context.Context, session sqlx.Session, data *User) (sql.Result, error) + FindOne(ctx context.Context, id int64) (*User, error) + FindOneByMobile(ctx context.Context, mobile string) (*User, error) + Update(ctx context.Context, session sqlx.Session, data *User) (sql.Result, error) + UpdateWithVersion(ctx context.Context, session sqlx.Session, data *User) error + Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error + SelectBuilder() squirrel.SelectBuilder + DeleteSoft(ctx context.Context, session sqlx.Session, data *User) error + FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error) + FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error) + FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*User, error) + FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*User, error) + FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*User, int64, error) + FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*User, error) + FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*User, error) + Delete(ctx context.Context, session sqlx.Session, id int64) error + } + + defaultUserModel struct { + sqlc.CachedConn + table string + } + + User struct { + Id int64 `db:"id"` + CreateTime time.Time `db:"create_time"` + UpdateTime time.Time `db:"update_time"` + DeleteTime time.Time `db:"delete_time"` + DelState int64 `db:"del_state"` + Version int64 `db:"version"` // 版本号 + Mobile string `db:"mobile"` + Password string `db:"password"` + Nickname string `db:"nickname"` + Info string `db:"info"` + } +) + +func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel { + return &defaultUserModel{ + CachedConn: sqlc.NewConn(conn, c), + table: "`user`", + } +} + +func (m *defaultUserModel) Insert(ctx context.Context, session sqlx.Session, data *User) (sql.Result, error) { + data.DeleteTime = time.Unix(0, 0) + data.DelState = globalkey.DelStateNo + userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id) + userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet) + if session != nil { + return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info) + } + return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info) + }, userIdKey, userMobileKey) +} + +func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) { + userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id) + var resp User + err := m.QueryRowCtx(ctx, &resp, userIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userRows, m.table) + return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultUserModel) FindOneByMobile(ctx context.Context, mobile string) (*User, error) { + userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile) + var resp User + err := m.QueryRowIndexCtx(ctx, &resp, userMobileKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where `mobile` = ? and del_state = ? limit 1", userRows, m.table) + if err := conn.QueryRowCtx(ctx, &resp, query, mobile, globalkey.DelStateNo); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultUserModel) Update(ctx context.Context, session sqlx.Session, newData *User) (sql.Result, error) { + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return nil, err + } + userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id) + userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Id) + } + return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Id) + }, userIdKey, userMobileKey) +} + +func (m *defaultUserModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *User) error { + + oldVersion := newData.Version + newData.Version += 1 + + var sqlResult sql.Result + var err error + + data, err := m.FindOne(ctx, newData.Id) + if err != nil { + return err + } + userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id) + userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile) + sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, userRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Id, oldVersion) + } + return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Id, oldVersion) + }, userIdKey, userMobileKey) + if err != nil { + return err + } + updateCount, err := sqlResult.RowsAffected() + if err != nil { + return err + } + if updateCount == 0 { + return ErrNoRowsUpdate + } + + return nil +} + +func (m *defaultUserModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *User) error { + data.DelState = globalkey.DelStateYes + data.DeleteTime = time.Now() + if err := m.UpdateWithVersion(ctx, session, data); err != nil { + return errors.Wrapf(errors.New("delete soft failed "), "UserModel delete err : %+v", err) + } + return nil +} + +func (m *defaultUserModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field") + } + + builder = builder.Columns("IFNULL(SUM(" + field + "),0)") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp float64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultUserModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field") + } + + builder = builder.Columns("COUNT(" + field + ")") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp int64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultUserModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*User, error) { + + builder = builder.Columns(userRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return nil, err + } + + var resp []*User + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultUserModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*User, error) { + + builder = builder.Columns(userRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*User + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultUserModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*User, int64, error) { + + total, err := m.FindCount(ctx, builder, "id") + if err != nil { + return nil, 0, err + } + + builder = builder.Columns(userRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, total, err + } + + var resp []*User + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, total, nil + default: + return nil, total, err + } +} + +func (m *defaultUserModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*User, error) { + + builder = builder.Columns(userRows) + + if preMinId > 0 { + builder = builder.Where(" id < ? ", preMinId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*User + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultUserModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*User, error) { + + builder = builder.Columns(userRows) + + if preMaxId > 0 { + builder = builder.Where(" id > ? ", preMaxId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*User + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultUserModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error { + + return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error { + return fn(ctx, session) + }) + +} + +func (m *defaultUserModel) SelectBuilder() squirrel.SelectBuilder { + return squirrel.Select().From(m.table) +} +func (m *defaultUserModel) Delete(ctx context.Context, session sqlx.Session, id int64) error { + data, err := m.FindOne(ctx, id) + if err != nil { + return err + } + + userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id) + userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile) + _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("delete from %s where `id` = ?", m.table) + if session != nil { + return session.ExecCtx(ctx, query, id) + } + return conn.ExecCtx(ctx, query, id) + }, userIdKey, userMobileKey) + return err +} +func (m *defaultUserModel) formatPrimary(primary interface{}) string { + return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary) +} +func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userRows, m.table) + return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo) +} + +func (m *defaultUserModel) tableName() string { + return m.table +} diff --git a/app/user/model/vars.go b/app/user/model/vars.go new file mode 100644 index 0000000..4723f1b --- /dev/null +++ b/app/user/model/vars.go @@ -0,0 +1,15 @@ +package model + +import ( + "errors" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +var ErrNotFound = sqlx.ErrNotFound +var ErrNoRowsUpdate = errors.New("update db no rows change") + +var UserAuthTypeAppMobile string = "app_mobile" //平台内部 +var UserAuthTypeAppWechat string = "app_wechat" //微信小程序 +var UserAuthTypeH5Mobile string = "h5_mobile" +var UserAuthTypeWxMini string = "wx_mini" +var UserAuthTypeWxOfficialAccount string = "wx_official_account" diff --git a/common/ctxdata/ctxData.go b/common/ctxdata/ctxData.go new file mode 100644 index 0000000..8b64c2d --- /dev/null +++ b/common/ctxdata/ctxData.go @@ -0,0 +1,22 @@ +package ctxdata + +import ( + "context" + "encoding/json" + "github.com/zeromicro/go-zero/core/logx" +) + +// CtxKeyJwtUserId get uid from ctx +var CtxKeyJwtUserId = "jwtUserId" + +func GetUidFromCtx(ctx context.Context) int64 { + var uid int64 + if jsonUid, ok := ctx.Value(CtxKeyJwtUserId).(json.Number); ok { + if int64Uid, err := jsonUid.Int64(); err == nil { + uid = int64Uid + } else { + logx.WithContext(ctx).Errorf("GetUidFromCtx err : %+v", err) + } + } + return uid +} diff --git a/common/globalkey/constantKey.go b/common/globalkey/constantKey.go new file mode 100644 index 0000000..584938d --- /dev/null +++ b/common/globalkey/constantKey.go @@ -0,0 +1,14 @@ +package globalkey + +/** +global constant key +*/ + +//软删除 +var DelStateNo int64 = 0 //未删除 +var DelStateYes int64 = 1 //已删除 + +//时间格式化模版 +var DateTimeFormatTplStandardDateTime = "Y-m-d H:i:s" +var DateTimeFormatTplStandardDate = "Y-m-d" +var DateTimeFormatTplStandardTime = "H:i:s" diff --git a/common/globalkey/redisCacheKey.go b/common/globalkey/redisCacheKey.go new file mode 100644 index 0000000..9296e19 --- /dev/null +++ b/common/globalkey/redisCacheKey.go @@ -0,0 +1,9 @@ +package globalkey + +/** +redis key except "model cache key" in here, +but "model cache key" in model +*/ + +// CacheUserTokenKey /** 用户登陆的token +const CacheUserTokenKey = "user_token:%d" diff --git a/common/interceptor/rpcserver/loggerInterceptor.go b/common/interceptor/rpcserver/loggerInterceptor.go new file mode 100644 index 0000000..98ef6ce --- /dev/null +++ b/common/interceptor/rpcserver/loggerInterceptor.go @@ -0,0 +1,39 @@ +package rpcserver + +import ( + "context" + + "qnc-server/common/xerr" + + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/logx" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +/** +* @Description rpc service logger interceptor +* @Author Mikael +* @Date 2021/1/9 13:35 +* @Version 1.0 +**/ + +func LoggerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { + + resp, err = handler(ctx, req) + if err != nil { + causeErr := errors.Cause(err) // err类型 + if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型 + logx.WithContext(ctx).Errorf("【RPC-SRV-ERR】 %+v", err) + + //转成grpc err + err = status.Error(codes.Code(e.GetErrCode()), e.GetErrMsg()) + } else { + logx.WithContext(ctx).Errorf("【RPC-SRV-ERR】 %+v", err) + } + + } + + return resp, err +} diff --git a/common/jwt/jwtx.go b/common/jwt/jwtx.go new file mode 100644 index 0000000..bae8de9 --- /dev/null +++ b/common/jwt/jwtx.go @@ -0,0 +1,68 @@ +package jwtx + +import ( + "errors" + "github.com/golang-jwt/jwt/v4" + "strconv" + "time" +) + +// Token 生成逻辑的函数,接收 userId、过期时间和密钥,返回生成的 token +func GenerateJwtToken(userId int64, secret string, expireTime int64) (string, error) { + // 获取当前时间戳 + now := time.Now().Unix() + // 定义 JWT Claims + claims := jwt.MapClaims{ + "exp": now + expireTime, // token 过期时间 + "iat": now, // 签发时间 + "userId": userId, // 用户ID + } + + // 创建新的 JWT token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + // 使用密钥对 token 签名 + signedToken, err := token.SignedString([]byte(secret)) + if err != nil { + return "", err + } + + return signedToken, nil +} +func ParseJwtToken(tokenStr string, secret string) (int64, error) { + token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) { + return []byte(secret), nil + }) + + if err != nil || !token.Valid { + return 0, errors.New("invalid JWT") + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok || !token.Valid { + return 0, errors.New("invalid JWT claims") + } + + // 从 claims 中提取 userId + userIdRaw, ok := claims["userId"] + if !ok { + return 0, errors.New("userId not found in JWT") + } + + // 处理不同类型的 userId,确保它被转换为 int64 + switch userId := userIdRaw.(type) { + case float64: + return int64(userId), nil + case int64: + return userId, nil + case string: + // 如果 userId 是字符串,可以尝试将其转换为 int64 + parsedId, err := strconv.ParseInt(userId, 10, 64) + if err != nil { + return 0, errors.New("invalid userId in JWT") + } + return parsedId, nil + default: + return 0, errors.New("unsupported userId type in JWT") + } +} diff --git a/common/kqueue/message.go b/common/kqueue/message.go new file mode 100644 index 0000000..e1df433 --- /dev/null +++ b/common/kqueue/message.go @@ -0,0 +1,8 @@ +//KqMessage +package kqueue + +//第三方支付回调更改支付状态通知 +type ThirdPaymentUpdatePayStatusNotifyMessage struct { + PayStatus int64 `json:"payStatus"` + OrderSn string `json:"orderSn"` +} diff --git a/common/middleware/commonJwtAuthMiddleware.go b/common/middleware/commonJwtAuthMiddleware.go new file mode 100644 index 0000000..db0dae7 --- /dev/null +++ b/common/middleware/commonJwtAuthMiddleware.go @@ -0,0 +1,31 @@ +package middleware + +import ( + "github.com/zeromicro/go-zero/rest/handler" + "net/http" +) + +// CommonJwtAuthMiddleware : with jwt on the verification, no jwt on the verification +type CommonJwtAuthMiddleware struct { + secret string +} + +func NewCommonJwtAuthMiddleware(secret string) *CommonJwtAuthMiddleware { + return &CommonJwtAuthMiddleware{ + secret: secret, + } +} + +func (m *CommonJwtAuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if len(r.Header.Get("Authorization")) > 0 { + //has jwt Authorization + authHandler := handler.Authorize(m.secret) + authHandler(next).ServeHTTP(w, r) + return + } else { + //no jwt Authorization + next(w, r) + } + } +} diff --git a/common/result/httpResult.go b/common/result/httpResult.go new file mode 100644 index 0000000..478e60d --- /dev/null +++ b/common/result/httpResult.go @@ -0,0 +1,89 @@ +package result + +import ( + "fmt" + "net/http" + + "qnc-server/common/xerr" + + "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 { + httpx.WriteJson(w, http.StatusOK, Success(resp)) + } 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("【API-ERR】 : %+v ", err) + + httpx.WriteJson(w, http.StatusBadRequest, Error(errcode, errmsg)) + } +} + +// 授权的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)) +} + +// http 参数校验失败返回 +func ParamValidateErrorResult(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.StatusOK, Error(xerr.PARAM_VERIFICATION_ERROR, err.Error())) +} diff --git a/common/result/jobResult.go b/common/result/jobResult.go new file mode 100644 index 0000000..d18476f --- /dev/null +++ b/common/result/jobResult.go @@ -0,0 +1,44 @@ +package result + +import ( + "context" + + "qnc-server/common/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 + } +} diff --git a/common/result/responseBean.go b/common/result/responseBean.go new file mode 100644 index 0000000..d29e1e0 --- /dev/null +++ b/common/result/responseBean.go @@ -0,0 +1,21 @@ +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{200, "OK", data} +} + +type ResponseErrorBean struct { + Code uint32 `json:"code"` + Msg string `json:"msg"` +} + +func Error(errCode uint32, errMsg string) *ResponseErrorBean { + return &ResponseErrorBean{errCode, errMsg} +} diff --git a/common/tool/coinconvert.go b/common/tool/coinconvert.go new file mode 100644 index 0000000..f04d461 --- /dev/null +++ b/common/tool/coinconvert.go @@ -0,0 +1,19 @@ +package tool + +import "github.com/shopspring/decimal" + +var oneHundredDecimal decimal.Decimal = decimal.NewFromInt(100) + +//分转元 +func Fen2Yuan(fen int64) float64 { + y, _ := decimal.NewFromInt(fen).Div(oneHundredDecimal).Truncate(2).Float64() + return y +} + +//元转分 +func Yuan2Fen(yuan float64) int64 { + + f, _ := decimal.NewFromFloat(yuan).Mul(oneHundredDecimal).Truncate(0).Float64() + return int64(f) + +} diff --git a/common/tool/encryption.go b/common/tool/encryption.go new file mode 100644 index 0000000..b94f562 --- /dev/null +++ b/common/tool/encryption.go @@ -0,0 +1,23 @@ +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)) +} diff --git a/common/tool/krand.go b/common/tool/krand.go new file mode 100644 index 0000000..fb5b869 --- /dev/null +++ b/common/tool/krand.go @@ -0,0 +1,28 @@ +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) +} diff --git a/common/tool/krand_test.go b/common/tool/krand_test.go new file mode 100644 index 0000000..c5c0356 --- /dev/null +++ b/common/tool/krand_test.go @@ -0,0 +1,8 @@ +package tool + +import "testing" + +func TestMd5ByString(t *testing.T) { + s := Md5ByString("AAA") + t.Log(s) +} diff --git a/common/tool/placeholders.go b/common/tool/placeholders.go new file mode 100644 index 0000000..53e28d1 --- /dev/null +++ b/common/tool/placeholders.go @@ -0,0 +1,15 @@ +package tool + +import "strings" + +//替换 +func InPlaceholders(n int) string { + var b strings.Builder + for i := 0; i < n-1; i++ { + b.WriteString("?,") + } + if n > 0 { + b.WriteString("?") + } + return b.String() +} diff --git a/common/uniqueid/sn.go b/common/uniqueid/sn.go new file mode 100644 index 0000000..9660b9d --- /dev/null +++ b/common/uniqueid/sn.go @@ -0,0 +1,20 @@ +package uniqueid + +import ( + "fmt" + "qnc-server/common/tool" + "time" +) + +// 生成sn单号 +type SnPrefix string + +const ( + SN_PREFIX_HOMESTAY_ORDER SnPrefix = "HSO" //民宿订单前缀 qnc-server_order/homestay_order + SN_PREFIX_THIRD_PAYMENT SnPrefix = "PMT" //第三方支付流水记录前缀 qnc-server_payment/third_payment +) + +// 生成单号 +func GenSn(snPrefix SnPrefix) string { + return fmt.Sprintf("%s%s%s", snPrefix, time.Now().Format("20060102150405"), tool.Krand(8, tool.KC_RAND_KIND_NUM)) +} diff --git a/common/uniqueid/sn_test.go b/common/uniqueid/sn_test.go new file mode 100644 index 0000000..6c12b9f --- /dev/null +++ b/common/uniqueid/sn_test.go @@ -0,0 +1,7 @@ +package uniqueid + +import "testing" + +func TestGenSn(t *testing.T) { + GenSn(SN_PREFIX_HOMESTAY_ORDER) +} diff --git a/common/uniqueid/uniqueid.go b/common/uniqueid/uniqueid.go new file mode 100644 index 0000000..ff4c474 --- /dev/null +++ b/common/uniqueid/uniqueid.go @@ -0,0 +1,23 @@ +package uniqueid + +import ( + "github.com/sony/sonyflake" + "github.com/zeromicro/go-zero/core/logx" +) + +var flake *sonyflake.Sonyflake + +func init() { + flake = sonyflake.NewSonyflake(sonyflake.Settings{}) +} + +func GenId() int64 { + + id, err := flake.NextID() + if err != nil { + logx.Severef("flake NextID failed with %s \n", err) + panic(err) + } + + return int64(id) +} diff --git a/common/wxminisub/tpl.go b/common/wxminisub/tpl.go new file mode 100644 index 0000000..d05af52 --- /dev/null +++ b/common/wxminisub/tpl.go @@ -0,0 +1,7 @@ +package wxminisub + +//订单支付成功 +const OrderPaySuccessTemplateID = "QIJPmfxaNqYzSjOlXGk1T6Xfw94JwbSPuOd3u_hi3WE" + +//支付成功入驻通知 +const OrderPaySuccessLiveKnowTemplateID = "kmm-maRr6v_9eMxEPpj-5clJ2YW_EFpd8-ngyYk63e4" diff --git a/common/xerr/errCode.go b/common/xerr/errCode.go new file mode 100644 index 0000000..747ce99 --- /dev/null +++ b/common/xerr/errCode.go @@ -0,0 +1,18 @@ +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 PARAM_VERIFICATION_ERROR uint32 = 100007 +const CUSTOM_ERROR uint32 = 100008 + +//用户模块 diff --git a/common/xerr/errMsg.go b/common/xerr/errMsg.go new file mode 100644 index 0000000..ece9110 --- /dev/null +++ b/common/xerr/errMsg.go @@ -0,0 +1,30 @@ +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" +} + +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 + } +} diff --git a/common/xerr/errors.go b/common/xerr/errors.go new file mode 100644 index 0000000..897e37e --- /dev/null +++ b/common/xerr/errors.go @@ -0,0 +1,39 @@ +package xerr + +import ( + "fmt" +) + +/** +常用通用固定错误 +*/ + +type CodeError struct { + errCode uint32 + errMsg string +} + +// 返回给前端的错误码 +func (e *CodeError) GetErrCode() uint32 { + return e.errCode +} + +// 返回给前端显示端错误信息 +func (e *CodeError) GetErrMsg() string { + return e.errMsg +} + +func (e *CodeError) Error() string { + return fmt.Sprintf("ErrCode:%d,ErrMsg:%s", e.errCode, e.errMsg) +} + +func NewErrCodeMsg(errCode uint32, errMsg string) *CodeError { + return &CodeError{errCode: errCode, errMsg: errMsg} +} +func NewErrCode(errCode uint32) *CodeError { + return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode)} +} + +func NewErrMsg(errMsg string) *CodeError { + return &CodeError{errCode: CUSTOM_ERROR, errMsg: errMsg} +} diff --git a/data/.gitkeep b/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/deploy/sql/user.sql b/deploy/sql/user.sql new file mode 100644 index 0000000..f50d4a4 --- /dev/null +++ b/deploy/sql/user.sql @@ -0,0 +1,42 @@ +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for user +-- ---------------------------- +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `delete_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `del_state` tinyint NOT NULL DEFAULT '0', + `version` bigint NOT NULL DEFAULT '0' COMMENT '版本号', + `mobile` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', + `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', + `info` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_mobile` (`mobile`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表'; + +-- ---------------------------- +-- Table structure for user_auth +-- ---------------------------- +DROP TABLE IF EXISTS `user_auth`; +CREATE TABLE `user_auth` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `delete_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `del_state` tinyint NOT NULL DEFAULT '0', + `version` bigint NOT NULL DEFAULT '0' COMMENT '版本号', + `user_id` bigint NOT NULL DEFAULT '0', + `auth_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台唯一id', + `auth_type` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台类型', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_type_key` (`auth_type`,`auth_key`) USING BTREE, + UNIQUE KEY `idx_userId_key` (`user_id`,`auth_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户授权表'; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/deploy/template/api/config.tpl b/deploy/template/api/config.tpl new file mode 100644 index 0000000..55127ef --- /dev/null +++ b/deploy/template/api/config.tpl @@ -0,0 +1,9 @@ +package config + +import {{.authImport}} + +type Config struct { + rest.RestConf + {{.auth}} + {{.jwtTrans}} +} diff --git a/deploy/template/api/context.tpl b/deploy/template/api/context.tpl new file mode 100644 index 0000000..c15c1e4 --- /dev/null +++ b/deploy/template/api/context.tpl @@ -0,0 +1,17 @@ +package svc + +import ( + {{.configImport}} +) + +type ServiceContext struct { + Config {{.config}} + {{.middleware}} +} + +func NewServiceContext(c {{.config}}) *ServiceContext { + return &ServiceContext{ + Config: c, + {{.middlewareAssignment}} + } +} diff --git a/deploy/template/api/etc.tpl b/deploy/template/api/etc.tpl new file mode 100644 index 0000000..ed55cf1 --- /dev/null +++ b/deploy/template/api/etc.tpl @@ -0,0 +1,3 @@ +Name: {{.serviceName}} +Host: {{.host}} +Port: {{.port}} diff --git a/deploy/template/api/handler.tpl b/deploy/template/api/handler.tpl new file mode 100644 index 0000000..dcd3729 --- /dev/null +++ b/deploy/template/api/handler.tpl @@ -0,0 +1,27 @@ +package {{.PkgName}} + +import ( + "net/http" + + "qnc-server/common/result" + "qnc-server/pkg/lzkit/validator" + "github.com/zeromicro/go-zero/rest/httpx" + {{.ImportPackages}} +) + +func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + {{if .HasRequest}}var req types.{{.RequestType}} + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r,w,err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + {{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx) + {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}}) + result.HttpResult(r, w, {{if .HasResp}}resp{{else}}nil{{end}}, err) + } +} diff --git a/deploy/template/api/logic.tpl b/deploy/template/api/logic.tpl new file mode 100644 index 0000000..7c04323 --- /dev/null +++ b/deploy/template/api/logic.tpl @@ -0,0 +1,25 @@ +package {{.pkgName}} + +import ( + {{.imports}} +) + +type {{.logic}} struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func New{{.logic}}(ctx context.Context, svcCtx *svc.ServiceContext) *{{.logic}} { + return &{{.logic}}{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} { + // todo: add your logic here and delete this line + + {{.returnString}} +} diff --git a/deploy/template/api/main.tpl b/deploy/template/api/main.tpl new file mode 100644 index 0000000..71be5cb --- /dev/null +++ b/deploy/template/api/main.tpl @@ -0,0 +1,27 @@ +package main + +import ( + "flag" + "fmt" + + {{.importPackages}} + "qnc-server/common/middleware" +) + +var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file") + +func main() { + flag.Parse() + + var c config.Config + conf.MustLoad(*configFile, &c) + + ctx := svc.NewServiceContext(c) + server := rest.MustNewServer(c.RestConf) + defer server.Stop() + + handler.RegisterHandlers(server, ctx) + + fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) + server.Start() +} diff --git a/deploy/template/api/middleware.tpl b/deploy/template/api/middleware.tpl new file mode 100644 index 0000000..af714e0 --- /dev/null +++ b/deploy/template/api/middleware.tpl @@ -0,0 +1,20 @@ + +package middleware + +import "net/http" + +type {{.name}} struct { +} + +func New{{.name}}() *{{.name}} { + return &{{.name}}{} +} + +func (m *{{.name}})Handle(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // TODO generate middleware implement function, delete after code implementation + + // Passthrough to next handler if need + next(w, r) + } +} diff --git a/deploy/template/api/route-addition.tpl b/deploy/template/api/route-addition.tpl new file mode 100644 index 0000000..bb8a5df --- /dev/null +++ b/deploy/template/api/route-addition.tpl @@ -0,0 +1,4 @@ + + server.AddRoutes( + {{.routes}} {{.jwt}}{{.signature}} {{.prefix}} {{.timeout}} {{.maxBytes}} + ) diff --git a/deploy/template/api/routes.tpl b/deploy/template/api/routes.tpl new file mode 100644 index 0000000..f13fb11 --- /dev/null +++ b/deploy/template/api/routes.tpl @@ -0,0 +1,13 @@ +// Code generated by goctl. DO NOT EDIT. +package handler + +import ( + "net/http"{{if .hasTimeout}} + "time"{{end}} + + {{.importPackages}} +) + +func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { + {{.routesAdditions}} +} diff --git a/deploy/template/api/template.tpl b/deploy/template/api/template.tpl new file mode 100644 index 0000000..2176441 --- /dev/null +++ b/deploy/template/api/template.tpl @@ -0,0 +1,24 @@ +syntax = "v1" + +info ( + title: // TODO: add title + desc: // TODO: add description + author: "{{.gitUser}}" + email: "{{.gitEmail}}" +) + +type request { + // TODO: add members here and delete this comment +} + +type response { + // TODO: add members here and delete this comment +} + +service {{.serviceName}} { + @handler GetUser // TODO: set handler name and delete this comment + get /users/id/:userId(request) returns(response) + + @handler CreateUser // TODO: set handler name and delete this comment + post /users/create(request) +} diff --git a/deploy/template/api/types.tpl b/deploy/template/api/types.tpl new file mode 100644 index 0000000..735ec2d --- /dev/null +++ b/deploy/template/api/types.tpl @@ -0,0 +1,6 @@ +// Code generated by goctl. DO NOT EDIT. +package types{{if .containsTime}} +import ( + "time" +){{end}} +{{.types}} diff --git a/deploy/template/docker/docker.tpl b/deploy/template/docker/docker.tpl new file mode 100644 index 0000000..d1b5ff4 --- /dev/null +++ b/deploy/template/docker/docker.tpl @@ -0,0 +1,33 @@ +FROM golang:{{.Version}}alpine AS builder + +LABEL stage=gobuilder + +ENV CGO_ENABLED 0 +{{if .Chinese}}ENV GOPROXY https://goproxy.cn,direct +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories +{{end}}{{if .HasTimezone}} +RUN apk update --no-cache && apk add --no-cache tzdata +{{end}} +WORKDIR /build + +ADD go.mod . +ADD go.sum . +RUN go mod download +COPY . . +{{if .Argument}}COPY {{.GoRelPath}}/etc /app/etc +{{end}}RUN go build -ldflags="-s -w" -o /app/{{.ExeFile}} {{.GoMainFrom}} + + +FROM {{.BaseImage}} + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +{{if .HasTimezone}}COPY --from=builder /usr/share/zoneinfo/{{.Timezone}} /usr/share/zoneinfo/{{.Timezone}} +ENV TZ {{.Timezone}} +{{end}} +WORKDIR /app +COPY --from=builder /app/{{.ExeFile}} /app/{{.ExeFile}}{{if .Argument}} +COPY --from=builder /app/etc /app/etc{{end}} +{{if .HasPort}} +EXPOSE {{.Port}} +{{end}} +CMD ["./{{.ExeFile}}"{{.Argument}}] diff --git a/deploy/template/gateway/etc.tpl b/deploy/template/gateway/etc.tpl new file mode 100644 index 0000000..0a70f1a --- /dev/null +++ b/deploy/template/gateway/etc.tpl @@ -0,0 +1,18 @@ +Name: gateway-example # gateway name +Host: localhost # gateway host +Port: 8888 # gateway port +Upstreams: # upstreams + - Grpc: # grpc upstream + Target: 0.0.0.0:8080 # grpc target,the direct grpc server address,for only one node +# Endpoints: [0.0.0.0:8080,192.168.120.1:8080] # grpc endpoints, the grpc server address list, for multiple nodes +# Etcd: # etcd config, if you want to use etcd to discover the grpc server address +# Hosts: [127.0.0.1:2378,127.0.0.1:2379] # etcd hosts +# Key: greet.grpc # the discovery key + # protoset mode + ProtoSets: + - hello.pb + # Mappings can also be written in proto options +# Mappings: # routes mapping +# - Method: get +# Path: /ping +# RpcPath: hello.Hello/Ping diff --git a/deploy/template/gateway/main.tpl b/deploy/template/gateway/main.tpl new file mode 100644 index 0000000..6273451 --- /dev/null +++ b/deploy/template/gateway/main.tpl @@ -0,0 +1,20 @@ +package main + +import ( + "flag" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/gateway" +) + +var configFile = flag.String("f", "etc/gateway.yaml", "config file") + +func main() { + flag.Parse() + + var c gateway.GatewayConf + conf.MustLoad(*configFile, &c) + gw := gateway.MustNewServer(c) + defer gw.Stop() + gw.Start() +} diff --git a/deploy/template/kube/deployment.tpl b/deploy/template/kube/deployment.tpl new file mode 100644 index 0000000..14145df --- /dev/null +++ b/deploy/template/kube/deployment.tpl @@ -0,0 +1,117 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{.Name}} + namespace: {{.Namespace}} + labels: + app: {{.Name}} +spec: + replicas: {{.Replicas}} + revisionHistoryLimit: {{.Revisions}} + selector: + matchLabels: + app: {{.Name}} + template: + metadata: + labels: + app: {{.Name}} + spec:{{if .ServiceAccount}} + serviceAccountName: {{.ServiceAccount}}{{end}} + containers: + - name: {{.Name}} + image: {{.Image}} + {{if .ImagePullPolicy}}imagePullPolicy: {{.ImagePullPolicy}} + {{end}}ports: + - containerPort: {{.Port}} + readinessProbe: + tcpSocket: + port: {{.Port}} + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + tcpSocket: + port: {{.Port}} + initialDelaySeconds: 15 + periodSeconds: 20 + resources: + requests: + cpu: {{.RequestCpu}}m + memory: {{.RequestMem}}Mi + limits: + cpu: {{.LimitCpu}}m + memory: {{.LimitMem}}Mi + volumeMounts: + - name: timezone + mountPath: /etc/localtime + {{if .Secret}}imagePullSecrets: + - name: {{.Secret}} + {{end}}volumes: + - name: timezone + hostPath: + path: /usr/share/zoneinfo/Asia/Shanghai + +--- + +apiVersion: v1 +kind: Service +metadata: + name: {{.Name}}-svc + namespace: {{.Namespace}} +spec: + ports: + {{if .UseNodePort}}- nodePort: {{.NodePort}} + port: {{.Port}} + protocol: TCP + targetPort: {{.TargetPort}} + type: NodePort{{else}}- port: {{.Port}} + targetPort: {{.TargetPort}}{{end}} + selector: + app: {{.Name}} + +--- + +apiVersion: autoscaling/v2beta2 +kind: HorizontalPodAutoscaler +metadata: + name: {{.Name}}-hpa-c + namespace: {{.Namespace}} + labels: + app: {{.Name}}-hpa-c +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{.Name}} + minReplicas: {{.MinReplicas}} + maxReplicas: {{.MaxReplicas}} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 + +--- + +apiVersion: autoscaling/v2beta2 +kind: HorizontalPodAutoscaler +metadata: + name: {{.Name}}-hpa-m + namespace: {{.Namespace}} + labels: + app: {{.Name}}-hpa-m +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{.Name}} + minReplicas: {{.MinReplicas}} + maxReplicas: {{.MaxReplicas}} + metrics: + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 diff --git a/deploy/template/kube/job.tpl b/deploy/template/kube/job.tpl new file mode 100644 index 0000000..0da72ed --- /dev/null +++ b/deploy/template/kube/job.tpl @@ -0,0 +1,37 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{.Name}} + namespace: {{.Namespace}} +spec: + successfulJobsHistoryLimit: {{.SuccessfulJobsHistoryLimit}} + schedule: "{{.Schedule}}" + jobTemplate: + spec: + template: + spec:{{if .ServiceAccount}} + serviceAccountName: {{.ServiceAccount}}{{end}} + {{end}}containers: + - name: {{.Name}} + image: # todo image url + resources: + requests: + cpu: {{.RequestCpu}}m + memory: {{.RequestMem}}Mi + limits: + cpu: {{.LimitCpu}}m + memory: {{.LimitMem}}Mi + command: + - ./{{.ServiceName}} + - -f + - ./{{.Name}}.yaml + volumeMounts: + - name: timezone + mountPath: /etc/localtime + imagePullSecrets: + - name: # registry secret, if no, remove this + restartPolicy: OnFailure + volumes: + - name: timezone + hostPath: + path: /usr/share/zoneinfo/Asia/Shanghai diff --git a/deploy/template/model/delete.tpl b/deploy/template/model/delete.tpl new file mode 100644 index 0000000..e22869a --- /dev/null +++ b/deploy/template/model/delete.tpl @@ -0,0 +1,21 @@ +func (m *default{{.upperStartCamelObject}}Model) Delete(ctx context.Context, session sqlx.Session, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) error { + {{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne(ctx, {{.lowerStartCamelPrimaryKey}}) + if err!=nil{ + return err + } + + {{end}} {{.keys}} + _, err {{if .containsIndexCache}}={{else}}:={{end}} m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table) + if session!=nil{ + return session.ExecCtx(ctx,query, {{.lowerStartCamelPrimaryKey}}) + } + return conn.ExecCtx(ctx, query, {{.lowerStartCamelPrimaryKey}}) + }, {{.keyValues}}){{else}}query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table) + if session!=nil{ + _,err:= session.ExecCtx(ctx,query, {{.lowerStartCamelPrimaryKey}}) + return err + } + _,err:=m.conn.ExecCtx(ctx, query, {{.lowerStartCamelPrimaryKey}}){{end}} + return err +} \ No newline at end of file diff --git a/deploy/template/model/err.tpl b/deploy/template/model/err.tpl new file mode 100644 index 0000000..cbc9f82 --- /dev/null +++ b/deploy/template/model/err.tpl @@ -0,0 +1,9 @@ +package {{.pkg}} + +import ( + "errors" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +var ErrNotFound = sqlx.ErrNotFound +var ErrNoRowsUpdate = errors.New("update db no rows change") \ No newline at end of file diff --git a/deploy/template/model/field.tpl b/deploy/template/model/field.tpl new file mode 100644 index 0000000..6b4ed38 --- /dev/null +++ b/deploy/template/model/field.tpl @@ -0,0 +1 @@ +{{.name}} {{.type}} {{.tag}} {{if .hasComment}}// {{.comment}}{{end}} \ No newline at end of file diff --git a/deploy/template/model/find-one-by-field-extra-method.tpl b/deploy/template/model/find-one-by-field-extra-method.tpl new file mode 100644 index 0000000..af15538 --- /dev/null +++ b/deploy/template/model/find-one-by-field-extra-method.tpl @@ -0,0 +1,7 @@ +func (m *default{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string { + return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary) +} +func (m *default{{.upperStartCamelObject}}Model) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { + query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = {{if .postgreSql}}$1{{else}}?{{end}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table ) + return conn.QueryRowCtx(ctx, v, query, primary,globalkey.DelStateNo) +} diff --git a/deploy/template/model/find-one-by-field.tpl b/deploy/template/model/find-one-by-field.tpl new file mode 100644 index 0000000..e6fd828 --- /dev/null +++ b/deploy/template/model/find-one-by-field.tpl @@ -0,0 +1,32 @@ + +func (m *default{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}(ctx context.Context, {{.in}}) (*{{.upperStartCamelObject}}, error) { +{{if .withCache}}{{.cacheKey}} + var resp {{.upperStartCamelObject}} + err := m.QueryRowIndexCtx(ctx, &resp, {{.cacheKeyVariable}}, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where {{.originalField}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table) + if err := conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelField}},globalkey.DelStateNo); err != nil { + return nil, err + } + return resp.{{.upperStartCamelPrimaryKey}}, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +}{{else}}var resp {{.upperStartCamelObject}} + query := fmt.Sprintf("select %s from %s where {{.originalField}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table ) + err := m.conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelField}},globalkey.DelStateNo) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +}{{end}} + diff --git a/deploy/template/model/find-one.tpl b/deploy/template/model/find-one.tpl new file mode 100644 index 0000000..72d5ecf --- /dev/null +++ b/deploy/template/model/find-one.tpl @@ -0,0 +1,26 @@ +func (m *default{{.upperStartCamelObject}}Model) FindOne(ctx context.Context, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) { + {{if .withCache}}{{.cacheKey}} + var resp {{.upperStartCamelObject}} + err := m.QueryRowCtx(ctx, &resp, {{.cacheKeyVariable}}, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { + query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table) + return conn.QueryRowCtx(ctx, v, query, {{.lowerStartCamelPrimaryKey}},globalkey.DelStateNo) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + }{{else}}query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table) + var resp {{.upperStartCamelObject}} + err := m.conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelPrimaryKey}},globalkey.DelStateNo) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + }{{end}} +} diff --git a/deploy/template/model/import-no-cache.tpl b/deploy/template/model/import-no-cache.tpl new file mode 100644 index 0000000..2b5e853 --- /dev/null +++ b/deploy/template/model/import-no-cache.tpl @@ -0,0 +1,16 @@ +import ( + "context" + "database/sql" + "fmt" + "strings" + + {{if .time}}"time"{{end}} + + "qnc-server/common/globalkey" + "github.com/Masterminds/squirrel" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/sqlc" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" +) diff --git a/deploy/template/model/import.tpl b/deploy/template/model/import.tpl new file mode 100644 index 0000000..3cd8038 --- /dev/null +++ b/deploy/template/model/import.tpl @@ -0,0 +1,17 @@ +import ( + "context" + "database/sql" + "fmt" + "strings" + + {{if .time}}"time"{{end}} + + "qnc-server/common/globalkey" + "github.com/Masterminds/squirrel" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlc" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" +) diff --git a/deploy/template/model/insert.tpl b/deploy/template/model/insert.tpl new file mode 100644 index 0000000..832e588 --- /dev/null +++ b/deploy/template/model/insert.tpl @@ -0,0 +1,18 @@ + +func (m *default{{.upperStartCamelObject}}Model) Insert(ctx context.Context,session sqlx.Session, data *{{.upperStartCamelObject}}) (sql.Result,error) { + data.DeleteTime = time.Unix(0,0) + data.DelState = globalkey.DelStateNo + {{if .withCache}}{{.keys}} + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet) + if session != nil{ + return session.ExecCtx(ctx,query,{{.expressionValues}}) + } + return conn.ExecCtx(ctx, query, {{.expressionValues}}) + }, {{.keyValues}}){{else}} + query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet) + if session != nil{ + return session.ExecCtx(ctx,query,{{.expressionValues}}) + } + return m.conn.ExecCtx(ctx, query, {{.expressionValues}}){{end}} +} diff --git a/deploy/template/model/interface-delete.tpl b/deploy/template/model/interface-delete.tpl new file mode 100644 index 0000000..a7f11a7 --- /dev/null +++ b/deploy/template/model/interface-delete.tpl @@ -0,0 +1 @@ +Delete(ctx context.Context,session sqlx.Session, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) error \ No newline at end of file diff --git a/deploy/template/model/interface-find-one-by-field.tpl b/deploy/template/model/interface-find-one-by-field.tpl new file mode 100644 index 0000000..9615aa3 --- /dev/null +++ b/deploy/template/model/interface-find-one-by-field.tpl @@ -0,0 +1 @@ +FindOneBy{{.upperField}}(ctx context.Context, {{.in}}) (*{{.upperStartCamelObject}}, error) \ No newline at end of file diff --git a/deploy/template/model/interface-find-one.tpl b/deploy/template/model/interface-find-one.tpl new file mode 100644 index 0000000..a7a5440 --- /dev/null +++ b/deploy/template/model/interface-find-one.tpl @@ -0,0 +1 @@ +FindOne(ctx context.Context, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) \ No newline at end of file diff --git a/deploy/template/model/interface-insert.tpl b/deploy/template/model/interface-insert.tpl new file mode 100644 index 0000000..6705d2b --- /dev/null +++ b/deploy/template/model/interface-insert.tpl @@ -0,0 +1 @@ +Insert(ctx context.Context, session sqlx.Session,data *{{.upperStartCamelObject}}) (sql.Result,error) \ No newline at end of file diff --git a/deploy/template/model/interface-update.tpl b/deploy/template/model/interface-update.tpl new file mode 100644 index 0000000..32a4214 --- /dev/null +++ b/deploy/template/model/interface-update.tpl @@ -0,0 +1,12 @@ +Update(ctx context.Context,session sqlx.Session, data *{{.upperStartCamelObject}}) (sql.Result, error) +UpdateWithVersion(ctx context.Context,session sqlx.Session,data *{{.upperStartCamelObject}}) error +Trans(ctx context.Context,fn func(context context.Context,session sqlx.Session) error) error +SelectBuilder() squirrel.SelectBuilder +DeleteSoft(ctx context.Context,session sqlx.Session, data *{{.upperStartCamelObject}}) error +FindSum(ctx context.Context,sumBuilder squirrel.SelectBuilder,field string) (float64,error) +FindCount(ctx context.Context,countBuilder squirrel.SelectBuilder,field string) (int64,error) +FindAll(ctx context.Context,rowBuilder squirrel.SelectBuilder,orderBy string) ([]*{{.upperStartCamelObject}},error) +FindPageListByPage(ctx context.Context,rowBuilder squirrel.SelectBuilder,page ,pageSize int64,orderBy string) ([]*{{.upperStartCamelObject}},error) +FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*{{.upperStartCamelObject}}, int64, error) +FindPageListByIdDESC(ctx context.Context,rowBuilder squirrel.SelectBuilder ,preMinId ,pageSize int64) ([]*{{.upperStartCamelObject}},error) +FindPageListByIdASC(ctx context.Context,rowBuilder squirrel.SelectBuilder,preMaxId ,pageSize int64) ([]*{{.upperStartCamelObject}},error) \ No newline at end of file diff --git a/deploy/template/model/model-gen.tpl b/deploy/template/model/model-gen.tpl new file mode 100644 index 0000000..8da9d0b --- /dev/null +++ b/deploy/template/model/model-gen.tpl @@ -0,0 +1,13 @@ +// Code generated by goctl. DO NOT EDIT! + +package {{.pkg}} +{{.imports}} +{{.vars}} +{{.types}} +{{.new}} +{{.insert}} +{{.find}} +{{.update}} +{{.delete}} +{{.extraMethod}} +{{.tableName}} \ No newline at end of file diff --git a/deploy/template/model/model-new.tpl b/deploy/template/model/model-new.tpl new file mode 100644 index 0000000..5e9d15f --- /dev/null +++ b/deploy/template/model/model-new.tpl @@ -0,0 +1,7 @@ + +func new{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) *default{{.upperStartCamelObject}}Model { + return &default{{.upperStartCamelObject}}Model{ + {{if .withCache}}CachedConn: sqlc.NewConn(conn, c){{else}}conn:conn{{end}}, + table: {{.table}}, + } +} diff --git a/deploy/template/model/model.tpl b/deploy/template/model/model.tpl new file mode 100644 index 0000000..0ac2918 --- /dev/null +++ b/deploy/template/model/model.tpl @@ -0,0 +1,37 @@ +package {{.pkg}} +{{if .withCache}} +import ( + + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlx" + +) +{{else}} +import ( + + "github.com/zeromicro/go-zero/core/stores/sqlx" + +) + +{{end}} +var _ {{.upperStartCamelObject}}Model = (*custom{{.upperStartCamelObject}}Model)(nil) + +type ( + // {{.upperStartCamelObject}}Model is an interface to be customized, add more methods here, + // and implement the added methods in custom{{.upperStartCamelObject}}Model. + {{.upperStartCamelObject}}Model interface { + {{.lowerStartCamelObject}}Model + +} + + custom{{.upperStartCamelObject}}Model struct { + *default{{.upperStartCamelObject}}Model + } +) + +// New{{.upperStartCamelObject}}Model returns a model for the database table. +func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) {{.upperStartCamelObject}}Model { + return &custom{{.upperStartCamelObject}}Model{ + default{{.upperStartCamelObject}}Model: new{{.upperStartCamelObject}}Model(conn{{if .withCache}}, c{{end}}), + } +} diff --git a/deploy/template/model/table-name.tpl b/deploy/template/model/table-name.tpl new file mode 100644 index 0000000..69733fa --- /dev/null +++ b/deploy/template/model/table-name.tpl @@ -0,0 +1,4 @@ + +func (m *default{{.upperStartCamelObject}}Model) tableName() string { + return m.table +} diff --git a/deploy/template/model/tag.tpl b/deploy/template/model/tag.tpl new file mode 100644 index 0000000..8e1ddf0 --- /dev/null +++ b/deploy/template/model/tag.tpl @@ -0,0 +1 @@ +`db:"{{.field}}"` \ No newline at end of file diff --git a/deploy/template/model/types.tpl b/deploy/template/model/types.tpl new file mode 100644 index 0000000..d551a4f --- /dev/null +++ b/deploy/template/model/types.tpl @@ -0,0 +1,15 @@ + +type ( + {{.lowerStartCamelObject}}Model interface{ + {{.method}} + } + + default{{.upperStartCamelObject}}Model struct { + {{if .withCache}}sqlc.CachedConn{{else}}conn sqlx.SqlConn{{end}} + table string + } + + {{.upperStartCamelObject}} struct { + {{.fields}} + } +) diff --git a/deploy/template/model/update.tpl b/deploy/template/model/update.tpl new file mode 100644 index 0000000..14e6618 --- /dev/null +++ b/deploy/template/model/update.tpl @@ -0,0 +1,286 @@ + +func (m *default{{.upperStartCamelObject}}Model) Update(ctx context.Context,session sqlx.Session, {{if .containsIndexCache}}newData{{else}}data{{end}} *{{.upperStartCamelObject}}) (sql.Result,error) { + {{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne(ctx, newData.{{.upperStartCamelPrimaryKey}}) + if err!=nil{ + return nil,err + } + {{end}}{{.keys}} + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder) + if session != nil{ + return session.ExecCtx(ctx,query, {{.expressionValues}}) + } + return conn.ExecCtx(ctx, query, {{.expressionValues}}) + }, {{.keyValues}}){{else}}query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder) + if session != nil{ + return session.ExecCtx(ctx,query, {{.expressionValues}}) + } + return m.conn.ExecCtx(ctx, query, {{.expressionValues}}){{end}} +} + +func (m *default{{.upperStartCamelObject}}Model) UpdateWithVersion(ctx context.Context,session sqlx.Session,{{if .containsIndexCache}}newData{{else}}data{{end}} *{{.upperStartCamelObject}}) error { + + {{if .containsIndexCache}} + oldVersion := newData.Version + newData.Version += 1 + {{else}} + oldVersion := data.Version + data.Version += 1 + {{end}} + + var sqlResult sql.Result + var err error + + {{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne(ctx, newData.{{.upperStartCamelPrimaryKey}}) + if err!=nil{ + return err + } + {{end}}{{.keys}} + sqlResult,err = m.ExecCtx(ctx,func(ctx context.Context,conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} and version = ? ", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder) + if session != nil{ + return session.ExecCtx(ctx,query, {{.expressionValues}},oldVersion) + } + return conn.ExecCtx(ctx,query, {{.expressionValues}},oldVersion) + }, {{.keyValues}}){{else}}query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} and version = ? ", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder) + if session != nil{ + sqlResult,err = session.ExecCtx(ctx,query, {{.expressionValues}},oldVersion) + }else{ + sqlResult,err = m.conn.ExecCtx(ctx,query, {{.expressionValues}},oldVersion) + } + {{end}} + if err != nil { + return err + } + updateCount , err := sqlResult.RowsAffected() + if err != nil{ + return err + } + if updateCount == 0 { + return ErrNoRowsUpdate + } + + return nil +} + +func (m *default{{.upperStartCamelObject}}Model) DeleteSoft(ctx context.Context,session sqlx.Session,data *{{.upperStartCamelObject}}) error { + data.DelState = globalkey.DelStateYes + data.DeleteTime = time.Now() + if err:= m.UpdateWithVersion(ctx,session, data);err!= nil{ + return errors.Wrapf(errors.New("delete soft failed "),"{{.upperStartCamelObject}}Model delete err : %+v",err) + } + return nil +} + +func (m *default{{.upperStartCamelObject}}Model) FindSum(ctx context.Context,builder squirrel.SelectBuilder, field string) (float64,error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field") + } + + builder = builder.Columns("IFNULL(SUM(" + field + "),0)") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp float64 + {{if .withCache}}err = m.QueryRowNoCacheCtx(ctx,&resp, query, values...){{else}} + err = m.conn.QueryRowCtx(ctx,&resp, query, values...) + {{end}} + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *default{{.upperStartCamelObject}}Model) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64,error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field") + } + + builder = builder.Columns("COUNT(" + field + ")") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp int64 + {{if .withCache}}err = m.QueryRowNoCacheCtx(ctx,&resp, query, values...){{else}} + err = m.conn.QueryRowCtx(ctx,&resp, query, values...) + {{end}} + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *default{{.upperStartCamelObject}}Model) FindAll(ctx context.Context,builder squirrel.SelectBuilder,orderBy string) ([]*{{.upperStartCamelObject}},error) { + + builder = builder.Columns({{.lowerStartCamelObject}}Rows) + + if orderBy == ""{ + builder = builder.OrderBy("id DESC") + }else{ + builder = builder.OrderBy(orderBy) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return nil, err + } + + var resp []*{{.upperStartCamelObject}} + {{if .withCache}}err = m.QueryRowsNoCacheCtx(ctx,&resp, query, values...){{else}} + err = m.conn.QueryRowsCtx(ctx,&resp, query, values...) + {{end}} + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *default{{.upperStartCamelObject}}Model) FindPageListByPage(ctx context.Context,builder squirrel.SelectBuilder,page ,pageSize int64,orderBy string) ([]*{{.upperStartCamelObject}},error) { + + builder = builder.Columns({{.lowerStartCamelObject}}Rows) + + if orderBy == ""{ + builder = builder.OrderBy("id DESC") + }else{ + builder = builder.OrderBy(orderBy) + } + + if page < 1{ + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*{{.upperStartCamelObject}} + {{if .withCache}}err = m.QueryRowsNoCacheCtx(ctx,&resp, query, values...){{else}} + err = m.conn.QueryRowsCtx(ctx,&resp, query, values...) + {{end}} + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + + +func (m *default{{.upperStartCamelObject}}Model) FindPageListByPageWithTotal(ctx context.Context,builder squirrel.SelectBuilder,page ,pageSize int64,orderBy string) ([]*{{.upperStartCamelObject}},int64,error) { + + total, err := m.FindCount(ctx, builder, "id") + if err != nil { + return nil, 0, err + } + + builder = builder.Columns({{.lowerStartCamelObject}}Rows) + + if orderBy == ""{ + builder = builder.OrderBy("id DESC") + }else{ + builder = builder.OrderBy(orderBy) + } + + if page < 1{ + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil,total, err + } + + var resp []*{{.upperStartCamelObject}} + {{if .withCache}}err = m.QueryRowsNoCacheCtx(ctx,&resp, query, values...){{else}} + err = m.conn.QueryRowsCtx(ctx,&resp, query, values...) + {{end}} + switch err { + case nil: + return resp,total, nil + default: + return nil,total, err + } +} + +func (m *default{{.upperStartCamelObject}}Model) FindPageListByIdDESC(ctx context.Context,builder squirrel.SelectBuilder ,preMinId ,pageSize int64) ([]*{{.upperStartCamelObject}},error) { + + builder = builder.Columns({{.lowerStartCamelObject}}Rows) + + if preMinId > 0 { + builder = builder.Where(" id < ? " , preMinId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*{{.upperStartCamelObject}} + {{if .withCache}}err = m.QueryRowsNoCacheCtx(ctx,&resp, query, values...){{else}} + err = m.conn.QueryRowsCtx(ctx,&resp, query, values...) + {{end}} + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *default{{.upperStartCamelObject}}Model) FindPageListByIdASC(ctx context.Context,builder squirrel.SelectBuilder,preMaxId ,pageSize int64) ([]*{{.upperStartCamelObject}},error) { + + builder = builder.Columns({{.lowerStartCamelObject}}Rows) + + if preMaxId > 0 { + builder = builder.Where(" id > ? " , preMaxId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*{{.upperStartCamelObject}} + {{if .withCache}}err = m.QueryRowsNoCacheCtx(ctx,&resp, query, values...){{else}} + err = m.conn.QueryRowsCtx(ctx,&resp, query, values...) + {{end}} + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *default{{.upperStartCamelObject}}Model) Trans(ctx context.Context,fn func(ctx context.Context,session sqlx.Session) error) error { + {{if .withCache}} + return m.TransactCtx(ctx,func(ctx context.Context,session sqlx.Session) error { + return fn(ctx,session) + }) + {{else}} + return m.conn.TransactCtx(ctx,func(ctx context.Context,session sqlx.Session) error { + return fn(ctx,session) + }) + {{end}} +} + +func(m *default{{.upperStartCamelObject}}Model) SelectBuilder() squirrel.SelectBuilder { + return squirrel.Select().From(m.table) +} \ No newline at end of file diff --git a/deploy/template/model/var.tpl b/deploy/template/model/var.tpl new file mode 100644 index 0000000..a10ca33 --- /dev/null +++ b/deploy/template/model/var.tpl @@ -0,0 +1,9 @@ + +var ( + {{.lowerStartCamelObject}}FieldNames = builder.RawFieldNames(&{{.upperStartCamelObject}}{}{{if .postgreSql}},true{{end}}) + {{.lowerStartCamelObject}}Rows = strings.Join({{.lowerStartCamelObject}}FieldNames, ",") + {{.lowerStartCamelObject}}RowsExpectAutoSet = {{if .postgreSql}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}",{{end}} "create_time", "update_time"), ","){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}",{{end}} "`create_time`", "`update_time`"), ","){{end}} + {{.lowerStartCamelObject}}RowsWithPlaceHolder = {{if .postgreSql}}builder.PostgreSqlJoin(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", "create_time", "update_time")){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", "`create_time`", "`update_time`"), "=?,") + "=?"{{end}} + + {{if .withCache}}{{.cacheKeys}}{{end}} +) diff --git a/deploy/template/mongo/err.tpl b/deploy/template/mongo/err.tpl new file mode 100644 index 0000000..27d9244 --- /dev/null +++ b/deploy/template/mongo/err.tpl @@ -0,0 +1,12 @@ +package model + +import ( + "errors" + + "github.com/zeromicro/go-zero/core/stores/mon" +) + +var ( + ErrNotFound = mon.ErrNotFound + ErrInvalidObjectId = errors.New("invalid objectId") +) diff --git a/deploy/template/mongo/model.tpl b/deploy/template/mongo/model.tpl new file mode 100644 index 0000000..287125d --- /dev/null +++ b/deploy/template/mongo/model.tpl @@ -0,0 +1,78 @@ +// Code generated by goctl. DO NOT EDIT. +package model + +import ( + "context" + "time" + + {{if .Cache}}"github.com/zeromicro/go-zero/core/stores/monc"{{else}}"github.com/zeromicro/go-zero/core/stores/mon"{{end}} + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" +) + +{{if .Cache}}var prefix{{.Type}}CacheKey = "cache:{{.lowerType}}:"{{end}} + +type {{.lowerType}}Model interface{ + Insert(ctx context.Context,data *{{.Type}}) error + FindOne(ctx context.Context,id string) (*{{.Type}}, error) + Update(ctx context.Context,data *{{.Type}}) (*mongo.UpdateResult, error) + Delete(ctx context.Context,id string) (int64, error) +} + +type default{{.Type}}Model struct { + conn {{if .Cache}}*monc.Model{{else}}*mon.Model{{end}} +} + +func newDefault{{.Type}}Model(conn {{if .Cache}}*monc.Model{{else}}*mon.Model{{end}}) *default{{.Type}}Model { + return &default{{.Type}}Model{conn: conn} +} + + +func (m *default{{.Type}}Model) Insert(ctx context.Context, data *{{.Type}}) error { + if data.ID.IsZero() { + data.ID = primitive.NewObjectID() + data.CreateAt = time.Now() + data.UpdateAt = time.Now() + } + + {{if .Cache}}key := prefix{{.Type}}CacheKey + data.ID.Hex(){{end}} + _, err := m.conn.InsertOne(ctx, {{if .Cache}}key, {{end}} data) + return err +} + +func (m *default{{.Type}}Model) FindOne(ctx context.Context, id string) (*{{.Type}}, error) { + oid, err := primitive.ObjectIDFromHex(id) + if err != nil { + return nil, ErrInvalidObjectId + } + + var data {{.Type}} + {{if .Cache}}key := prefix{{.Type}}CacheKey + id{{end}} + err = m.conn.FindOne(ctx, {{if .Cache}}key, {{end}}&data, bson.M{"_id": oid}) + switch err { + case nil: + return &data, nil + case {{if .Cache}}monc{{else}}mon{{end}}.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *default{{.Type}}Model) Update(ctx context.Context, data *{{.Type}}) (*mongo.UpdateResult, error) { + data.UpdateAt = time.Now() + {{if .Cache}}key := prefix{{.Type}}CacheKey + data.ID.Hex(){{end}} + res, err := m.conn.UpdateOne(ctx, {{if .Cache}}key, {{end}}bson.M{"_id": data.ID}, bson.M{"$set": data}) + return res, err +} + +func (m *default{{.Type}}Model) Delete(ctx context.Context, id string) (int64, error) { + oid, err := primitive.ObjectIDFromHex(id) + if err != nil { + return 0, ErrInvalidObjectId + } + {{if .Cache}}key := prefix{{.Type}}CacheKey +id{{end}} + res, err := m.conn.DeleteOne(ctx, {{if .Cache}}key, {{end}}bson.M{"_id": oid}) + return res, err +} diff --git a/deploy/template/mongo/model_custom.tpl b/deploy/template/mongo/model_custom.tpl new file mode 100644 index 0000000..31fa865 --- /dev/null +++ b/deploy/template/mongo/model_custom.tpl @@ -0,0 +1,38 @@ +package model + +{{if .Cache}}import ( + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/monc" +){{else}}import "github.com/zeromicro/go-zero/core/stores/mon"{{end}} + +{{if .Easy}} +const {{.Type}}CollectionName = "{{.snakeType}}" +{{end}} + +var _ {{.Type}}Model = (*custom{{.Type}}Model)(nil) + +type ( + // {{.Type}}Model is an interface to be customized, add more methods here, + // and implement the added methods in custom{{.Type}}Model. + {{.Type}}Model interface { + {{.lowerType}}Model + } + + custom{{.Type}}Model struct { + *default{{.Type}}Model + } +) + + +// New{{.Type}}Model returns a model for the mongo. +{{if .Easy}}func New{{.Type}}Model(url, db string{{if .Cache}}, c cache.CacheConf{{end}}) {{.Type}}Model { + conn := {{if .Cache}}monc{{else}}mon{{end}}.MustNewModel(url, db, {{.Type}}CollectionName{{if .Cache}}, c{{end}}) + return &custom{{.Type}}Model{ + default{{.Type}}Model: newDefault{{.Type}}Model(conn), + } +}{{else}}func New{{.Type}}Model(url, db, collection string{{if .Cache}}, c cache.CacheConf{{end}}) {{.Type}}Model { + conn := {{if .Cache}}monc{{else}}mon{{end}}.MustNewModel(url, db, collection{{if .Cache}}, c{{end}}) + return &custom{{.Type}}Model{ + default{{.Type}}Model: newDefault{{.Type}}Model(conn), + } +}{{end}} diff --git a/deploy/template/mongo/model_types.tpl b/deploy/template/mongo/model_types.tpl new file mode 100644 index 0000000..8da006f --- /dev/null +++ b/deploy/template/mongo/model_types.tpl @@ -0,0 +1,14 @@ +package model + +import ( + "time" + + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type {{.Type}} struct { + ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` + // TODO: Fill your own fields + UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"` + CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"` +} diff --git a/deploy/template/newapi/newtemplate.tpl b/deploy/template/newapi/newtemplate.tpl new file mode 100644 index 0000000..28be510 --- /dev/null +++ b/deploy/template/newapi/newtemplate.tpl @@ -0,0 +1,12 @@ +type Request { + Name string `path:"name,options=you|me"` +} + +type Response { + Message string `json:"message"` +} + +service {{.name}}-api { + @handler {{.handler}}Handler + get /from/:name(Request) returns (Response) +} diff --git a/deploy/template/rpc/call.tpl b/deploy/template/rpc/call.tpl new file mode 100644 index 0000000..27b4879 --- /dev/null +++ b/deploy/template/rpc/call.tpl @@ -0,0 +1,33 @@ +{{.head}} + +package {{.filePackage}} + +import ( + "context" + + {{.pbPackage}} + {{if ne .pbPackage .protoGoPackage}}{{.protoGoPackage}}{{end}} + + "github.com/zeromicro/go-zero/zrpc" + "google.golang.org/grpc" +) + +type ( + {{.alias}} + + {{.serviceName}} interface { + {{.interface}} + } + + default{{.serviceName}} struct { + cli zrpc.Client + } +) + +func New{{.serviceName}}(cli zrpc.Client) {{.serviceName}} { + return &default{{.serviceName}}{ + cli: cli, + } +} + +{{.functions}} diff --git a/deploy/template/rpc/config.tpl b/deploy/template/rpc/config.tpl new file mode 100644 index 0000000..c1f85b9 --- /dev/null +++ b/deploy/template/rpc/config.tpl @@ -0,0 +1,7 @@ +package config + +import "github.com/zeromicro/go-zero/zrpc" + +type Config struct { + zrpc.RpcServerConf +} diff --git a/deploy/template/rpc/etc.tpl b/deploy/template/rpc/etc.tpl new file mode 100644 index 0000000..6cd4bdd --- /dev/null +++ b/deploy/template/rpc/etc.tpl @@ -0,0 +1,6 @@ +Name: {{.serviceName}}.rpc +ListenOn: 0.0.0.0:8080 +Etcd: + Hosts: + - 127.0.0.1:2379 + Key: {{.serviceName}}.rpc diff --git a/deploy/template/rpc/logic-func.tpl b/deploy/template/rpc/logic-func.tpl new file mode 100644 index 0000000..e9410d4 --- /dev/null +++ b/deploy/template/rpc/logic-func.tpl @@ -0,0 +1,6 @@ +{{if .hasComment}}{{.comment}}{{end}} +func (l *{{.logicName}}) {{.method}} ({{if .hasReq}}in {{.request}}{{if .stream}},stream {{.streamBody}}{{end}}{{else}}stream {{.streamBody}}{{end}}) ({{if .hasReply}}{{.response}},{{end}} error) { + // todo: add your logic here and delete this line + + return {{if .hasReply}}&{{.responseType}}{},{{end}} nil +} diff --git a/deploy/template/rpc/logic.tpl b/deploy/template/rpc/logic.tpl new file mode 100644 index 0000000..b8d81f0 --- /dev/null +++ b/deploy/template/rpc/logic.tpl @@ -0,0 +1,24 @@ +package {{.packageName}} + +import ( + "context" + + {{.imports}} + + "github.com/zeromicro/go-zero/core/logx" +) + +type {{.logicName}} struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func New{{.logicName}}(ctx context.Context,svcCtx *svc.ServiceContext) *{{.logicName}} { + return &{{.logicName}}{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} +{{.functions}} diff --git a/deploy/template/rpc/main.tpl b/deploy/template/rpc/main.tpl new file mode 100644 index 0000000..d7bea24 --- /dev/null +++ b/deploy/template/rpc/main.tpl @@ -0,0 +1,42 @@ +package main + +import ( + "flag" + "fmt" + + {{.imports}} + "qnc-server/common/interceptor/rpcserver" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/core/service" + "github.com/zeromicro/go-zero/zrpc" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file") + +func main() { + flag.Parse() + + var c config.Config + conf.MustLoad(*configFile, &c) + ctx := svc.NewServiceContext(c) + + s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { +{{range .serviceNames}} {{.Pkg}}.Register{{.Service}}Server(grpcServer, {{.ServerPkg}}.New{{.Service}}Server(ctx)) +{{end}} + if c.Mode == service.DevMode || c.Mode == service.TestMode { + reflection.Register(grpcServer) + } + }) + + //rpc log + s.AddUnaryInterceptors(rpcserver.LoggerInterceptor) + + defer s.Stop() + + fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) + s.Start() +} + diff --git a/deploy/template/rpc/server-func.tpl b/deploy/template/rpc/server-func.tpl new file mode 100644 index 0000000..d771b43 --- /dev/null +++ b/deploy/template/rpc/server-func.tpl @@ -0,0 +1,6 @@ + +{{if .hasComment}}{{.comment}}{{end}} +func (s *{{.server}}Server) {{.method}} ({{if .notStream}}ctx context.Context,{{if .hasReq}} in {{.request}}{{end}}{{else}}{{if .hasReq}} in {{.request}},{{end}}stream {{.streamBody}}{{end}}) ({{if .notStream}}{{.response}},{{end}}error) { + l := {{.logicPkg}}.New{{.logicName}}({{if .notStream}}ctx,{{else}}stream.Context(),{{end}}s.svcCtx) + return l.{{.method}}({{if .hasReq}}in{{if .stream}} ,stream{{end}}{{else}}{{if .stream}}stream{{end}}{{end}}) +} diff --git a/deploy/template/rpc/server.tpl b/deploy/template/rpc/server.tpl new file mode 100644 index 0000000..84a2f9c --- /dev/null +++ b/deploy/template/rpc/server.tpl @@ -0,0 +1,22 @@ +{{.head}} + +package server + +import ( + {{if .notStream}}"context"{{end}} + + {{.imports}} +) + +type {{.server}}Server struct { + svcCtx *svc.ServiceContext + {{.unimplementedServer}} +} + +func New{{.server}}Server(svcCtx *svc.ServiceContext) *{{.server}}Server { + return &{{.server}}Server{ + svcCtx: svcCtx, + } +} + +{{.funcs}} diff --git a/deploy/template/rpc/svc.tpl b/deploy/template/rpc/svc.tpl new file mode 100644 index 0000000..cf2b47a --- /dev/null +++ b/deploy/template/rpc/svc.tpl @@ -0,0 +1,13 @@ +package svc + +import {{.imports}} + +type ServiceContext struct { + Config config.Config +} + +func NewServiceContext(c config.Config) *ServiceContext { + return &ServiceContext{ + Config:c, + } +} diff --git a/deploy/template/rpc/template.tpl b/deploy/template/rpc/template.tpl new file mode 100644 index 0000000..76daa94 --- /dev/null +++ b/deploy/template/rpc/template.tpl @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package {{.package}}; +option go_package="./{{.package}}"; + +message Request { + string ping = 1; +} + +message Response { + string pong = 1; +} + +service {{.serviceName}} { + rpc Ping(Request) returns(Response); +} diff --git a/docker-compose-env.yml b/docker-compose-env.yml new file mode 100644 index 0000000..fc61625 --- /dev/null +++ b/docker-compose-env.yml @@ -0,0 +1,57 @@ +version: '3' + +services: + + mysql: + image: mysql:8.0.34 + container_name: qnc_mysql + environment: + # 时区上海 - Time zone Shanghai (Change if needed) + TZ: Asia/Shanghai + # root 密码 - root password + MYSQL_ROOT_PASSWORD: eh:GiX%cWyiV$WF_ + MYSQL_DATABASE: qnc + MYSQL_USER: qnc + MYSQL_PASSWORD: V3j@K9!qL5#wX4$p + ports: + - "20001:3306" + volumes: + # 数据挂载 - Data mounting + - ./data/mysql/data:/var/lib/mysql + # 日志 + command: + # 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配) + # Modify the Mysql 8.0 default password strategy to the original strategy (MySQL8.0 to change its default strategy will cause the password to be unable to match) + --default-authentication-plugin=mysql_native_password + --character-set-server=utf8mb4 + --collation-server=utf8mb4_general_ci + --explicit_defaults_for_timestamp=true + --lower_case_table_names=1 + privileged: true + restart: always + networks: + - qnc_net + + #redis容器 - Redis container + redis: + image: redis:6.2.5 + container_name: qnc_redis + ports: + - "20002:6379" + environment: + # 时区上海 - Time zone Shanghai (Change if needed) + TZ: Asia/Shanghai + volumes: + # 数据文件 - data files + - ./data/redis/data:/data:rw + command: "redis-server --requirepass A!02Myl>H}ZQfpxC --appendonly yes" + privileged: true + restart: always + networks: + - qnc_net + + +networks: + qnc_net: + driver: bridge + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1928f1e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3' + +services: + qnc_user_api: + image: cosmtrek/air + container_name: qnc_user_api + environment: + - TZ=Asia/Shanghai + working_dir: /app # 将工作目录设置为根目录 + ports: + - 31001:8888 + volumes: + - .:/app # 将项目根目录挂载到容器根目录 + entrypoint: air -c ./app/user/cmd/api/.air.toml + privileged: true + restart: always + networks: + - qnc_net + + +networks: + qnc_net: + driver: bridge + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1c0be8c --- /dev/null +++ b/go.mod @@ -0,0 +1,83 @@ +module qnc-server + +go 1.22.4 + +require ( + github.com/Masterminds/squirrel v1.5.4 + github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 + github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6 + github.com/alibabacloud-go/tea v1.2.2 + github.com/alibabacloud-go/tea-utils/v2 v2.0.7 + github.com/go-playground/validator/v10 v10.22.1 + github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/pkg/errors v0.9.1 + github.com/shopspring/decimal v1.4.0 + github.com/sony/sonyflake v1.2.0 + github.com/zeromicro/go-zero v1.7.3 + google.golang.org/grpc v1.67.1 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect + github.com/alibabacloud-go/debug v1.0.1 // indirect + github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect + github.com/alibabacloud-go/openapi-util v0.1.0 // indirect + github.com/alibabacloud-go/tea-utils v1.3.1 // indirect + github.com/alibabacloud-go/tea-xml v1.1.3 // indirect + github.com/aliyun/credentials-go v1.3.10 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/clbanning/mxj/v2 v2.5.5 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/fatih/color v1.17.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/openzipkin/zipkin-go v0.4.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/redis/go-redis/v9 v9.7.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tjfoc/gmsm v1.4.1 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/sdk v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..df79c23 --- /dev/null +++ b/go.sum @@ -0,0 +1,394 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= +github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA= +github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g= +github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY= +github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI= +github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE= +github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8= +github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc= +github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE= +github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg= +github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ= +github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo= +github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= +github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= +github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= +github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= +github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6 h1:UTl97mt2qfavxveqCkaVg4tKaZUPzA9RKbFIRaIdtdg= +github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6/go.mod h1:UWpcGrWwTbES9QW7OQ7xDffukMJ/l7lzioixIz8+lgY= +github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= +github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY= +github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= +github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU= +github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= +github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I= +github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4= +github.com/alibabacloud-go/tea-utils/v2 v2.0.3/go.mod h1:sj1PbjPodAVTqGTA3olprfeeqqmwD0A5OQz94o9EuXQ= +github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4= +github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= +github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0= +github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= +github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0= +github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= +github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA= +github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= +github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= +github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= +github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM= +github.com/aliyun/credentials-go v1.3.10 h1:45Xxrae/evfzQL9V10zL3xX31eqgLWEaIdCoPipOEQA= +github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= +github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ= +github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/zeromicro/go-zero v1.7.3 h1:yDUQF2DXDhUHc77/NZF6mzsoRPMBfldjPmG2O/ZSzss= +github.com/zeromicro/go-zero v1.7.3/go.mod h1:9JIW3gHBGuc9LzvjZnNwINIq9QdiKu3AigajLtkJamQ= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA= +go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY= +go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= +gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= +gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= diff --git a/modd.conf b/modd.conf new file mode 100644 index 0000000..d629e87 --- /dev/null +++ b/modd.conf @@ -0,0 +1,5 @@ +#user +app/user/cmd/api/**/*.go { + prep: go build -o data/server/user-api -v app/user/cmd/api/user.go + daemon +sigkill: ./data/server/user-api -f app/user/cmd/api/etc/user.yaml +} diff --git a/pkg/lzkit/validator/error_messages.go b/pkg/lzkit/validator/error_messages.go new file mode 100644 index 0000000..3fd76fe --- /dev/null +++ b/pkg/lzkit/validator/error_messages.go @@ -0,0 +1,23 @@ +package validator + +// 定义自定义错误消息 +var customMessages = map[string]string{ + "Name.min": "姓名不能少于1个字", + "Name.required": "姓名是必填项", + "Name.name": "姓名只能包含中文", + "Mobile.required": "手机号是必填项", + "Mobile.min": "电话号码必须为有效的中国电话号码", + "Mobile.max": "电话号码必须为有效的中国电话号码", + "Mobile.mobile": "电话号码必须为有效的中国电话号码", + "IDCard.required": "身份证号是必填项", + "IDCard.idCard": "无效的身份证号码", +} + +// 获取自定义错误消息 +func GetErrorMessage(field, tag string) string { + key := field + "." + tag + if msg, exists := customMessages[key]; exists { + return msg + } + return "请输入正确格式的参数" +} diff --git a/pkg/lzkit/validator/validator.go b/pkg/lzkit/validator/validator.go new file mode 100644 index 0000000..7780199 --- /dev/null +++ b/pkg/lzkit/validator/validator.go @@ -0,0 +1,129 @@ +package validator + +import ( + "errors" + "fmt" + "github.com/go-playground/validator/v10" + "regexp" +) + +var validate *validator.Validate + +// 初始化自定义校验器 +func init() { + validate = validator.New() + + if err := validate.RegisterValidation("name", validName); err != nil { + panic(fmt.Sprintf("注册 name 验证器时发生错误: %v", err)) + } + + // 注册自定义验证器 validmobile + if err := validate.RegisterValidation("mobile", validmobile); err != nil { + panic(fmt.Sprintf("注册 mobile 验证器时发生错误: %v", err)) + } + + // 注册自定义验证器 validDate + if err := validate.RegisterValidation("date", validDate); err != nil { + panic(fmt.Sprintf("注册 date 验证器时发生错误: %v", err)) + } + + // 注册自定义验证器 validIDCard + if err := validate.RegisterValidation("idCard", validIDCard); err != nil { + panic(fmt.Sprintf("注册 idCard 验证器时发生错误: %v", err)) + } + + if err := validate.RegisterValidation("bankCard", validBankCard); err != nil { + panic(fmt.Sprintf("注册 bankCard 验证器时发生错误: %v", err)) + } + + if err := validate.RegisterValidation("USCI", validUSCI); err != nil { + panic(fmt.Sprintf("注册 USCI 社会统一信用代码 验证器时发生错误: %v", err)) + } + + if err := validate.RegisterValidation("mobileType", validMobileType); err != nil { + panic(fmt.Sprintf("注册 mobileType 验证器时发生错误: %v", err)) + } +} + +// Validate 校验参数逻辑 +func Validate(req interface{}) error { + if err := validate.Struct(req); err != nil { + // 检查 err 是否是 ValidationErrors 类型 + if validationErrors, ok := err.(validator.ValidationErrors); ok { + for _, validationErr := range validationErrors { + field := validationErr.StructField() + tag := validationErr.Tag() + return errors.New(GetErrorMessage(field, tag)) + } + } else { + // 其他错误处理 + return fmt.Errorf("验证时出现未知错误: %v", err) + } + } + return nil +} + +// 自定义的名称验证 +func validName(fl validator.FieldLevel) bool { + name := fl.Field().String() + validNamePattern := `^[\p{Han}]+$` + matched, _ := regexp.MatchString(validNamePattern, name) + return matched +} + +// 自定义的手机号验证 +func validmobile(fl validator.FieldLevel) bool { + phone := fl.Field().String() + validmobilePattern := `^1[3-9]\d{9}$` + matched, _ := regexp.MatchString(validmobilePattern, phone) + return matched +} + +// 自定义正则表达式校验 yyyyMMdd 格式 +func validDate(fl validator.FieldLevel) bool { + date := fl.Field().String() + validDatePattern := `^\d{4}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$` + matched, _ := regexp.MatchString(validDatePattern, date) + return matched +} + +// 自定义身份证校验 +func validIDCard(fl validator.FieldLevel) bool { + id := fl.Field().String() + validIDPattern := `^\d{17}(\d|X|x)$` // 匹配18位身份证号码 + matched, _ := regexp.MatchString(validIDPattern, id) + return matched +} + +func validBankCard(fl validator.FieldLevel) bool { + bankCard := fl.Field().String() + // 银行卡号一般是13到19位的数字 + validBankCardPattern := `^\d{13,19}$` + matched, _ := regexp.MatchString(validBankCardPattern, bankCard) + return matched +} + +func validUSCI(fl validator.FieldLevel) bool { + usci := fl.Field().String() + // 社会信用代码为18位数字和大写字母的组合,最后一位为校验码 + validUSCIPattern := `^[1-9A-Z]{2}[0-9]{6}[0-9A-Z]{9}[0-9A-Z]$` + matched, _ := regexp.MatchString(validUSCIPattern, usci) + return matched +} + +// 自定义的手机号类型验证(可以为空) +func validMobileType(fl validator.FieldLevel) bool { + mobileType := fl.Field().String() + if mobileType == "" { + return true // 如果为空,认为是有效的 + } + + // 校验是否是 CTCC, CMCC, CUCC 之一 + validTypes := map[string]bool{ + "CTCC": true, // 中国电信 + "CMCC": true, // 中国移动 + "CUCC": true, // 中国联通 + } + + return validTypes[mobileType] +} diff --git a/tmp/build-errors.log b/tmp/build-errors.log new file mode 100644 index 0000000..1cada71 --- /dev/null +++ b/tmp/build-errors.log @@ -0,0 +1 @@ +exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file diff --git a/user/cmd/api/tmp/build-errors.log b/user/cmd/api/tmp/build-errors.log new file mode 100644 index 0000000..05e5985 --- /dev/null +++ b/user/cmd/api/tmp/build-errors.log @@ -0,0 +1 @@ +exit status 1 \ No newline at end of file