From 96c43325c083469d45c71629d433a3de5e677cd9 Mon Sep 17 00:00:00 2001 From: eson <9673575+githubcontent@user.noreply.gitee.com> Date: Sun, 25 Jun 2023 18:30:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=90=8E=E5=8F=B0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E8=A1=A8=E7=9B=B4=E6=8E=A5=E5=88=86=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fs_gen_api_backend.sh | 7 ++ go.mod | 2 +- go.sum | 4 +- goctl_template_backend/api/config.tpl | 11 ++ goctl_template_backend/api/context.tpl | 59 +++++++++ goctl_template_backend/api/etc.tpl | 8 ++ goctl_template_backend/api/handler.tpl | 74 +++++++++++ goctl_template_backend/api/logic.tpl | 29 +++++ goctl_template_backend/api/main.tpl | 46 +++++++ goctl_template_backend/api/middleware.tpl | 19 +++ goctl_template_backend/api/route-addition.tpl | 4 + goctl_template_backend/api/routes.tpl | 13 ++ goctl_template_backend/api/template.tpl | 24 ++++ goctl_template_backend/api/types.tpl | 50 ++++++++ goctl_template_backend/docker/docker.tpl | 33 +++++ goctl_template_backend/gateway/etc.tpl | 18 +++ goctl_template_backend/gateway/main.tpl | 20 +++ goctl_template_backend/kube/deployment.tpl | 117 ++++++++++++++++++ goctl_template_backend/kube/job.tpl | 37 ++++++ goctl_template_backend/model/delete.tpl | 14 +++ goctl_template_backend/model/err.tpl | 5 + goctl_template_backend/model/field.tpl | 1 + .../model/find-one-by-field-extra-method.tpl | 8 ++ .../model/find-one-by-field.tpl | 30 +++++ goctl_template_backend/model/find-one.tpl | 26 ++++ .../model/import-no-cache.tpl | 13 ++ goctl_template_backend/model/import.tpl | 14 +++ goctl_template_backend/model/insert.tpl | 9 ++ .../model/interface-delete.tpl | 1 + .../model/interface-find-one-by-field.tpl | 1 + .../model/interface-find-one.tpl | 1 + .../model/interface-insert.tpl | 1 + .../model/interface-update.tpl | 1 + goctl_template_backend/model/model-gen.tpl | 13 ++ goctl_template_backend/model/model-new.tpl | 6 + goctl_template_backend/model/model.tpl | 30 +++++ goctl_template_backend/model/table-name.tpl | 3 + goctl_template_backend/model/tag.tpl | 1 + goctl_template_backend/model/types.tpl | 14 +++ goctl_template_backend/model/update.tpl | 14 +++ goctl_template_backend/model/var.tpl | 8 ++ goctl_template_backend/mongo/err.tpl | 12 ++ goctl_template_backend/mongo/model.tpl | 78 ++++++++++++ goctl_template_backend/mongo/model_custom.tpl | 38 ++++++ goctl_template_backend/mongo/model_types.tpl | 14 +++ goctl_template_backend/newapi/newtemplate.tpl | 12 ++ goctl_template_backend/rpc/call.tpl | 33 +++++ goctl_template_backend/rpc/config.tpl | 7 ++ goctl_template_backend/rpc/etc.tpl | 6 + goctl_template_backend/rpc/logic-func.tpl | 6 + goctl_template_backend/rpc/logic.tpl | 24 ++++ goctl_template_backend/rpc/main.tpl | 36 ++++++ goctl_template_backend/rpc/server-func.tpl | 6 + goctl_template_backend/rpc/server.tpl | 22 ++++ goctl_template_backend/rpc/svc.tpl | 13 ++ goctl_template_backend/rpc/template.tpl | 16 +++ model/gmodel/fs_backend_user_gen.go | 24 ++++ model/gmodel/fs_backend_user_logic.go | 15 +++ model/gmodel/var_gen.go | 22 +--- server/backend/backend.go | 49 ++++++++ server/backend/etc/backend.yaml | 8 ++ server/backend/internal/config/config.go | 13 ++ .../handler/backenduserloginhandler.go | 49 ++++++++ .../handler/quotationdetailhandler.go | 73 +++++++++++ server/backend/internal/handler/routes.go | 27 ++++ .../internal/logic/backenduserloginlogic.go | 70 +++++++++++ .../internal/logic/quotationdetaillogic.go | 34 +++++ server/backend/internal/svc/servicecontext.go | 57 +++++++++ server/backend/internal/types/types.go | 74 +++++++++++ server/webset/test/wetset_test.go | 22 ---- server_api/backend.api | 34 +++++ utils/auth/user.go | 72 ++++++----- 72 files changed, 1678 insertions(+), 77 deletions(-) create mode 100755 fs_gen_api_backend.sh create mode 100644 goctl_template_backend/api/config.tpl create mode 100644 goctl_template_backend/api/context.tpl create mode 100644 goctl_template_backend/api/etc.tpl create mode 100644 goctl_template_backend/api/handler.tpl create mode 100644 goctl_template_backend/api/logic.tpl create mode 100644 goctl_template_backend/api/main.tpl create mode 100644 goctl_template_backend/api/middleware.tpl create mode 100644 goctl_template_backend/api/route-addition.tpl create mode 100644 goctl_template_backend/api/routes.tpl create mode 100644 goctl_template_backend/api/template.tpl create mode 100644 goctl_template_backend/api/types.tpl create mode 100644 goctl_template_backend/docker/docker.tpl create mode 100644 goctl_template_backend/gateway/etc.tpl create mode 100644 goctl_template_backend/gateway/main.tpl create mode 100644 goctl_template_backend/kube/deployment.tpl create mode 100644 goctl_template_backend/kube/job.tpl create mode 100644 goctl_template_backend/model/delete.tpl create mode 100644 goctl_template_backend/model/err.tpl create mode 100644 goctl_template_backend/model/field.tpl create mode 100644 goctl_template_backend/model/find-one-by-field-extra-method.tpl create mode 100644 goctl_template_backend/model/find-one-by-field.tpl create mode 100644 goctl_template_backend/model/find-one.tpl create mode 100644 goctl_template_backend/model/import-no-cache.tpl create mode 100644 goctl_template_backend/model/import.tpl create mode 100644 goctl_template_backend/model/insert.tpl create mode 100644 goctl_template_backend/model/interface-delete.tpl create mode 100644 goctl_template_backend/model/interface-find-one-by-field.tpl create mode 100644 goctl_template_backend/model/interface-find-one.tpl create mode 100644 goctl_template_backend/model/interface-insert.tpl create mode 100644 goctl_template_backend/model/interface-update.tpl create mode 100644 goctl_template_backend/model/model-gen.tpl create mode 100644 goctl_template_backend/model/model-new.tpl create mode 100644 goctl_template_backend/model/model.tpl create mode 100644 goctl_template_backend/model/table-name.tpl create mode 100644 goctl_template_backend/model/tag.tpl create mode 100644 goctl_template_backend/model/types.tpl create mode 100644 goctl_template_backend/model/update.tpl create mode 100644 goctl_template_backend/model/var.tpl create mode 100644 goctl_template_backend/mongo/err.tpl create mode 100644 goctl_template_backend/mongo/model.tpl create mode 100644 goctl_template_backend/mongo/model_custom.tpl create mode 100644 goctl_template_backend/mongo/model_types.tpl create mode 100644 goctl_template_backend/newapi/newtemplate.tpl create mode 100644 goctl_template_backend/rpc/call.tpl create mode 100644 goctl_template_backend/rpc/config.tpl create mode 100644 goctl_template_backend/rpc/etc.tpl create mode 100644 goctl_template_backend/rpc/logic-func.tpl create mode 100644 goctl_template_backend/rpc/logic.tpl create mode 100644 goctl_template_backend/rpc/main.tpl create mode 100644 goctl_template_backend/rpc/server-func.tpl create mode 100644 goctl_template_backend/rpc/server.tpl create mode 100644 goctl_template_backend/rpc/svc.tpl create mode 100644 goctl_template_backend/rpc/template.tpl create mode 100644 model/gmodel/fs_backend_user_gen.go create mode 100644 model/gmodel/fs_backend_user_logic.go create mode 100644 server/backend/backend.go create mode 100644 server/backend/etc/backend.yaml create mode 100644 server/backend/internal/config/config.go create mode 100644 server/backend/internal/handler/backenduserloginhandler.go create mode 100644 server/backend/internal/handler/quotationdetailhandler.go create mode 100644 server/backend/internal/handler/routes.go create mode 100644 server/backend/internal/logic/backenduserloginlogic.go create mode 100644 server/backend/internal/logic/quotationdetaillogic.go create mode 100644 server/backend/internal/svc/servicecontext.go create mode 100644 server/backend/internal/types/types.go create mode 100644 server_api/backend.api diff --git a/fs_gen_api_backend.sh b/fs_gen_api_backend.sh new file mode 100755 index 00000000..037cfba9 --- /dev/null +++ b/fs_gen_api_backend.sh @@ -0,0 +1,7 @@ +#! /bin/bash +# 专为后台序列化 +name=${1%\/} +echo $name +goctl api go -api server_api/$name.api -dir server/$name --home ./goctl_template_backend/ +# ctxName=server/$name/internal/svc/servicecontext.go +# gofmt -w $ctxName \ No newline at end of file diff --git a/go.mod b/go.mod index cc7770cd..d4a3e687 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( ) require ( - github.com/474420502/requests v1.33.3 + github.com/474420502/requests v1.33.4 github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/tidwall/gjson v1.12.0 diff --git a/go.sum b/go.sum index 53209a58..e5372365 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/474420502/random v0.4.1 h1:HUUyLXRWMijVb7CJoEC16f0aFQOW25Lkr80Mut6PoKU= -github.com/474420502/requests v1.33.3 h1:CZs7M9OoaDMTJBCPQh4kob+rgFp2R9vHVZD0kaVDdrs= -github.com/474420502/requests v1.33.3/go.mod h1:5qAlksMg7JIrEXrpkxw1++4Z5W2tUkZbHA6M7oq1r/0= +github.com/474420502/requests v1.33.4 h1:cTz78KQdH5iKu9BPYuSFc45xxiGqSuKJB9BHCPzV+/0= +github.com/474420502/requests v1.33.4/go.mod h1:5qAlksMg7JIrEXrpkxw1++4Z5W2tUkZbHA6M7oq1r/0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= diff --git a/goctl_template_backend/api/config.tpl b/goctl_template_backend/api/config.tpl new file mode 100644 index 00000000..dff7003f --- /dev/null +++ b/goctl_template_backend/api/config.tpl @@ -0,0 +1,11 @@ +package config + +import {{.authImport}} + +type Config struct { + rest.RestConf + SourceMysql string + Auth types.Auth + {{.auth}} + {{.jwtTrans}} +} diff --git a/goctl_template_backend/api/context.tpl b/goctl_template_backend/api/context.tpl new file mode 100644 index 00000000..543ba1ae --- /dev/null +++ b/goctl_template_backend/api/context.tpl @@ -0,0 +1,59 @@ +package svc + +import ( + {{.configImport}} + "errors" + "fmt" + "net/http" + + "fusenapi/initalize" + "fusenapi/model/gmodel" + + "gorm.io/gorm" + "github.com/golang-jwt/jwt" +) + +type ServiceContext struct { + Config {{.config}} + {{.middleware}} + MysqlConn *gorm.DB + AllModels *gmodel.AllModelsGen +} + +func NewServiceContext(c {{.config}}) *ServiceContext { + + return &ServiceContext{ + Config: c, + MysqlConn: initalize.InitMysql(c.SourceMysql), + AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)), + {{.middlewareAssignment}} + } +} + + +func (svcCtx *ServiceContext) ParseJwtToken(r *http.Request) (jwt.MapClaims, error) { + AuthKey := r.Header.Get("Authorization") + + if len(AuthKey) <= 50 { + return nil, errors.New(fmt.Sprint("Error parsing token, len:", len(AuthKey))) + } + + token, err := jwt.Parse(AuthKey, func(token *jwt.Token) (interface{}, error) { + // 检查签名方法是否为 HS256 + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + // 返回用于验证签名的密钥 + return []byte(svcCtx.Config.Auth.AccessSecret), nil + }) + if err != nil { + return nil, errors.New(fmt.Sprint("Error parsing token:", err)) + } + + // 验证成功返回 + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + return claims, nil + } + + return nil, errors.New(fmt.Sprint("Invalid token", err)) +} \ No newline at end of file diff --git a/goctl_template_backend/api/etc.tpl b/goctl_template_backend/api/etc.tpl new file mode 100644 index 00000000..d2c383f3 --- /dev/null +++ b/goctl_template_backend/api/etc.tpl @@ -0,0 +1,8 @@ +Name: {{.serviceName}} +Host: {{.host}} +Port: {{.port}} +SourceMysql: "" +Auth: + AccessSecret: fusen2023 + AccessExpire: 604800 + RefreshAfter: 345600 \ No newline at end of file diff --git a/goctl_template_backend/api/handler.tpl b/goctl_template_backend/api/handler.tpl new file mode 100644 index 00000000..5bd709a9 --- /dev/null +++ b/goctl_template_backend/api/handler.tpl @@ -0,0 +1,74 @@ +package {{.PkgName}} + +import ( + "net/http" + "errors" + + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/rest/httpx" + + "fusenapi/utils/auth" + "fusenapi/utils/basic" + + {{.ImportPackages}} +) + +func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var ( + // 定义错误变量 + err error + // 定义用户信息变量 + userinfo *auth.UserInfo + ) + // 解析JWT token,并对空用户进行判断 + claims, err := svcCtx.ParseJwtToken(r) + // 如果解析JWT token出错,则返回未授权的JSON响应并记录错误消息 + if err != nil || claims == nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 401, // 返回401状态码,表示未授权 + Message: "unauthorized", // 返回未授权信息 + }) + logx.Info("unauthorized:", err.Error()) // 记录错误日志 + return + } + + + + // 从token中获取对应的用户信息 + userinfo, err = auth.GetBackendUserInfoFormMapClaims(claims) + // 如果获取用户信息出错,则返回未授权的JSON响应并记录错误消息 + if err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 401, + Message: "unauthorized", + }) + logx.Info("unauthorized:", err.Error()) + return + } + + + {{if .HasRequest}}var req types.{{.RequestType}} + // 如果端点有请求结构体,则使用httpx.Parse方法从HTTP请求体中解析请求数据 + if err := httpx.Parse(r, &req); err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 510, + Message: "parameter error", + }) + logx.Info(err) + return + } + // 创建一个业务逻辑层实例 + {{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx) + {{if .HasResp}}resp{{end}} := l.{{.Call}}({{if .HasRequest}}&req, {{end}}userinfo) + // 如果响应不为nil,则使用httpx.OkJsonCtx方法返回JSON响应; + if resp != nil { + {{if .HasResp}}httpx.OkJsonCtx(r.Context(), w, resp){{else}}httpx.Ok(w){{end}} + } else { + err := errors.New("server logic is error, resp must not be nil") + httpx.ErrorCtx(r.Context(), w, err) + logx.Error(err) + } + } +} diff --git a/goctl_template_backend/api/logic.tpl b/goctl_template_backend/api/logic.tpl new file mode 100644 index 00000000..09147e36 --- /dev/null +++ b/goctl_template_backend/api/logic.tpl @@ -0,0 +1,29 @@ +package {{.pkgName}} + +import ( + "fusenapi/utils/auth" + "fusenapi/utils/basic" + + {{.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}}, userinfo *auth.UserInfo) (resp *basic.Response) { + // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) + // userinfo 传入值时, 一定不为null + + {{.returnString}} resp.SetStatus(basic.CodeOK) +} diff --git a/goctl_template_backend/api/main.tpl b/goctl_template_backend/api/main.tpl new file mode 100644 index 00000000..709ddbd1 --- /dev/null +++ b/goctl_template_backend/api/main.tpl @@ -0,0 +1,46 @@ +package main + +import ( + "flag" + "fmt" + + {{.importPackages}} +) + +var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file") + +func main() { + flag.Parse() + + var c config.Config + conf.MustLoad(*configFile, &c) + + server := rest.MustNewServer(c.RestConf) + defer server.Stop() + + ctx := svc.NewServiceContext(c) + handler.RegisterHandlers(server, ctx) + + fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) + server.Start() +} + + + +// var testConfigFile = flag.String("f", "../etc/{{.serviceName}}.yaml", "the config file") +// var cnf config.Config + +// func GetTestServer() *rest.Server { +// flag.Parse() + +// conf.MustLoad(*testConfigFile, &cnf) + +// server := rest.MustNewServer(cnf.RestConf) +// defer server.Stop() + +// ctx := svc.NewServiceContext(cnf) +// handler.RegisterHandlers(server, ctx) + +// fmt.Printf("Starting server at %s:%d...\n", cnf.Host, cnf.Port) +// return server +// } \ No newline at end of file diff --git a/goctl_template_backend/api/middleware.tpl b/goctl_template_backend/api/middleware.tpl new file mode 100644 index 00000000..3a9f8e91 --- /dev/null +++ b/goctl_template_backend/api/middleware.tpl @@ -0,0 +1,19 @@ +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/goctl_template_backend/api/route-addition.tpl b/goctl_template_backend/api/route-addition.tpl new file mode 100644 index 00000000..bb8a5dfe --- /dev/null +++ b/goctl_template_backend/api/route-addition.tpl @@ -0,0 +1,4 @@ + + server.AddRoutes( + {{.routes}} {{.jwt}}{{.signature}} {{.prefix}} {{.timeout}} {{.maxBytes}} + ) diff --git a/goctl_template_backend/api/routes.tpl b/goctl_template_backend/api/routes.tpl new file mode 100644 index 00000000..f13fb111 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/api/template.tpl b/goctl_template_backend/api/template.tpl new file mode 100644 index 00000000..2176441c --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/api/types.tpl b/goctl_template_backend/api/types.tpl new file mode 100644 index 00000000..b1734d42 --- /dev/null +++ b/goctl_template_backend/api/types.tpl @@ -0,0 +1,50 @@ +// Code generated by goctl. DO NOT EDIT. +package types +import ( + {{if .containsTime}}"time"{{end}} + + "fusenapi/utils/basic" +) +{{.types}} + + +// Set 设置Response的Code和Message值 +func (resp *Response) Set(Code int, Message string) *Response { + return &Response{ + Code: Code, + Message: Message, + } +} + +// Set 设置整个Response +func (resp *Response) SetWithData(Code int, Message string, Data interface{}) *Response { + return &Response{ + Code: Code, + Message: Message, + Data: Data, + } +} + +// SetStatus 设置默认StatusResponse(内部自定义) 默认msg, 可以带data, data只使用一个参数 +func (resp *Response) SetStatus(sr *basic.StatusResponse, data ...interface{}) *Response { + newResp := &Response{ + Code: sr.Code, + } + if len(data) == 1 { + newResp.Data = data[0] + } + return newResp +} + +// SetStatusWithMessage 设置默认StatusResponse(内部自定义) 非默认msg, 可以带data, data只使用一个参数 +func (resp *Response) SetStatusWithMessage(sr *basic.StatusResponse, msg string, data ...interface{}) *Response { + newResp := &Response{ + Code: sr.Code, + Message: msg, + } + if len(data) == 1 { + newResp.Data = data[0] + } + return newResp +} + \ No newline at end of file diff --git a/goctl_template_backend/docker/docker.tpl b/goctl_template_backend/docker/docker.tpl new file mode 100644 index 00000000..d1b5ff44 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/gateway/etc.tpl b/goctl_template_backend/gateway/etc.tpl new file mode 100644 index 00000000..0a70f1ac --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/gateway/main.tpl b/goctl_template_backend/gateway/main.tpl new file mode 100644 index 00000000..62734516 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/kube/deployment.tpl b/goctl_template_backend/kube/deployment.tpl new file mode 100644 index 00000000..14145df2 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/kube/job.tpl b/goctl_template_backend/kube/job.tpl new file mode 100644 index 00000000..0da72ede --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/model/delete.tpl b/goctl_template_backend/model/delete.tpl new file mode 100644 index 00000000..fb995c26 --- /dev/null +++ b/goctl_template_backend/model/delete.tpl @@ -0,0 +1,14 @@ +func (m *default{{.upperStartCamelObject}}Model) Delete(ctx context.Context, {{.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) + return conn.ExecCtx(ctx, query, {{.lowerStartCamelPrimaryKey}}) + }, {{.keyValues}}){{else}}query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table) + _,err:=m.conn.ExecCtx(ctx, query, {{.lowerStartCamelPrimaryKey}}){{end}} + return err +} diff --git a/goctl_template_backend/model/err.tpl b/goctl_template_backend/model/err.tpl new file mode 100644 index 00000000..dc5eac4a --- /dev/null +++ b/goctl_template_backend/model/err.tpl @@ -0,0 +1,5 @@ +package {{.pkg}} + +import "github.com/zeromicro/go-zero/core/stores/sqlx" + +var ErrNotFound = sqlx.ErrNotFound diff --git a/goctl_template_backend/model/field.tpl b/goctl_template_backend/model/field.tpl new file mode 100644 index 00000000..6b4ed387 --- /dev/null +++ b/goctl_template_backend/model/field.tpl @@ -0,0 +1 @@ +{{.name}} {{.type}} {{.tag}} {{if .hasComment}}// {{.comment}}{{end}} \ No newline at end of file diff --git a/goctl_template_backend/model/find-one-by-field-extra-method.tpl b/goctl_template_backend/model/find-one-by-field-extra-method.tpl new file mode 100644 index 00000000..7e1df69d --- /dev/null +++ b/goctl_template_backend/model/find-one-by-field-extra-method.tpl @@ -0,0 +1,8 @@ +func (m *default{{.upperStartCamelObject}}Model) formatPrimary(primary any) string { + return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary) +} + +func (m *default{{.upperStartCamelObject}}Model) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error { + query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = {{if .postgreSql}}$1{{else}}?{{end}} limit 1", {{.lowerStartCamelObject}}Rows, m.table ) + return conn.QueryRowCtx(ctx, v, query, primary) +} diff --git a/goctl_template_backend/model/find-one-by-field.tpl b/goctl_template_backend/model/find-one-by-field.tpl new file mode 100644 index 00000000..390fb01c --- /dev/null +++ b/goctl_template_backend/model/find-one-by-field.tpl @@ -0,0 +1,30 @@ +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 any) (i any, e error) { + query := fmt.Sprintf("select %s from %s where {{.originalField}} limit 1", {{.lowerStartCamelObject}}Rows, m.table) + if err := conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelField}}); 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}} limit 1", {{.lowerStartCamelObject}}Rows, m.table ) + err := m.conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelField}}) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +}{{end}} diff --git a/goctl_template_backend/model/find-one.tpl b/goctl_template_backend/model/find-one.tpl new file mode 100644 index 00000000..a8085580 --- /dev/null +++ b/goctl_template_backend/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 any) error { + query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} limit 1", {{.lowerStartCamelObject}}Rows, m.table) + return conn.QueryRowCtx(ctx, v, query, {{.lowerStartCamelPrimaryKey}}) + }) + 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}} limit 1", {{.lowerStartCamelObject}}Rows, m.table) + var resp {{.upperStartCamelObject}} + err := m.conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelPrimaryKey}}) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + }{{end}} +} diff --git a/goctl_template_backend/model/import-no-cache.tpl b/goctl_template_backend/model/import-no-cache.tpl new file mode 100644 index 00000000..d99564e1 --- /dev/null +++ b/goctl_template_backend/model/import-no-cache.tpl @@ -0,0 +1,13 @@ +import ( + "context" + "database/sql" + "fmt" + "strings" + {{if .time}}"time"{{end}} + + {{if .containsPQ}}"github.com/lib/pq"{{end}} + "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/goctl_template_backend/model/import.tpl b/goctl_template_backend/model/import.tpl new file mode 100644 index 00000000..7e0c40f5 --- /dev/null +++ b/goctl_template_backend/model/import.tpl @@ -0,0 +1,14 @@ +import ( + "context" + "database/sql" + "fmt" + "strings" + {{if .time}}"time"{{end}} + + {{if .containsPQ}}"github.com/lib/pq"{{end}} + "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/goctl_template_backend/model/insert.tpl b/goctl_template_backend/model/insert.tpl new file mode 100644 index 00000000..08e02bce --- /dev/null +++ b/goctl_template_backend/model/insert.tpl @@ -0,0 +1,9 @@ +func (m *default{{.upperStartCamelObject}}Model) Insert(ctx context.Context, data *{{.upperStartCamelObject}}) (sql.Result,error) { + {{if .withCache}}{{.keys}} + ret, err := 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) + return conn.ExecCtx(ctx, query, {{.expressionValues}}) + }, {{.keyValues}}){{else}}query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet) + ret,err:=m.conn.ExecCtx(ctx, query, {{.expressionValues}}){{end}} + return ret,err +} diff --git a/goctl_template_backend/model/interface-delete.tpl b/goctl_template_backend/model/interface-delete.tpl new file mode 100644 index 00000000..d10978b9 --- /dev/null +++ b/goctl_template_backend/model/interface-delete.tpl @@ -0,0 +1 @@ +Delete(ctx context.Context, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) error \ No newline at end of file diff --git a/goctl_template_backend/model/interface-find-one-by-field.tpl b/goctl_template_backend/model/interface-find-one-by-field.tpl new file mode 100644 index 00000000..e18ded7a --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/model/interface-find-one.tpl b/goctl_template_backend/model/interface-find-one.tpl new file mode 100644 index 00000000..a7a54401 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/model/interface-insert.tpl b/goctl_template_backend/model/interface-insert.tpl new file mode 100644 index 00000000..28724037 --- /dev/null +++ b/goctl_template_backend/model/interface-insert.tpl @@ -0,0 +1 @@ +Insert(ctx context.Context, data *{{.upperStartCamelObject}}) (sql.Result,error) \ No newline at end of file diff --git a/goctl_template_backend/model/interface-update.tpl b/goctl_template_backend/model/interface-update.tpl new file mode 100644 index 00000000..1920425d --- /dev/null +++ b/goctl_template_backend/model/interface-update.tpl @@ -0,0 +1 @@ +Update(ctx context.Context, {{if .containsIndexCache}}newData{{else}}data{{end}} *{{.upperStartCamelObject}}) error \ No newline at end of file diff --git a/goctl_template_backend/model/model-gen.tpl b/goctl_template_backend/model/model-gen.tpl new file mode 100644 index 00000000..cc3b5954 --- /dev/null +++ b/goctl_template_backend/model/model-gen.tpl @@ -0,0 +1,13 @@ +// Code generated by goctl. DO NOT EDIT. + +package {{.pkg}} +{{.imports}} +{{.vars}} +{{.types}} +{{.new}} +{{.delete}} +{{.find}} +{{.insert}} +{{.update}} +{{.extraMethod}} +{{.tableName}} diff --git a/goctl_template_backend/model/model-new.tpl b/goctl_template_backend/model/model-new.tpl new file mode 100644 index 00000000..76598ecd --- /dev/null +++ b/goctl_template_backend/model/model-new.tpl @@ -0,0 +1,6 @@ +func new{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf, opts ...cache.Option{{end}}) *default{{.upperStartCamelObject}}Model { + return &default{{.upperStartCamelObject}}Model{ + {{if .withCache}}CachedConn: sqlc.NewConn(conn, c, opts...){{else}}conn:conn{{end}}, + table: {{.table}}, + } +} diff --git a/goctl_template_backend/model/model.tpl b/goctl_template_backend/model/model.tpl new file mode 100644 index 00000000..3b1b1013 --- /dev/null +++ b/goctl_template_backend/model/model.tpl @@ -0,0 +1,30 @@ +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, opts ...cache.Option{{end}}) {{.upperStartCamelObject}}Model { + return &custom{{.upperStartCamelObject}}Model{ + default{{.upperStartCamelObject}}Model: new{{.upperStartCamelObject}}Model(conn{{if .withCache}}, c, opts...{{end}}), + } +} diff --git a/goctl_template_backend/model/table-name.tpl b/goctl_template_backend/model/table-name.tpl new file mode 100644 index 00000000..8b14e33a --- /dev/null +++ b/goctl_template_backend/model/table-name.tpl @@ -0,0 +1,3 @@ +func (m *default{{.upperStartCamelObject}}Model) tableName() string { + return m.table +} diff --git a/goctl_template_backend/model/tag.tpl b/goctl_template_backend/model/tag.tpl new file mode 100644 index 00000000..8e1ddf0d --- /dev/null +++ b/goctl_template_backend/model/tag.tpl @@ -0,0 +1 @@ +`db:"{{.field}}"` \ No newline at end of file diff --git a/goctl_template_backend/model/types.tpl b/goctl_template_backend/model/types.tpl new file mode 100644 index 00000000..960cf2b3 --- /dev/null +++ b/goctl_template_backend/model/types.tpl @@ -0,0 +1,14 @@ +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/goctl_template_backend/model/update.tpl b/goctl_template_backend/model/update.tpl new file mode 100644 index 00000000..41b9331c --- /dev/null +++ b/goctl_template_backend/model/update.tpl @@ -0,0 +1,14 @@ +func (m *default{{.upperStartCamelObject}}Model) Update(ctx context.Context, {{if .containsIndexCache}}newData{{else}}data{{end}} *{{.upperStartCamelObject}}) error { + {{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne(ctx, newData.{{.upperStartCamelPrimaryKey}}) + if err!=nil{ + return err + } + +{{end}} {{.keys}} + _, {{if .containsIndexCache}}err{{else}}err:{{end}}= 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) + 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) + _,err:=m.conn.ExecCtx(ctx, query, {{.expressionValues}}){{end}} + return err +} diff --git a/goctl_template_backend/model/var.tpl b/goctl_template_backend/model/var.tpl new file mode 100644 index 00000000..c11fe535 --- /dev/null +++ b/goctl_template_backend/model/var.tpl @@ -0,0 +1,8 @@ +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}} {{.ignoreColumns}}), ","){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}", {{end}} {{.ignoreColumns}}), ","){{end}} +{{.lowerStartCamelObject}}RowsWithPlaceHolder = {{if .postgreSql}}builder.PostgreSqlJoin(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", {{.ignoreColumns}})){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", {{.ignoreColumns}}), "=?,") + "=?"{{end}} + +{{if .withCache}}{{.cacheKeys}}{{end}} +) diff --git a/goctl_template_backend/mongo/err.tpl b/goctl_template_backend/mongo/err.tpl new file mode 100644 index 00000000..418c875f --- /dev/null +++ b/goctl_template_backend/mongo/err.tpl @@ -0,0 +1,12 @@ +package gmodel + +import ( + "errors" + + "github.com/zeromicro/go-zero/core/stores/mon" +) + +var ( + ErrNotFound = mon.ErrNotFound + ErrInvalidObjectId = errors.New("invalid objectId") +) diff --git a/goctl_template_backend/mongo/model.tpl b/goctl_template_backend/mongo/model.tpl new file mode 100644 index 00000000..9c2d94ce --- /dev/null +++ b/goctl_template_backend/mongo/model.tpl @@ -0,0 +1,78 @@ +// Code generated by goctl. DO NOT EDIT. +package gmodel + +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/goctl_template_backend/mongo/model_custom.tpl b/goctl_template_backend/mongo/model_custom.tpl new file mode 100644 index 00000000..c75bdcd9 --- /dev/null +++ b/goctl_template_backend/mongo/model_custom.tpl @@ -0,0 +1,38 @@ +package gmodel + +{{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/goctl_template_backend/mongo/model_types.tpl b/goctl_template_backend/mongo/model_types.tpl new file mode 100644 index 00000000..523de08a --- /dev/null +++ b/goctl_template_backend/mongo/model_types.tpl @@ -0,0 +1,14 @@ +package gmodel + +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/goctl_template_backend/newapi/newtemplate.tpl b/goctl_template_backend/newapi/newtemplate.tpl new file mode 100644 index 00000000..28be5100 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/rpc/call.tpl b/goctl_template_backend/rpc/call.tpl new file mode 100644 index 00000000..27b48796 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/rpc/config.tpl b/goctl_template_backend/rpc/config.tpl new file mode 100644 index 00000000..c1f85b99 --- /dev/null +++ b/goctl_template_backend/rpc/config.tpl @@ -0,0 +1,7 @@ +package config + +import "github.com/zeromicro/go-zero/zrpc" + +type Config struct { + zrpc.RpcServerConf +} diff --git a/goctl_template_backend/rpc/etc.tpl b/goctl_template_backend/rpc/etc.tpl new file mode 100644 index 00000000..6cd4bddf --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/rpc/logic-func.tpl b/goctl_template_backend/rpc/logic-func.tpl new file mode 100644 index 00000000..e9410d41 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/rpc/logic.tpl b/goctl_template_backend/rpc/logic.tpl new file mode 100644 index 00000000..b8d81f0b --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/rpc/main.tpl b/goctl_template_backend/rpc/main.tpl new file mode 100644 index 00000000..58cb6760 --- /dev/null +++ b/goctl_template_backend/rpc/main.tpl @@ -0,0 +1,36 @@ +package main + +import ( + "flag" + "fmt" + + {{.imports}} + + "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) + } + }) + defer s.Stop() + + fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) + s.Start() +} diff --git a/goctl_template_backend/rpc/server-func.tpl b/goctl_template_backend/rpc/server-func.tpl new file mode 100644 index 00000000..d771b438 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/rpc/server.tpl b/goctl_template_backend/rpc/server.tpl new file mode 100644 index 00000000..84a2f9c1 --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/rpc/svc.tpl b/goctl_template_backend/rpc/svc.tpl new file mode 100644 index 00000000..cf2b47af --- /dev/null +++ b/goctl_template_backend/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/goctl_template_backend/rpc/template.tpl b/goctl_template_backend/rpc/template.tpl new file mode 100644 index 00000000..76daa944 --- /dev/null +++ b/goctl_template_backend/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/model/gmodel/fs_backend_user_gen.go b/model/gmodel/fs_backend_user_gen.go new file mode 100644 index 00000000..80f22aae --- /dev/null +++ b/model/gmodel/fs_backend_user_gen.go @@ -0,0 +1,24 @@ +package gmodel + +import ( + "gorm.io/gorm" +) + +// fs_backend_user 管理员表 +type FsBackendUser struct { + Id int64 `gorm:"primary_key;default:0;auto_increment;" json:"id"` // ID + Username *string `gorm:"unique_key;default:'';" json:"username"` // 用户名 + AuthKey *string `gorm:"default:'';" json:"auth_key"` // jwt token + PasswordHash *string `gorm:"default:'';" json:"password_hash"` // 加密密码 + PasswordResetToken *string `gorm:"default:'';" json:"password_reset_token"` // + Email *string `gorm:"unique_key;default:'';" json:"email"` // 邮箱 + Status *int64 `gorm:"default:1;" json:"status"` // 状态 + Icon *string `gorm:"default:'';" json:"icon"` // + DepartmentId *int64 `gorm:"default:0;" json:"department_id"` // 部门id + Permission *string `gorm:"default:'';" json:"permission"` // 权限设置 + CreatedAt *int64 `gorm:"default:0;" json:"created_at"` // 创建时间 + UpdatedAt *int64 `gorm:"default:0;" json:"updated_at"` // 更新时间 +} +type FsBackendUserModel struct{ db *gorm.DB } + +func NewFsBackendUserModel(db *gorm.DB) *FsBackendUserModel { return &FsBackendUserModel{db} } diff --git a/model/gmodel/fs_backend_user_logic.go b/model/gmodel/fs_backend_user_logic.go new file mode 100644 index 00000000..10d7d90a --- /dev/null +++ b/model/gmodel/fs_backend_user_logic.go @@ -0,0 +1,15 @@ +package gmodel + +import "context" + +// TODO: 使用model的属性做你想做的 + +func (u *FsBackendUserModel) FindUserByEmail(ctx context.Context, emailname string) (resp FsBackendUser, err error) { + err = u.db.WithContext(ctx).Model(&resp).Where("`email` = ?", emailname).Take(&resp).Error + return resp, err +} + +func (u *FsBackendUserModel) FindUserById(ctx context.Context, Id int64) (resp FsBackendUser, err error) { + err = u.db.WithContext(ctx).Model(&resp).Where("`id` = ? and status != ?", Id, 0).Take(&resp).Error + return resp, err +} diff --git a/model/gmodel/var_gen.go b/model/gmodel/var_gen.go index 7860a532..01ef144f 100644 --- a/model/gmodel/var_gen.go +++ b/model/gmodel/var_gen.go @@ -9,6 +9,7 @@ type AllModelsGen struct { FsAuthItem *FsAuthItemModel // fs_auth_item 用户角色和权限信息 FsAuthItemChild *FsAuthItemChildModel // fs_auth_item_child 角色和权限关系表 FsAuthRule *FsAuthRuleModel // fs_auth_rule 规则表 + FsBackendUser *FsBackendUserModel // fs_backend_user 管理员表 FsCanteenProduct *FsCanteenProductModel // fs_canteen_product 餐厅类别产品对应表 FsCanteenType *FsCanteenTypeModel // fs_canteen_type 餐厅类型表 FsCard *FsCardModel // fs_card 卡号表 @@ -16,16 +17,12 @@ type AllModelsGen struct { FsCart *FsCartModel // fs_cart 购物车 FsChangeCode *FsChangeCodeModel // fs_change_code 忘记密码code表 FsCloud *FsCloudModel // fs_cloud 云仓表 - FsCloudDeliverEveryTmp *FsCloudDeliverEveryTmpModel // fs_cloud_deliver_every_tmp - FsCloudDeliverTmp *FsCloudDeliverTmpModel // fs_cloud_deliver_tmp FsCloudPickUp *FsCloudPickUpModel // fs_cloud_pick_up 云仓提货单 FsCloudPickUpDetail *FsCloudPickUpDetailModel // fs_cloud_pick_up_detail 云仓提货单-详情 FsCloudReceive *FsCloudReceiveModel // fs_cloud_receive 云仓接收工厂总单 - FsCloudReceiveEvery *FsCloudReceiveEveryModel // fs_cloud_receive_every FsCloudRenderLog *FsCloudRenderLogModel // fs_cloud_render_log 云渲染日志表 FsCloudUserApplyBack *FsCloudUserApplyBackModel // fs_cloud_user_apply_back 该表废弃 FsContact *FsContactModel // fs_contact 该表暂未使用 - FsContactService *FsContactServiceModel // fs_contact_service FsCoupon *FsCouponModel // fs_coupon 代金券(暂未使用) FsDeliver *FsDeliverModel // fs_deliver 发货表 云仓 直发 通用(已废弃) FsDeliverEvery *FsDeliverEveryModel // fs_deliver_every 发货详细表(已废弃) @@ -36,7 +33,6 @@ type AllModelsGen struct { FsFactoryDeliver *FsFactoryDeliverModel // fs_factory_deliver 工厂发货主表(废弃) FsFactoryDeliverEvery *FsFactoryDeliverEveryModel // fs_factory_deliver_every 该表废弃 FsFactoryProduct *FsFactoryProductModel // fs_factory_product 工厂生产表(废弃) - FsFactoryShipTmp *FsFactoryShipTmpModel // fs_factory_ship_tmp FsFaq *FsFaqModel // fs_faq 常见问题 FsFont *FsFontModel // fs_font 字体配置 FsGerent *FsGerentModel // fs_gerent 管理员表 @@ -54,12 +50,10 @@ type AllModelsGen struct { FsProduct *FsProductModel // fs_product 产品表 FsProductCopy1 *FsProductCopy1Model // fs_product_copy1 产品表 FsProductDesign *FsProductDesignModel // fs_product_design 产品设计表 - FsProductDesignGather *FsProductDesignGatherModel // fs_product_design_gather FsProductModel3d *FsProductModel3dModel // fs_product_model3d 产品模型表 FsProductModel3dLight *FsProductModel3dLightModel // fs_product_model3d_light 模型-灯光组表 FsProductOption *FsProductOptionModel // fs_product_option 产品选项表(已废弃) FsProductPrice *FsProductPriceModel // fs_product_price 阶梯价格表 - FsProductRenderDesign *FsProductRenderDesignModel // fs_product_render_design FsProductScene *FsProductSceneModel // fs_product_scene 产品场景表 FsProductSize *FsProductSizeModel // fs_product_size 产品尺寸表 FsProductTemplate *FsProductTemplateModel // fs_product_template 产品模板表(已废弃) @@ -68,7 +62,6 @@ type AllModelsGen struct { FsProductTemplateTags *FsProductTemplateTagsModel // fs_product_template_tags 模板标签表 FsProductTemplateV2 *FsProductTemplateV2Model // fs_product_template_v2 产品-模型-模板表 FsProductV2Tmp *FsProductV2TmpModel // fs_product_v2_tmp 产品表 - FsQrcode *FsQrcodeModel // fs_qrcode FsQrcodeLog *FsQrcodeLogModel // fs_qrcode_log 二维码扫描日志 FsQrcodeSet *FsQrcodeSetModel // fs_qrcode_set 二维码边框配置表 FsQrcodeUser *FsQrcodeUserModel // fs_qrcode_user 二维码-用户名表 @@ -76,13 +69,11 @@ type AllModelsGen struct { FsQuotationProduct *FsQuotationProductModel // fs_quotation_product 报价单产品表 FsQuotationRemarkTemplate *FsQuotationRemarkTemplateModel // fs_quotation_remark_template 报价单备注模板 FsQuotationSaler *FsQuotationSalerModel // fs_quotation_saler 报价单业务员表 - FsRefundReason *FsRefundReasonModel // fs_refund_reason FsStandardLogo *FsStandardLogoModel // fs_standard_logo 标准logo FsTags *FsTagsModel // fs_tags 产品分类表 FsToolLogs *FsToolLogsModel // fs_tool_logs 3d设计工具日志表 FsToolTemplate *FsToolTemplateModel // fs_tool_template 设计工具模板(废弃) FsToolUser *FsToolUserModel // fs_tool_user 3d设计工具用户表 - FsTrade *FsTradeModel // fs_trade FsUser *FsUserModel // fs_user 用户表 FsUserDesign *FsUserDesignModel // fs_user_design 废弃表 FsUserStock *FsUserStockModel // fs_user_stock 用户云仓库存 @@ -97,6 +88,7 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen { FsAuthItem: NewFsAuthItemModel(gdb), FsAuthItemChild: NewFsAuthItemChildModel(gdb), FsAuthRule: NewFsAuthRuleModel(gdb), + FsBackendUser: NewFsBackendUserModel(gdb), FsCanteenProduct: NewFsCanteenProductModel(gdb), FsCanteenType: NewFsCanteenTypeModel(gdb), FsCard: NewFsCardModel(gdb), @@ -104,16 +96,12 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen { FsCart: NewFsCartModel(gdb), FsChangeCode: NewFsChangeCodeModel(gdb), FsCloud: NewFsCloudModel(gdb), - FsCloudDeliverEveryTmp: NewFsCloudDeliverEveryTmpModel(gdb), - FsCloudDeliverTmp: NewFsCloudDeliverTmpModel(gdb), FsCloudPickUp: NewFsCloudPickUpModel(gdb), FsCloudPickUpDetail: NewFsCloudPickUpDetailModel(gdb), FsCloudReceive: NewFsCloudReceiveModel(gdb), - FsCloudReceiveEvery: NewFsCloudReceiveEveryModel(gdb), FsCloudRenderLog: NewFsCloudRenderLogModel(gdb), FsCloudUserApplyBack: NewFsCloudUserApplyBackModel(gdb), FsContact: NewFsContactModel(gdb), - FsContactService: NewFsContactServiceModel(gdb), FsCoupon: NewFsCouponModel(gdb), FsDeliver: NewFsDeliverModel(gdb), FsDeliverEvery: NewFsDeliverEveryModel(gdb), @@ -124,7 +112,6 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen { FsFactoryDeliver: NewFsFactoryDeliverModel(gdb), FsFactoryDeliverEvery: NewFsFactoryDeliverEveryModel(gdb), FsFactoryProduct: NewFsFactoryProductModel(gdb), - FsFactoryShipTmp: NewFsFactoryShipTmpModel(gdb), FsFaq: NewFsFaqModel(gdb), FsFont: NewFsFontModel(gdb), FsGerent: NewFsGerentModel(gdb), @@ -142,12 +129,10 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen { FsProduct: NewFsProductModel(gdb), FsProductCopy1: NewFsProductCopy1Model(gdb), FsProductDesign: NewFsProductDesignModel(gdb), - FsProductDesignGather: NewFsProductDesignGatherModel(gdb), FsProductModel3d: NewFsProductModel3dModel(gdb), FsProductModel3dLight: NewFsProductModel3dLightModel(gdb), FsProductOption: NewFsProductOptionModel(gdb), FsProductPrice: NewFsProductPriceModel(gdb), - FsProductRenderDesign: NewFsProductRenderDesignModel(gdb), FsProductScene: NewFsProductSceneModel(gdb), FsProductSize: NewFsProductSizeModel(gdb), FsProductTemplate: NewFsProductTemplateModel(gdb), @@ -156,7 +141,6 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen { FsProductTemplateTags: NewFsProductTemplateTagsModel(gdb), FsProductTemplateV2: NewFsProductTemplateV2Model(gdb), FsProductV2Tmp: NewFsProductV2TmpModel(gdb), - FsQrcode: NewFsQrcodeModel(gdb), FsQrcodeLog: NewFsQrcodeLogModel(gdb), FsQrcodeSet: NewFsQrcodeSetModel(gdb), FsQrcodeUser: NewFsQrcodeUserModel(gdb), @@ -164,13 +148,11 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen { FsQuotationProduct: NewFsQuotationProductModel(gdb), FsQuotationRemarkTemplate: NewFsQuotationRemarkTemplateModel(gdb), FsQuotationSaler: NewFsQuotationSalerModel(gdb), - FsRefundReason: NewFsRefundReasonModel(gdb), FsStandardLogo: NewFsStandardLogoModel(gdb), FsTags: NewFsTagsModel(gdb), FsToolLogs: NewFsToolLogsModel(gdb), FsToolTemplate: NewFsToolTemplateModel(gdb), FsToolUser: NewFsToolUserModel(gdb), - FsTrade: NewFsTradeModel(gdb), FsUser: NewFsUserModel(gdb), FsUserDesign: NewFsUserDesignModel(gdb), FsUserStock: NewFsUserStockModel(gdb), diff --git a/server/backend/backend.go b/server/backend/backend.go new file mode 100644 index 00000000..901be84f --- /dev/null +++ b/server/backend/backend.go @@ -0,0 +1,49 @@ +package main + +import ( + "flag" + "fmt" + + "fusenapi/server/backend/internal/config" + "fusenapi/server/backend/internal/handler" + "fusenapi/server/backend/internal/svc" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/rest" +) + +var configFile = flag.String("f", "etc/backend.yaml", "the config file") + +func main() { + flag.Parse() + + var c config.Config + conf.MustLoad(*configFile, &c) + + server := rest.MustNewServer(c.RestConf) + defer server.Stop() + + ctx := svc.NewServiceContext(c) + handler.RegisterHandlers(server, ctx) + + fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) + server.Start() +} + +// var testConfigFile = flag.String("f", "../etc/backend.yaml", "the config file") +// var cnf config.Config + +// func GetTestServer() *rest.Server { +// flag.Parse() + +// conf.MustLoad(*testConfigFile, &cnf) + +// server := rest.MustNewServer(cnf.RestConf) +// defer server.Stop() + +// ctx := svc.NewServiceContext(cnf) +// handler.RegisterHandlers(server, ctx) + +// fmt.Printf("Starting server at %s:%d...\n", cnf.Host, cnf.Port) +// return server +// } diff --git a/server/backend/etc/backend.yaml b/server/backend/etc/backend.yaml new file mode 100644 index 00000000..4eaa9a8d --- /dev/null +++ b/server/backend/etc/backend.yaml @@ -0,0 +1,8 @@ +Name: backend +Host: 0.0.0.0 +Port: 8888 +SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest +Auth: + AccessSecret: fusen_backend_2023 + AccessExpire: 604800 + RefreshAfter: 345600 \ No newline at end of file diff --git a/server/backend/internal/config/config.go b/server/backend/internal/config/config.go new file mode 100644 index 00000000..0e62a34a --- /dev/null +++ b/server/backend/internal/config/config.go @@ -0,0 +1,13 @@ +package config + +import ( + "fusenapi/server/backend/internal/types" + + "github.com/zeromicro/go-zero/rest" +) + +type Config struct { + rest.RestConf + SourceMysql string + Auth types.Auth +} diff --git a/server/backend/internal/handler/backenduserloginhandler.go b/server/backend/internal/handler/backenduserloginhandler.go new file mode 100644 index 00000000..f09d1424 --- /dev/null +++ b/server/backend/internal/handler/backenduserloginhandler.go @@ -0,0 +1,49 @@ +package handler + +import ( + "errors" + "fmt" + "net/http" + + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/rest/httpx" + + "fusenapi/utils/basic" + + "fusenapi/server/backend/internal/logic" + "fusenapi/server/backend/internal/svc" + "fusenapi/server/backend/internal/types" +) + +func BackendUserLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var req types.RequestUserLogin + + // 如果端点有请求结构体,则使用httpx.Parse方法从HTTP请求体中解析请求数据 + if err := httpx.Parse(r, &req); err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 510, + Message: "parameter error", + }) + logx.Info(err) + return + } + // 创建一个业务逻辑层实例 + l := logic.NewBackendUserLoginLogic(r.Context(), svcCtx) + resp, token := l.BackendUserLogin(&req) + if resp.Code == basic.CodeOK.Code { + w.Header().Add("Authorization", fmt.Sprintf("Bearer %s", token)) + } + + // 如果响应不为nil,则使用httpx.OkJsonCtx方法返回JSON响应; + // 否则,发送500内部服务器错误的JSON响应并记录错误消息logx.Error。 + if resp != nil { + httpx.OkJsonCtx(r.Context(), w, resp) + } else { + err := errors.New("server logic is error, resp must not be nil") + httpx.ErrorCtx(r.Context(), w, err) + logx.Error(err) + } + } +} diff --git a/server/backend/internal/handler/quotationdetailhandler.go b/server/backend/internal/handler/quotationdetailhandler.go new file mode 100644 index 00000000..6585222e --- /dev/null +++ b/server/backend/internal/handler/quotationdetailhandler.go @@ -0,0 +1,73 @@ +package handler + +import ( + "errors" + "net/http" + + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/rest/httpx" + + "fusenapi/utils/auth" + "fusenapi/utils/basic" + + "fusenapi/server/backend/internal/logic" + "fusenapi/server/backend/internal/svc" + "fusenapi/server/backend/internal/types" +) + +func QuotationDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var ( + // 定义错误变量 + err error + // 定义用户信息变量 + userinfo *auth.BackendUserInfo + ) + // 解析JWT token,并对空用户进行判断 + claims, err := svcCtx.ParseJwtToken(r) + // 如果解析JWT token出错,则返回未授权的JSON响应并记录错误消息 + if err != nil || claims == nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 401, // 返回401状态码,表示未授权 + Message: "unauthorized", // 返回未授权信息 + }) + logx.Info("unauthorized:", err.Error()) // 记录错误日志 + return + } + + // 从token中获取对应的用户信息 + userinfo, err = auth.GetBackendUserInfoFormMapClaims(claims) + // 如果获取用户信息出错,则返回未授权的JSON响应并记录错误消息 + if err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 401, + Message: "unauthorized", + }) + logx.Info("unauthorized:", err.Error()) + return + } + + var req types.Request + // 如果端点有请求结构体,则使用httpx.Parse方法从HTTP请求体中解析请求数据 + if err := httpx.Parse(r, &req); err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 510, + Message: "parameter error", + }) + logx.Info(err) + return + } + // 创建一个业务逻辑层实例 + l := logic.NewQuotationDetailLogic(r.Context(), svcCtx) + resp := l.QuotationDetail(&req, userinfo) + // 如果响应不为nil,则使用httpx.OkJsonCtx方法返回JSON响应; + if resp != nil { + httpx.OkJsonCtx(r.Context(), w, resp) + } else { + err := errors.New("server logic is error, resp must not be nil") + httpx.ErrorCtx(r.Context(), w, err) + logx.Error(err) + } + } +} diff --git a/server/backend/internal/handler/routes.go b/server/backend/internal/handler/routes.go new file mode 100644 index 00000000..2b9652ff --- /dev/null +++ b/server/backend/internal/handler/routes.go @@ -0,0 +1,27 @@ +// Code generated by goctl. DO NOT EDIT. +package handler + +import ( + "net/http" + + "fusenapi/server/backend/internal/svc" + + "github.com/zeromicro/go-zero/rest" +) + +func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { + server.AddRoutes( + []rest.Route{ + { + Method: http.MethodGet, + Path: "/quotation/detail", + Handler: QuotationDetailHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/backend-user/login", + Handler: BackendUserLoginHandler(serverCtx), + }, + }, + ) +} diff --git a/server/backend/internal/logic/backenduserloginlogic.go b/server/backend/internal/logic/backenduserloginlogic.go new file mode 100644 index 00000000..f67315fe --- /dev/null +++ b/server/backend/internal/logic/backenduserloginlogic.go @@ -0,0 +1,70 @@ +package logic + +import ( + "errors" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "time" + + "context" + + "fusenapi/server/backend/internal/svc" + "fusenapi/server/backend/internal/types" + + "github.com/zeromicro/go-zero/core/logx" + "gorm.io/gorm" +) + +type BackendUserLoginLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewBackendUserLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BackendUserLoginLogic { + return &BackendUserLoginLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *BackendUserLoginLogic) BackendUserLogin(req *types.RequestUserLogin) (resp *basic.Response, jwtToken string) { + // 创建一个 FsUserModel 对象 m 并实例化之,该对象用于操作 MySQL 数据库中的用户数据表。 + m := l.svcCtx.AllModels.FsBackendUser + + // 在用户数据表中根据登录名(email)查找用户记录,并返回 UserModel 类型的结构体对象 userModel。 + user, err := m.FindUserByEmail(l.ctx, req.Name) + if errors.Is(err, gorm.ErrRecordNotFound) { + return resp.SetStatus(basic.CodeEmailNotFoundErr), "" + } + + // 如果在用户数据表中找到了登录名匹配的用户记录,则判断密码是否匹配。 + if *user.PasswordHash != req.Password { + logx.Info("密码错误") + return resp.SetStatus(basic.CodePasswordErr), jwtToken + } + + // 如果密码匹配,则生成 JWT Token。 + nowSec := time.Now().Unix() + jwtToken, err = auth.GenerateBackendJwtToken(&l.svcCtx.Config.Auth.AccessSecret, l.svcCtx.Config.Auth.AccessExpire, nowSec, user.Id, 0) + + // 如果生成 JWT Token 失败,则抛出错误并返回未认证的状态码。 + if err != nil { + logx.Error(err) + return resp.SetStatus(basic.CodeUnAuth), jwtToken + } + + // 如果更新 VerificationToken 字段失败,则返回未认证的状态码。 + if err != nil { + return resp.SetStatus(basic.CodeUnAuth), jwtToken + } + + // 构造 DataUserLogin 类型的数据对象 data 并设置其属性值为生成的 JWT Token。 + data := &types.DataUserLogin{ + Token: jwtToken, + } + + // 返回认证成功的状态码以及数据对象 data 和 JWT Token。 + return resp.SetStatus(basic.CodeOK, data), jwtToken +} diff --git a/server/backend/internal/logic/quotationdetaillogic.go b/server/backend/internal/logic/quotationdetaillogic.go new file mode 100644 index 00000000..8eb3b01b --- /dev/null +++ b/server/backend/internal/logic/quotationdetaillogic.go @@ -0,0 +1,34 @@ +package logic + +import ( + "fusenapi/utils/auth" + "fusenapi/utils/basic" + + "context" + + "fusenapi/server/backend/internal/svc" + "fusenapi/server/backend/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type QuotationDetailLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewQuotationDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QuotationDetailLogic { + return &QuotationDetailLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *QuotationDetailLogic) QuotationDetail(req *types.Request, userinfo *auth.BackendUserInfo) (resp *basic.Response) { + // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) + // userinfo 传入值时, 一定不为null + + return resp.SetStatus(basic.CodeOK) +} diff --git a/server/backend/internal/svc/servicecontext.go b/server/backend/internal/svc/servicecontext.go new file mode 100644 index 00000000..b1041d34 --- /dev/null +++ b/server/backend/internal/svc/servicecontext.go @@ -0,0 +1,57 @@ +package svc + +import ( + "errors" + "fmt" + "fusenapi/server/backend/internal/config" + "net/http" + + "fusenapi/initalize" + "fusenapi/model/gmodel" + + "github.com/golang-jwt/jwt" + "gorm.io/gorm" +) + +type ServiceContext struct { + Config config.Config + + MysqlConn *gorm.DB + AllModels *gmodel.AllModelsGen +} + +func NewServiceContext(c config.Config) *ServiceContext { + + return &ServiceContext{ + Config: c, + MysqlConn: initalize.InitMysql(c.SourceMysql), + AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)), + } +} + +func (svcCtx *ServiceContext) ParseJwtToken(r *http.Request) (jwt.MapClaims, error) { + AuthKey := r.Header.Get("Authorization") + + if len(AuthKey) <= 50 { + return nil, errors.New(fmt.Sprint("Error parsing token, len:", len(AuthKey))) + } + + token, err := jwt.Parse(AuthKey, func(token *jwt.Token) (interface{}, error) { + // 检查签名方法是否为 HS256 + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + // 返回用于验证签名的密钥 + return []byte(svcCtx.Config.Auth.AccessSecret), nil + }) + if err != nil { + return nil, errors.New(fmt.Sprint("Error parsing token:", err)) + } + + // 验证成功返回 + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + return claims, nil + } + + return nil, errors.New(fmt.Sprint("Invalid token", err)) +} diff --git a/server/backend/internal/types/types.go b/server/backend/internal/types/types.go new file mode 100644 index 00000000..e52e288c --- /dev/null +++ b/server/backend/internal/types/types.go @@ -0,0 +1,74 @@ +// Code generated by goctl. DO NOT EDIT. +package types + +import ( + "fusenapi/utils/basic" +) + +type RequestQuotationId struct { + QuotationId int64 `form:"id"` +} + +type RequestUserLogin struct { + Name string `json:"name"` + Password string `json:"pwd"` +} + +type DataUserLogin struct { + Token string `json:"token"` // 登录jwt token +} + +type Request struct { +} + +type Response struct { + Code int `json:"code"` + Message string `json:"msg"` + Data interface{} `json:"data"` +} + +type Auth struct { + AccessSecret string `json:"accessSecret"` + AccessExpire int64 `json:"accessExpire"` + RefreshAfter int64 `json:"refreshAfter"` +} + +// Set 设置Response的Code和Message值 +func (resp *Response) Set(Code int, Message string) *Response { + return &Response{ + Code: Code, + Message: Message, + } +} + +// Set 设置整个Response +func (resp *Response) SetWithData(Code int, Message string, Data interface{}) *Response { + return &Response{ + Code: Code, + Message: Message, + Data: Data, + } +} + +// SetStatus 设置默认StatusResponse(内部自定义) 默认msg, 可以带data, data只使用一个参数 +func (resp *Response) SetStatus(sr *basic.StatusResponse, data ...interface{}) *Response { + newResp := &Response{ + Code: sr.Code, + } + if len(data) == 1 { + newResp.Data = data[0] + } + return newResp +} + +// SetStatusWithMessage 设置默认StatusResponse(内部自定义) 非默认msg, 可以带data, data只使用一个参数 +func (resp *Response) SetStatusWithMessage(sr *basic.StatusResponse, msg string, data ...interface{}) *Response { + newResp := &Response{ + Code: sr.Code, + Message: msg, + } + if len(data) == 1 { + newResp.Data = data[0] + } + return newResp +} diff --git a/server/webset/test/wetset_test.go b/server/webset/test/wetset_test.go index 95cf0bb4..0689f54a 100644 --- a/server/webset/test/wetset_test.go +++ b/server/webset/test/wetset_test.go @@ -89,26 +89,4 @@ func TestWetSetLogic(t *testing.T) { } } - // 检查返回值中的 introduction 字段是否存在 - // intro := result.Get("introduction") - // if !intro.Exists() { - // t.Error("introduction is not exists") - // } - - // // 检查返回值中的 list 数组是否存在 - // list := result.Get("list") - // if !list.Exists() { - // t.Error("list is not exists") - // } - - // // 校验 list 数组中的每个条款 title 和 content 字段是否存在 - // for _, item := range list.Array() { - // title := item.Get("title") - // content := item.Get("content") - // if !title.Exists() || !content.Exists() { - // t.Error("title or content not exists") - // log.Println(result) - // continue - // } - // } } diff --git a/server_api/backend.api b/server_api/backend.api new file mode 100644 index 00000000..06c4f53e --- /dev/null +++ b/server_api/backend.api @@ -0,0 +1,34 @@ +syntax = "v1" + +info ( + title: // TODO: add title + desc: // TODO: add description + author: "" + email: "" +) + +import "basic.api" + +service backend { + // 报价单详情 + @handler QuotationDetailHandler + get /quotation/detail(request) returns (response); + + @handler BackendUserLoginHandler + post /backend-user/login(RequestUserLogin) returns (response); +} + +type RequestQuotationId { + QuotationId int64 `form:"id"` +} + +// BackendUserLoginHandler 用户登录请求结构 +type RequestUserLogin { + Name string `json:"name"` + Password string `json:"pwd"` +} + +// BackendUserLoginHandler 用户登录请求结构 +type DataUserLogin { + Token string `json:"token"` // 登录jwt token +} \ No newline at end of file diff --git a/utils/auth/user.go b/utils/auth/user.go index d778caae..7eba88ae 100644 --- a/utils/auth/user.go +++ b/utils/auth/user.go @@ -3,7 +3,6 @@ package auth import ( "errors" "fmt" - "net/http" "github.com/golang-jwt/jwt" "github.com/zeromicro/go-zero/core/logx" @@ -62,6 +61,11 @@ func (info *UserInfo) IsOnlooker() bool { return info.UserId != 0 && info.GuestId != 0 } +type BackendUserInfo struct { + UserId int64 `json:"user_id"` + DepartmentId int64 `json:"department_id"` +} + // 获取登录信息 func GetUserInfoFormMapClaims(claims jwt.MapClaims) (*UserInfo, error) { userinfo := &UserInfo{} @@ -96,6 +100,27 @@ func GetUserInfoFormMapClaims(claims jwt.MapClaims) (*UserInfo, error) { return userinfo, nil } +// GetBackendUserInfoFormMapClaims 获取后台登录信息 +func GetBackendUserInfoFormMapClaims(claims jwt.MapClaims) (*BackendUserInfo, error) { + userinfo := &BackendUserInfo{} + if userid, ok := claims["user_id"]; ok { + uid, ok := userid.(float64) + if !ok { + err := errors.New(fmt.Sprint("parse uid form context err:", userid)) + logx.Error("parse uid form context err:", err) + return nil, err + } + userinfo.UserId = int64(uid) + } else { + err := errors.New(`userid not in claims`) + logx.Error(`userid not in claims`) + return nil, err + } + + return userinfo, nil +} + +// GenerateJwtToken 网站jwt token生成 func GenerateJwtToken(accessSecret *string, accessExpire, nowSec int64, userid int64, guestid int64) (string, error) { claims := make(jwt.MapClaims) claims["exp"] = nowSec + accessExpire @@ -115,40 +140,23 @@ func GenerateJwtToken(accessSecret *string, accessExpire, nowSec int64, userid i return token.SignedString([]byte(*accessSecret)) } -func ParseJwtToken(w http.ResponseWriter, r *http.Request, AccessSecret *string) (*UserInfo, error) { - // 解析jwtToken - claims, err := getJwtClaimsFromRequest(r, AccessSecret) - // 如果解析出错,则返回未授权的JSON响应并记录错误消息 - if err != nil { - // httpx.OkJsonCtx(r.Context(), w, &basic.Response{ - // Code: 401, - // Message: "unauthorized", - // }) - // logx.Info("unauthorized:", err.Error()) - return nil, err - } +// GenerateBackendJwtToken 后台jwt token生成 +func GenerateBackendJwtToken(accessSecret *string, accessExpire, nowSec int64, userId int64, departmentId int64) (string, error) { + claims := make(jwt.MapClaims) + claims["exp"] = nowSec + accessExpire + claims["iat"] = nowSec - // 从Token里获取对应的信息 - userinfo, err := GetUserInfoFormMapClaims(claims) - // 如果获取用户信息出错,则返回未授权的JSON响应并记录错误消息 - if err != nil { - // httpx.OkJsonCtx(r.Context(), w, &basic.Response{ - // Code: 401, - // Message: "unauthorized", - // }) - // logx.Info("unauthorized:", err.Error()) - return nil, err + if userId == 0 { + err := errors.New("userId cannot be 0 at the same time") + logx.Error(err) + return "", err } - return userinfo, err -} + claims["user_id"] = userId + claims["department_id"] = departmentId -func getJwtClaimsFromRequest(r *http.Request, AccessSecret *string) (jwt.MapClaims, error) { - AuthKey := r.Header.Get("Authorization") - if len(AuthKey) <= 50 { - return nil, errors.New(fmt.Sprint("Error parsing token, len:", len(AuthKey))) - } - - return getJwtClaims(AuthKey, AccessSecret) + token := jwt.New(jwt.SigningMethodHS256) + token.Claims = claims + return token.SignedString([]byte(*accessSecret)) } func getJwtClaims(AuthKey string, AccessSecret *string) (jwt.MapClaims, error) {