commit c69b63abdaa842f30dc2d9e27aabccb9de294545 Author: wangfuduo Date: Mon Sep 21 16:28:08 2026 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66ba629 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ + + +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +cmd/**/*.exe + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +/logs +/src/test + +/src/test +log +data +conf/server.conf.dev +conf/server.conf.rzh + +*.wfd +*.log +readme.md \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..34e7758 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM registry.cn-shanghai.aliyuncs.com/devcon/godev:v1.0.19 + +ADD . /app/src +WORKDIR /app/src +RUN date +"%Y%m%d_%H%M%S"> /etc/build_time +RUN git rev-parse HEAD> /etc/git_commit_id + +RUN mkdir -p /app/bin /app/log /app/base/static /app/data + +RUN export GOROOT=/usr/local/go1.24.5 && \ +export GOPROXY=https://goproxy.cn && \ +export GOPRIVATE=git.u8t.cn && \ +export PATH=$PATH:$GOROOT/bin && \ +go mod tidy && \ +go get git.u8t.cn/open/go-server && \ +go build cmd/yintai-api/yintai-api.go && \ +go build cmd/yintai-worker/yintai-worker.go && \ +mv yintai-api /app/bin && \ +mv yintai-worker /app/bin && \ +cp -r conf /app && \ +cp start.sh /app && \ +chmod +x /app/start.sh + +WORKDIR /app +EXPOSE 9202 + +CMD ["/app/start.sh"] diff --git a/app/admin/controller/category.go b/app/admin/controller/category.go new file mode 100644 index 0000000..0b0ed5f --- /dev/null +++ b/app/admin/controller/category.go @@ -0,0 +1,50 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Category struct{} + +func (r *Category) Create(ctx *gin.Context) { + var req request.CategoryCreate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + + id := new(service.Category).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(id)) +} + +func (r *Category) Update(ctx *gin.Context) { + var req request.CategoryUpdate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Category).Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Category) Delete(ctx *gin.Context) { + var req request.CategoryDelete + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Category).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Category) List(ctx *gin.Context) { + var req request.CategoryList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Category).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} diff --git a/app/admin/controller/config.go b/app/admin/controller/config.go new file mode 100644 index 0000000..039d99c --- /dev/null +++ b/app/admin/controller/config.go @@ -0,0 +1,50 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Config struct{} + +func (r *Config) Create(ctx *gin.Context) { + var req request.ConfigCreate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + + id := new(service.Config).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(id)) +} + +func (r *Config) Update(ctx *gin.Context) { + var req request.ConfigUpdate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Config).Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Config) Delete(ctx *gin.Context) { + var req request.ConfigDelete + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Config).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Config) List(ctx *gin.Context) { + var req request.ConfigList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Config).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} diff --git a/app/admin/controller/document.go b/app/admin/controller/document.go new file mode 100644 index 0000000..fb31021 --- /dev/null +++ b/app/admin/controller/document.go @@ -0,0 +1,57 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Document struct{} + +func (r *Document) Create(ctx *gin.Context) { + var req request.DocumentCreate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + + id := new(service.Document).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(id)) +} + +func (r *Document) Update(ctx *gin.Context) { + var req request.DocumentUpdate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Document).Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Document) Delete(ctx *gin.Context) { + var req request.DocumentDelete + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Document).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Document) List(ctx *gin.Context) { + var req request.DocumentList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Document).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} + +func (r *Document) Get(ctx *gin.Context) { + var req request.DocumentDelete + req.ID = ctx.Param("id") + res := new(service.Document).Get(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} diff --git a/app/admin/controller/file.go b/app/admin/controller/file.go new file mode 100644 index 0000000..9f21bb2 --- /dev/null +++ b/app/admin/controller/file.go @@ -0,0 +1,31 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Files struct{} + +func (f *Files) Upload(ctx *gin.Context) { + file, err := ctx.FormFile("file") + if err != nil { + err2.ParamErr(err) + } + + if file == nil { + err2.ParamErr() + } + + res := new(service.Files).Upload(file, ctx) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} + +func (f *Files) UploadToken(ctx *gin.Context) { + token := new(service.Files).UploadToken() + ctx.JSON(http.StatusOK, response.NewRsp(map[string]any{"token": token})) +} diff --git a/app/admin/controller/job.go b/app/admin/controller/job.go new file mode 100644 index 0000000..a30209f --- /dev/null +++ b/app/admin/controller/job.go @@ -0,0 +1,57 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Job struct{} + +func (r *Job) Create(ctx *gin.Context) { + var req request.JobCreate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + + id := new(service.Job).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(id)) +} + +func (r *Job) Update(ctx *gin.Context) { + var req request.JobUpdate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Job).Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Job) Delete(ctx *gin.Context) { + var req request.JobDelete + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Job).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Job) List(ctx *gin.Context) { + var req request.JobList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Job).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} + +func (r *Job) Get(ctx *gin.Context) { + var req request.JobDelete + req.ID = ctx.Param("id") + res := new(service.Job).Get(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} diff --git a/app/admin/controller/news.go b/app/admin/controller/news.go new file mode 100644 index 0000000..0426268 --- /dev/null +++ b/app/admin/controller/news.go @@ -0,0 +1,57 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type News struct{} + +func (r *News) Create(ctx *gin.Context) { + var req request.NewsCreate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + + id := new(service.News).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(id)) +} + +func (r *News) Update(ctx *gin.Context) { + var req request.NewsUpdate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.News).Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *News) Delete(ctx *gin.Context) { + var req request.NewsDelete + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.News).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *News) List(ctx *gin.Context) { + var req request.NewsList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.News).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} + +func (r *News) Get(ctx *gin.Context) { + var req request.NewsDelete + req.ID = ctx.Param("id") + res := new(service.News).Get(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} diff --git a/app/admin/controller/page.go b/app/admin/controller/page.go new file mode 100644 index 0000000..314835e --- /dev/null +++ b/app/admin/controller/page.go @@ -0,0 +1,50 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Page struct{} + +func (r *Page) Create(ctx *gin.Context) { + var req request.PageCreate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + + id := new(service.Page).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(id)) +} + +func (r *Page) Update(ctx *gin.Context) { + var req request.PageUpdate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Page).Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Page) Delete(ctx *gin.Context) { + var req request.PageDelete + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Page).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Page) List(ctx *gin.Context) { + var req request.PageList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Page).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} diff --git a/app/admin/controller/process.go b/app/admin/controller/process.go new file mode 100644 index 0000000..b354761 --- /dev/null +++ b/app/admin/controller/process.go @@ -0,0 +1,57 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Process struct{} + +func (r *Process) Create(ctx *gin.Context) { + var req request.ProcessCreate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + + id := new(service.Process).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(id)) +} + +func (r *Process) Update(ctx *gin.Context) { + var req request.ProcessUpdate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Process).Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Process) Delete(ctx *gin.Context) { + var req request.ProcessDelete + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Process).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Process) List(ctx *gin.Context) { + var req request.ProcessList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Process).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} + +func (r *Process) Get(ctx *gin.Context) { + var req request.ProcessDelete + req.ID = ctx.Param("id") + res := new(service.Process).Get(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} diff --git a/app/admin/controller/resource.go b/app/admin/controller/resource.go new file mode 100644 index 0000000..55f2f76 --- /dev/null +++ b/app/admin/controller/resource.go @@ -0,0 +1,50 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Resource struct{} + +func (r *Resource) Create(ctx *gin.Context) { + var req request.ResourceCreate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + + id := new(service.Resource).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(id)) +} + +func (r *Resource) Update(ctx *gin.Context) { + var req request.ResourceUpdate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Resource).Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Resource) Delete(ctx *gin.Context) { + var req request.ResourceDelete + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Resource).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Resource) List(ctx *gin.Context) { + var req request.ResourceList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Resource).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} diff --git a/app/admin/controller/role.go b/app/admin/controller/role.go new file mode 100644 index 0000000..6ec46f9 --- /dev/null +++ b/app/admin/controller/role.go @@ -0,0 +1,64 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Role struct{} + +func (r *Role) Create(ctx *gin.Context) { + var req request.RoleCreate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + + id := new(service.Role).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(id)) +} + +func (r *Role) Update(ctx *gin.Context) { + var req request.RoleUpdate + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Role).Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Role) Delete(ctx *gin.Context) { + var req request.RoleDelete + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + new(service.Role).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (r *Role) List(ctx *gin.Context) { + var req request.RoleList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Role).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} + +func (r *Role) Get(ctx *gin.Context) { + var req request.RoleGet + req.ID = ctx.Param("id") + res := new(service.Role).Get(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} + +func (r *Role) Accounts(ctx *gin.Context) { + var req request.RoleGet + req.ID = ctx.Param("id") + res := new(service.Role).Accounts(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} diff --git a/app/admin/controller/translate.go b/app/admin/controller/translate.go new file mode 100644 index 0000000..576e38c --- /dev/null +++ b/app/admin/controller/translate.go @@ -0,0 +1,22 @@ +package controller + +import ( + "net/http" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + "yintai-api/app/common/service" + + "github.com/gin-gonic/gin" +) + +type Translate struct{} + +func (Translate) Translate2En(ctx *gin.Context) { + text := ctx.Query("text") + en, err := service.NewBaiduTranslate().ZH2EN(text) + if err != nil { + err2.TranslateErr() + } + + ctx.JSON(http.StatusOK, response.NewRsp(en)) +} diff --git a/app/admin/controller/user.go b/app/admin/controller/user.go new file mode 100644 index 0000000..f810058 --- /dev/null +++ b/app/admin/controller/user.go @@ -0,0 +1,90 @@ +package controller + +import ( + "net/http" + "yintai-api/app/admin/request" + "yintai-api/app/admin/service" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + "yintai-api/base/config" + + "github.com/gin-gonic/gin" + "github.com/spf13/cast" +) + +type User struct{} + +func (a *User) Create(ctx *gin.Context) { + var req request.UserCreate + if err := ctx.ShouldBind(&req); err != nil { + err2.ParamErr("", err) + } + + id := new(service.User).Create(req) + ctx.JSON(http.StatusOK, response.NewRsp(map[string]any{"id": id})) +} + +func (a *User) Update(ctx *gin.Context) { + var req request.UserUpdate + var err error + if err = ctx.ShouldBind(&req); err != nil { + err2.ParamErr("", err) + } + // 获取当前登录用户的账号 + userinfo := service.NewToken().UserInfo(ctx.GetHeader("token")) + if userId := cast.ToInt64(req.UserId); userId != 0 { + userinfo, err = dao.NewUser().GetUserId(userId) + if userinfo == nil { + panic(config.ErrNotExists.New().Append()) + } + } + + userService := service.NewUser(userinfo) + userService.Update(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (a *User) Delete(ctx *gin.Context) { + var req request.UserDelete + if err := ctx.ShouldBind(&req); err != nil { + err2.ParamErr("", err) + } + + userinfo := service.NewToken().UserInfo(ctx.GetHeader("token")) + if userinfo.ID == cast.ToInt64(req.ID) { + err2.PriErr("you can't delete yourself") + } + + new(service.User).Delete(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} + +func (a *User) List(ctx *gin.Context) { + var req request.UserList + if err := ctx.ShouldBind(&req); err != nil { + err2.ParamErr("", err) + } + res, count := new(service.User).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} + +func (a *User) Login(ctx *gin.Context) { + var req request.UserLogin + if err := ctx.ShouldBind(&req); err != nil { + err2.ParamErr("", err) + } + res := new(service.User).Login(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) + return +} + +func (a *User) Logout(ctx *gin.Context) { + var req request.UserLogout + if err := ctx.ShouldBindHeader(&req); err != nil { + err2.ParamErr("", err) + } + + new(service.User).Logout(req) + ctx.JSON(http.StatusOK, response.NewRspOk()) +} diff --git a/app/admin/middleware/login.go b/app/admin/middleware/login.go new file mode 100644 index 0000000..d18f370 --- /dev/null +++ b/app/admin/middleware/login.go @@ -0,0 +1,89 @@ +package middleware + +import ( + "encoding/json" + "strings" + "yintai-api/app/common/cache" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/app/common/utils" + "yintai-api/base/global" + + "github.com/gin-gonic/gin" +) + +func Login(c *gin.Context) { + token := c.Request.Header.Get("token") + token = strings.TrimSpace(token) + if token == "" { + err2.TokenErr(" token not exists") + c.Abort() + return + } + + key := cache.NewKeys().User() + token + if res := global.GetRedis().Get(key); res.Val() == "" { + err2.TokenErr(" token not exists", res.Err()) + c.Abort() + return + } + + c.Next() +} + +// Permission user router permission check +func Permission() gin.HandlerFunc { + return func(c *gin.Context) { + token := c.Request.Header.Get("token") + token = strings.TrimSpace(token) + if token == "" { + err2.TokenErr(" token not exists") + c.Abort() + return + } + key := cache.NewKeys().User() + token + val := global.GetRedis().Get(key).Val() + if val == "" { + err2.TokenErr(" token not exists") + c.Abort() + return + } + + var user *model.UserWithRole + err := json.Unmarshal([]byte(val), &user) + if err != nil { + err2.TokenErr(" token not exists") + } + + key = cache.NewKeys().Api(user.RoleID) + member := utils.SourceFormat(c.Request.Method, c.FullPath()) + // skip no auth + if noAuth(member) { + c.Next() + } + + if val := global.GetRedis().SIsMember(key, member); !val.Val() { + err2.PriErr(" no permission", val.Err()) + c.Abort() + return + } + c.Next() + } +} + +// 不需要鉴权的接口 +func noAuth(source string) bool { + type no struct { + Method string + Url string + } + x := make([]no, 0) + x = append(x, no{"POST", "/yt/user/login"}) + for _, v := range x { + if source == utils.SourceFormat(v.Method, v.Url) { + return true + } + } + + return false +} diff --git a/app/admin/request/category.go b/app/admin/request/category.go new file mode 100644 index 0000000..44172a8 --- /dev/null +++ b/app/admin/request/category.go @@ -0,0 +1,27 @@ +package request + +import "yintai-api/app/common/request" + +type CategoryCreate struct { + Name string `json:"name"` + NameEn string `json:"name_en"` + Pid string `json:"pid"` + Type string `json:"type"` +} + +type CategoryUpdate struct { + ID string `json:"id"` + CategoryCreate +} + +type CategoryDelete struct { + ID string `form:"id"` +} + +type CategoryList struct { + request.Paginate + Pid string `form:"pid"` + Name string `form:"name"` + NameEn string `form:"name_en"` + Type string `form:"type"` +} diff --git a/app/admin/request/config.go b/app/admin/request/config.go new file mode 100644 index 0000000..6293138 --- /dev/null +++ b/app/admin/request/config.go @@ -0,0 +1,19 @@ +package request + +import "yintai-api/app/common/request" + +type ConfigCreate struct { +} + +type ConfigUpdate struct { + ID string `json:"id"` + ConfigCreate +} + +type ConfigDelete struct { + ID string `form:"id"` +} + +type ConfigList struct { + request.Paginate +} diff --git a/app/admin/request/document.go b/app/admin/request/document.go new file mode 100644 index 0000000..1709be9 --- /dev/null +++ b/app/admin/request/document.go @@ -0,0 +1,38 @@ +package request + +import "yintai-api/app/common/request" + +type DocTrans struct { + Path string `json:"path"` + FileName string `json:"file_name"` + Title string `json:"title"` + SubTitle string `json:"sub_title"` + Cover string `json:"cover"` + Desc string `json:"desc"` + Extra string `json:"extra"` +} + +type DocumentCreate struct { + Trans map[string]DocTrans `json:"translations"` + CategoryID string `json:"category_id"` + Weight string `json:"weight"` +} + +type DocumentUpdate struct { + ID string `json:"id"` + DocumentCreate +} + +type DocumentDelete struct { + ID string `form:"id"` +} + +type DocumentList struct { + request.Paginate + Lang string `form:"lang"` + Category string `form:"category_id"` + FileName string `form:"file_name"` + Title string `form:"title"` + SubTitle string `form:"sub_title"` + Desc string `form:"desc"` +} diff --git a/app/admin/request/job.go b/app/admin/request/job.go new file mode 100644 index 0000000..16e5e6b --- /dev/null +++ b/app/admin/request/job.go @@ -0,0 +1,40 @@ +package request + +import "yintai-api/app/common/request" + +type JobTrans struct { + Name string `json:"name"` + Desc string `json:"description"` + Requirement string `json:"requirement"` + ContractInfo string `json:"contract_info"` +} + +type JobCreate struct { + JobType string `json:"job_type"` + JobArea string `json:"job_area"` + JobUnit string `json:"job_unit"` + RecruitCount string `json:"recruit_count"` + City string `json:"city"` + Translations map[string]JobTrans `json:"translations"` +} + +type JobUpdate struct { + ID string `json:"id"` + JobCreate +} + +type JobDelete struct { + ID string `form:"id"` +} + +type JobList struct { + request.Paginate + Lang string `form:"lang" binding:"required"` + City string `form:"city"` + Title string `form:"title"` + JobType string `form:"job_type"` + JobArea string `form:"job_area"` + JobUnit string `form:"job_unit"` + Desc string `form:"desc"` + Reqs string `form:"reqs"` +} diff --git a/app/admin/request/news.go b/app/admin/request/news.go new file mode 100644 index 0000000..9ec370d --- /dev/null +++ b/app/admin/request/news.go @@ -0,0 +1,37 @@ +package request + +import "yintai-api/app/common/request" + +type NewsTranslation struct { + Title string `json:"title"` + Content string `json:"content"` + PublishTime string `json:"publish_time"` +} + +type NewsCreate struct { + CategoryID string `json:"category_id"` + CoverShow string `json:"cover_show"` + Weight string `json:"weight"` + Pinned string `json:"pinned"` + + CoverResource map[string][]string `json:"cover_resource"` + Trans map[string]NewsTranslation `json:"translations"` +} + +type NewsUpdate struct { + ID string `json:"id"` + NewsCreate +} + +type NewsDelete struct { + ID string `form:"id"` +} + +type NewsList struct { + request.Paginate + Lang string `form:"lang" binding:"required"` + Title string `form:"title"` + Content string `form:"content"` + PublishTime string `form:"publish_time"` + CategoryID string `form:"category_id"` +} diff --git a/app/admin/request/page.go b/app/admin/request/page.go new file mode 100644 index 0000000..bbaa44d --- /dev/null +++ b/app/admin/request/page.go @@ -0,0 +1,28 @@ +package request + +import ( + "yintai-api/app/common/request" +) + +type PageCreate struct { + Pid string `json:"pid"` + Name string `json:"name"` + Page string `json:"page"` + Extra string `json:"content"` + Weight string `json:"weight"` + Tags string `json:"tags"` +} + +type PageUpdate struct { + ID string `json:"id"` + PageCreate +} + +type PageDelete struct { + ID string `form:"id"` +} + +type PageList struct { + request.Paginate + Name string `form:"name"` +} diff --git a/app/admin/request/process.go b/app/admin/request/process.go new file mode 100644 index 0000000..2d20201 --- /dev/null +++ b/app/admin/request/process.go @@ -0,0 +1,33 @@ +package request + +import "yintai-api/app/common/request" + +type ProcessTranslation struct { + Title string `json:"title"` + Images []string `json:"images"` + Content string `json:"content"` + ProcessTime string `json:"process_time"` +} + +type ProcessCreate struct { + CategoryID string `json:"category_id"` + Translations map[string]ProcessTranslation `json:"translations"` +} + +type ProcessUpdate struct { + ID string `json:"id"` + ProcessCreate +} + +type ProcessDelete struct { + ID string `form:"id"` +} + +type ProcessList struct { + request.Paginate + Lang string `form:"lang" binding:"required"` + Title string `form:"title"` + Content string `form:"content"` + ProcessTime string `form:"process_time"` + CategoryID string `form:"category_id"` +} diff --git a/app/admin/request/resource.go b/app/admin/request/resource.go new file mode 100644 index 0000000..75d7489 --- /dev/null +++ b/app/admin/request/resource.go @@ -0,0 +1,31 @@ +package request + +import "yintai-api/app/common/request" + +type ResourceCreate struct { + Name string `json:"name"` + Type string `json:"type"` + Code string `json:"code"` + Path string `json:"path"` + Method string `json:"method"` + Pid string `json:"pid"` +} + +type ResourceUpdate struct { + ID string `json:"id"` + ResourceCreate +} + +type ResourceDelete struct { + ID string `form:"id"` +} + +type ResourceList struct { + request.Paginate + Name string `form:"name"` + Type string `form:"type"` + Code string `form:"code"` + Path string `form:"path"` + Method string `form:"method"` + Pid string `form:"pid"` +} diff --git a/app/admin/request/role.go b/app/admin/request/role.go new file mode 100644 index 0000000..69562aa --- /dev/null +++ b/app/admin/request/role.go @@ -0,0 +1,29 @@ +package request + +import "yintai-api/app/common/request" + +type RoleCreate struct { + Name string `json:"name"` + Desc string `json:"desc"` + Resource []string `json:"resource"` +} + +type RoleUpdate struct { + ID string `json:"id"` + RoleCreate +} + +type RoleDelete struct { + ID string `form:"id"` + DelAccount string `form:"del_account"` +} + +type RoleList struct { + request.Paginate + Name string `form:"name"` + Desc string `form:"desc"` +} + +type RoleGet struct { + ID string `form:"id"` +} diff --git a/app/admin/request/user.go b/app/admin/request/user.go new file mode 100644 index 0000000..b66ceec --- /dev/null +++ b/app/admin/request/user.go @@ -0,0 +1,38 @@ +package request + +import "yintai-api/app/common/request" + +type UserCreate struct { + Username string `json:"username"` + Account string `json:"account"` + Password string `json:"password"` + Role string `json:"role"` +} + +type UserUpdate struct { + UserId string `json:"id"` + Username string `json:"username"` + Password string `json:"password"` + Role string `json:"role"` + Status string `json:"status"` +} + +type UserDelete struct { + ID string `form:"id"` +} + +type UserList struct { + request.Paginate + Username string `form:"username"` + Account string `form:"account"` + Role string `from:"role"` +} + +type UserLogin struct { + Account string `json:"account"` + Password string `json:"password"` +} + +type UserLogout struct { + Token string `header:"token"` +} diff --git a/app/admin/service/base.go b/app/admin/service/base.go new file mode 100644 index 0000000..6524aba --- /dev/null +++ b/app/admin/service/base.go @@ -0,0 +1,75 @@ +package service + +import ( + "fmt" + "time" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/app/common/service" + + "gorm.io/gorm" +) + +func Images(s []string) []string { + imgs := make([]string, 0) + for _, img := range s { + temp := service.NewQiniu().ObjectKey(img) + if temp != "" { + imgs = append(imgs, temp) + } + } + + return imgs +} + +func BizCateCreate(tx *gorm.DB, BizID, cateID int64, cateType string) error { + cate, err := dao.NewCategory(tx).GetWithType(cateID, cateType) + if err != nil { + return err + } + + if cate == nil { + err2.NotEsxErr(fmt.Sprintf("category not exist: type [%s] id [%d]", cateType, cateID)) + } + + m := &model.CategoryRelation{ + CategoryId: cateID, + Type: cateType, + BizId: BizID, + CreateTime: time.Now().Unix(), + } + + if err = dao.NewCategoryRelation(tx).Create(m); err != nil { + return err + } + + return nil +} + +func BizCateUpdate(tx *gorm.DB, BizID int64, cateID int64, cateType string) error { + cate, err := dao.NewCategory(tx).GetWithType(cateID, cateType) + if err != nil { + return err + } + + if cate == nil { + err2.NotEsxErr(fmt.Sprintf("category not exist: type [%s] id [%d]", cateType, cateID)) + } + + if err = dao.NewCategoryRelation(tx).DeleteByCondition(BizID, cateType); err != nil { + return err + } + + m := &model.CategoryRelation{ + CategoryId: cateID, + Type: cateType, + BizId: BizID, + CreateTime: time.Now().Unix(), + } + if err = dao.NewCategoryRelation(tx).Create(m); err != nil { + return err + } + + return nil +} diff --git a/app/admin/service/category.go b/app/admin/service/category.go new file mode 100644 index 0000000..0f98096 --- /dev/null +++ b/app/admin/service/category.go @@ -0,0 +1,199 @@ +package service + +import ( + "fmt" + "slices" + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/base/global" + + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type Category struct{} + +func (r *Category) Create(req request.CategoryCreate) int64 { + if cast.ToInt64(req.Pid) != 0 { + ok, err := dao.NewCategory().Exists(cast.ToInt64(req.Pid)) + if err != nil { + err2.DbErr(err) + } + + if !ok { + err2.NotEsxErr() + } + } + + if !slices.Contains(model.CategoryTypes, req.Type) { + err2.ParamErr() + } + + m := new(model.Category) + m.Pid = cast.ToInt64(req.Pid) + m.Name = req.Name + m.NameEn = req.NameEn + m.Type = req.Type + m.CreateTime = time.Now().Unix() + err := global.GetDB().Transaction(func(tx *gorm.DB) error { + if err := dao.NewCategory(tx).Create(m); err != nil { + return fmt.Errorf(": create category error: " + err.Error()) + } + + path := fmt.Sprintf("%d/", m.ID) + if m.Pid != 0 { + parent, err := dao.NewCategory(tx).Get(m.Pid) + if err != nil { + return fmt.Errorf(": create category error: " + err.Error()) + } + + path = fmt.Sprintf("%s%d/", parent.Path, m.ID) + } + + m.Path = path + if err := dao.NewCategory(tx).Update(m); err != nil { + return fmt.Errorf(": update category path error: " + err.Error()) + } + + return nil + }) + + if err != nil { + err2.DbErr(err.Error()) + } + + return m.ID +} + +func (r *Category) Update(req request.CategoryUpdate) { + m, err := dao.NewCategory().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + if req.Name != "" { + m.Name = req.Name + } + if req.NameEn != "" { + m.NameEn = req.NameEn + } + if req.Type != "" { + if !slices.Contains(model.CategoryTypes, req.Type) { + err2.ParamErr() + } + m.Type = req.Type + } + + oldPath := m.Path + oldPid := m.Pid + m.Pid = cast.ToInt64(req.Pid) + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + path := fmt.Sprintf("%d/", m.ID) + if m.Pid != 0 { + if parentCate, err := dao.NewCategory(tx).Get(m.Pid); err != nil { + return fmt.Errorf(": get category error: " + err.Error()) + } else { + path = fmt.Sprintf("%s%d/", parentCate.Path, m.ID) + } + } + + m.Path = path + m.UpdateTime = time.Now().Unix() + if err = dao.NewCategory(tx).Update(m); err != nil { + return fmt.Errorf(": update category path error: " + err.Error()) + } + + if oldPid == m.Pid { + return nil + } + + // 更新子级别 + subCate, err := dao.NewCategory(tx).GetByPath(oldPath) + if err != nil { + return fmt.Errorf(": get sub category data error: " + err.Error()) + } + + for _, category := range subCate { + parentCate, _ := dao.NewCategory(tx).Get(category.Pid) + category.Path = fmt.Sprintf("%s%d/", parentCate.Path, category.ID) + category.UpdateTime = time.Now().Unix() + if err = dao.NewCategory(tx).Update(category); err != nil { + return fmt.Errorf(": update sub category path error: " + err.Error()) + } + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return +} + +func (r *Category) Delete(req request.CategoryDelete) { + m, err := dao.NewCategory().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + // 查询这个分类下的所有数据 + ms, err := dao.NewCategory().GetByPath(m.Path) + if err != nil { + err2.DbErr(err) + } + + if ms == nil { + err2.NotEsxErr() + } + ids := make([]int64, 0) + for _, category := range ms { + ids = append(ids, cast.ToInt64(category.ID)) + } + + relation, err := dao.NewCategoryRelation().GetByIds(ids...) + if err != nil { + err2.DbErr(err) + } + + if relation != nil && len(relation) > 0 { + err2.InUseErr() + } + + if err = dao.NewCategory().Delete(m.Path); err != nil { + err2.DbErr(err) + } + return +} + +func (r *Category) List(req request.CategoryList) (any, int64) { + if !slices.Contains(model.CategoryTypes, req.Type) { + err2.ParamErr() + } + + req.Pagination() + lists, count, err := dao.NewCategory().List(cast.ToInt64(req.Pid), req.Name, req.NameEn, req.Type, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} diff --git a/app/admin/service/config.go b/app/admin/service/config.go new file mode 100644 index 0000000..63cebf0 --- /dev/null +++ b/app/admin/service/config.go @@ -0,0 +1,74 @@ +package service + +import ( + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + + "github.com/spf13/cast" +) + +type Config struct{} + +func (r *Config) Create(req request.ConfigCreate) int64 { + m := new(model.Config) + m.CreateTime = time.Now().Unix() + if err := dao.NewConfig().Create(m); err != nil { + err2.DbErr(err) + } + + return m.ID +} + +func (r *Config) Update(req request.ConfigUpdate) { + m, err := dao.NewConfig().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + m.UpdateTime = time.Now().Unix() + if err = dao.NewConfig().Update(m); err != nil { + err2.DbErr(err) + } + + return +} + +func (r *Config) Delete(req request.ConfigDelete) { + m, err := dao.NewConfig().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + if err = dao.NewConfig().Delete(m.ID); err != nil { + err2.DbErr(err) + } + return +} + +func (r *Config) List(req request.ConfigList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewConfig().List(req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} diff --git a/app/admin/service/document.go b/app/admin/service/document.go new file mode 100644 index 0000000..7370279 --- /dev/null +++ b/app/admin/service/document.go @@ -0,0 +1,207 @@ +package service + +import ( + "slices" + "strings" + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/consts" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/app/common/service" + "yintai-api/base/global" + + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type Document struct{} + +func (r *Document) Create(req request.DocumentCreate) int64 { + m := new(model.Document) + m.Weight = cast.ToInt64(req.Weight) + m.CreateTime = time.Now().Unix() + + trans := make([]*model.DocumentTranslations, 0) + for lang, tran := range req.Trans { + lang = strings.ToUpper(lang) + if !slices.Contains(consts.LangSupport, lang) { + continue + } + + trans = append(trans, &model.DocumentTranslations{ + DocumentID: 0, + Lang: lang, + Name: tran.FileName, + Title: tran.Title, + SubTitle: tran.SubTitle, + ImageTitle: "", + Description: tran.Desc, + StorageType: "qiniu", + StoragePath: service.NewQiniu().ObjectKey(tran.Path), + Cover: service.NewQiniu().ObjectKey(tran.Cover), + Extra: tran.Extra, + }) + + } + + err := global.GetDB().Transaction(func(tx *gorm.DB) error { + if err := dao.NewDocument(tx).Create(m); err != nil { + return err + } + + for _, tran := range trans { + tran.DocumentID = m.ID + if err := dao.NewDocumentTranslations(tx).Create(tran); err != nil { + return err + } + } + + if err := BizCateCreate(tx, m.ID, cast.ToInt64(req.CategoryID), model.CateTypeFile); err != nil { + return err + } + + return nil + }) + if err != nil { + err2.DbErr(err) + } + + return m.ID +} + +func (r *Document) Update(req request.DocumentUpdate) { + m, err := dao.NewDocument().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + if weight := cast.ToInt64(req.Weight); weight != m.Weight { + m.Weight = weight + } + + m.UpdateTime = time.Now().Unix() + trans := make([]*model.DocumentTranslations, 0) + for lang, tran := range req.Trans { + lang = strings.ToUpper(lang) + if !slices.Contains(consts.LangSupport, lang) { + continue + } + + t, err := dao.NewDocumentTranslations().GetByMaster(m.ID, lang) + if err != nil { + err2.DbErr(err) + } + if t == nil { + t = new(model.DocumentTranslations) + } + if tran.Path != "" { + t.StoragePath = service.NewQiniu().ObjectKey(tran.Path) + } + if tran.FileName != "" { + t.Name = tran.FileName + } + if tran.SubTitle != "" { + t.SubTitle = tran.SubTitle + } + if tran.Cover != "" { + t.Cover = service.NewQiniu().ObjectKey(tran.Cover) + } + if tran.Desc != "" { + t.Description = tran.Desc + } + if tran.Extra != "" { + t.Extra = tran.Extra + } + + trans = append(trans, t) + } + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewDocument(tx).Update(m); err != nil { + return err + } + + for _, t := range trans { + t.DocumentID = m.ID + if err = dao.NewDocumentTranslations(tx).Update(t); err != nil { + return err + } + } + + if v := cast.ToInt64(req.CategoryID); v != 0 { + if err = BizCateUpdate(tx, m.ID, v, model.CateTypeFile); err != nil { + return err + } + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } +} + +func (r *Document) Delete(req request.DocumentDelete) { + m, err := dao.NewDocument().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewDocument(tx).Delete(m.ID); err != nil { + return err + } + + if err = dao.NewDocumentTranslations(tx).Delete(m.ID); err != nil { + return err + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } +} + +func (r *Document) List(req request.DocumentList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewDocument().List(req.Lang, cast.ToInt64(req.Category), req.FileName, req.Title, req.SubTitle, req.Desc, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} + +func (r *Document) Get(req request.DocumentDelete) any { + res := make(map[string]any) + for _, lang := range consts.LangSupport { + x, err := dao.NewDocument().GetLang(lang, cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + res[lang] = x.Format() + } + + return res +} diff --git a/app/admin/service/files.go b/app/admin/service/files.go new file mode 100644 index 0000000..2464895 --- /dev/null +++ b/app/admin/service/files.go @@ -0,0 +1,102 @@ +package service + +import ( + "fmt" + "mime/multipart" + "os" + "path" + "runtime" + "slices" + "strings" + "time" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/service" + + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" + "github.com/smbrave/goutil" +) + +type Files struct{} + +// 直接上传到七牛云上。返回保存的地址,访问的url每次都用新的 + +func (f Files) UploadToken() string { + return service.NewQiniu().UploadToken() +} + +func (f Files) Upload(file *multipart.FileHeader, ctx *gin.Context) any { + start := time.Now() + defer log.Debug(" [all] time cost:", time.Since(start)) + if file == nil { + err2.ParamErr("file is nil") + return nil + } + + // support check + if err := f.Support(path.Ext(file.Filename), file.Size); err != nil { + err2.ParamErr(err) + } + + // 保存本地临时文件 + dst := f.Dst(file.Filename) + if runtime.GOOS == "windows" { + dst = dst[1:] + } + + err := ctx.SaveUploadedFile(file, dst) + if err != nil { + err2.InnerErr(err) + } + + objectKey := service.NewQiniu().Save(dst, f.ObjectKey(file.Filename)) + + defer func() { + _ = os.Remove(dst) + }() + + return service.NewQiniu().ForeverUrl(objectKey) +} + +func (f Files) ObjectKey(ext string) string { + return fmt.Sprintf("yintai/%s", f.Name(ext)) +} + +func (f Files) Dst(ext string) string { + return "/tmp/" + f.Name(ext) +} + +func (f Files) Name(ext string) string { + now := time.Now().Format("20060102150405") + return fmt.Sprintf("%s_%s%s", now, goutil.RandomStr(4), ext) + +} + +func (f Files) Support(ext string, size int64) error { + video := []string{"mp4", "m3u8"} + audio := []string{"mp3"} + img := []string{"png", "jpg", "jpeg", "jpg", "svg", "gif", "webp"} + doc := []string{"ppt", "xls", "xlsx", "pptx", "pdf"} + + type T struct { + support []string + size int64 + } + + x := make([]T, 0) + x = append(x, T{support: video, size: 30 << 20}) + x = append(x, T{support: audio, size: 30 << 20}) + x = append(x, T{support: img, size: 10 << 20}) + x = append(x, T{support: doc, size: 120 << 20}) + + for _, t := range x { + ext = strings.TrimLeft(ext, ".") + if slices.Contains(t.support, ext) { + if size > t.size { + return fmt.Errorf("file too large") + } + } + } + + return nil +} diff --git a/app/admin/service/job.go b/app/admin/service/job.go new file mode 100644 index 0000000..6d462c0 --- /dev/null +++ b/app/admin/service/job.go @@ -0,0 +1,287 @@ +package service + +import ( + "slices" + "strings" + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/consts" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/base/global" + + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type Job struct{} + +func (r *Job) Create(req request.JobCreate) int64 { + m := new(model.Job) + m.RecruitCount = cast.ToInt64(req.RecruitCount) + m.City = req.City + m.JobType = cast.ToInt64(req.JobType) + m.JobArea = cast.ToInt64(req.JobArea) + m.JobUnit = cast.ToInt64(req.JobUnit) + m.CreateTime = time.Now().Unix() + + ms := make([]*model.JobTranslations, 0) + for lang, trans := range req.Translations { + lang = strings.ToUpper(lang) + if !slices.Contains(consts.LangSupport, lang) { + continue + } + + temp := &model.JobTranslations{ + Lang: lang, + Name: trans.Name, + Description: trans.Desc, + Requirement: trans.Requirement, + ContractInfo: trans.ContractInfo, + } + ms = append(ms, temp) + } + + err := global.GetDB().Transaction(func(tx *gorm.DB) error { + if err := dao.NewJob(tx).Create(m); err != nil { + return err + } + + for _, trans := range ms { + trans.JobID = m.ID + if err := dao.NewJobTranslations(tx).Create(trans); err != nil { + return err + } + } + + if err := BizCateCreate(tx, m.ID, cast.ToInt64(req.JobType), model.CateTypeJobType); err != nil { + return err + } + + if err := BizCateCreate(tx, m.ID, cast.ToInt64(req.JobArea), model.CateTypeJobArea); err != nil { + return err + } + + if err := BizCateCreate(tx, m.ID, cast.ToInt64(req.JobUnit), model.CateTypeJobUnit); err != nil { + return err + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return m.ID +} + +func (r *Job) Update(req request.JobUpdate) { + m, err := dao.NewJob().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + if cast.ToInt64(req.RecruitCount) != 0 { + m.RecruitCount = cast.ToInt64(req.RecruitCount) + } + + if req.City != "" { + m.City = req.City + } + m.UpdateTime = time.Now().Unix() + + ms := make([]*model.JobTranslations, 0) + for lang, trans := range req.Translations { + lang = strings.ToUpper(lang) + if !slices.Contains(consts.LangSupport, lang) { + continue + } + + t := new(model.JobTranslations) + if t, err = dao.NewJobTranslations().GetByMaster(m.ID, lang); err != nil { + err2.DbErr(err) + } + + if trans.Name != "" { + t.Name = trans.Name + } + + if trans.Desc != "" { + t.Description = trans.Desc + } + + if trans.Requirement != "" { + t.Requirement = trans.Requirement + } + + if trans.ContractInfo != "" { + t.ContractInfo = trans.ContractInfo + } + + ms = append(ms, t) + } + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + for _, trans := range ms { + if err = dao.NewJobTranslations(tx).Update(trans); err != nil { + return err + } + } + + if v := cast.ToInt64(req.JobType); v > 0 { + m.JobType = v + if err = BizCateUpdate(tx, m.ID, v, model.CateTypeJobType); err != nil { + return err + } + } + + if v := cast.ToInt64(req.JobArea); v > 0 { + m.JobArea = v + if err = BizCateUpdate(tx, m.ID, v, model.CateTypeJobArea); err != nil { + return err + } + } + + if v := cast.ToInt64(req.JobUnit); v > 0 { + m.JobUnit = v + if err = BizCateUpdate(tx, m.ID, v, model.CateTypeJobUnit); err != nil { + return err + } + } + + if err = dao.NewJob(tx).Update(m); err != nil { + return err + } + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return +} + +func (r *Job) Delete(req request.JobDelete) { + m, err := dao.NewJob().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewJob().Delete(m.ID); err != nil { + return err + } + + if err = dao.NewJobTranslations(tx).Delete(m.ID); err != nil { + return err + } + + if err = dao.NewCategoryRelation(tx).DeleteByCondition(m.ID, model.CateTypeJob); err != nil { + return err + } + + for _, t := range model.CateJobTypes { + if err = dao.NewCategoryRelation(tx).DeleteByCondition(m.ID, t); err != nil { + return err + } + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return +} + +func (r *Job) List(req request.JobList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewJob().List(req.Lang, req.City, req.Title, req.Desc, req.Reqs, req.JobType, req.JobArea, req.JobUnit, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + res := make([]map[string]any, 0) + for _, list := range lists { + temp := list.Format() + temp["category"] = r.GetCate(list)[list.Lang] + res = append(res, temp) + } + + return res, count +} + +func (r *Job) Get(req request.JobDelete) any { + res := make(map[string]any) + for _, lang := range consts.LangSupport { + x, err := dao.NewJob().GetLang(cast.ToInt64(req.ID), lang) + if err != nil { + err2.DbErr(err) + } + + if x == nil { + res[lang] = make(map[string]any) + continue + } + + format := x.Format() + format["category"] = r.GetCate(x)[x.Lang] + res[lang] = format + } + + return res +} + +func (r *Job) GetCate(params *model.JobPage) map[string]map[string]any { + // type:id + s := map[string]int64{ + model.CateTypeJobType: params.JobType, + model.CateTypeJobArea: params.JobArea, + model.CateTypeJobUnit: params.JobUnit, + } + + res := make(map[string]map[string]any) + + for t, id := range s { + x, err := dao.NewCategory().GetWithType(id, t) + if err != nil { + err2.DbErr(err) + } + + if x == nil { + x = new(model.Category) + } + + for _, lang := range consts.LangSupport { + name := "" + switch lang { + case consts.LangZH: + name = x.Name + case consts.LangEN: + name = x.NameEn + } + if _, ok := res[lang]; !ok { + res[lang] = make(map[string]any) + } + + res[lang][t] = x.ID + res[lang][t+"_name"] = name + } + } + + return res +} diff --git a/app/admin/service/news.go b/app/admin/service/news.go new file mode 100644 index 0000000..ddbc3af --- /dev/null +++ b/app/admin/service/news.go @@ -0,0 +1,229 @@ +package service + +import ( + "encoding/json" + "slices" + "strings" + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/consts" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/base/global" + + "github.com/smbrave/goutil" + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type News struct{} + +func (r *News) Create(req request.NewsCreate) int64 { + images := make(map[string][]string) + for sourceType, imgs := range req.CoverResource { + images[sourceType] = Images(imgs) + } + + m := new(model.News) + m.CoverImages = goutil.EncodeJSON(images) + m.CoverShow = req.CoverShow + m.Count = 0 + m.CreateTime = time.Now().Unix() + m.Weight = cast.ToInt64(req.Weight) + m.Pinned = cast.ToInt64(req.Pinned) % 2 + + ms := make([]*model.NewsTranslations, 0) + for lang, trans := range req.Trans { + lang = strings.ToUpper(lang) + if !slices.Contains(consts.LangSupport, lang) { + continue + } + ms = append(ms, &model.NewsTranslations{ + Lang: lang, + Title: trans.Title, + PublishTime: trans.PublishTime, + Content: trans.Content, + }) + } + + err := global.GetDB().Transaction(func(tx *gorm.DB) error { + if err := dao.NewNews(tx).Create(m); err != nil { + return err + } + + for _, trans := range ms { + trans.NewsID = m.ID + if err := dao.NewNewsTranslations(tx).Create(trans); err != nil { + return err + } + } + + if err := BizCateCreate(tx, m.ID, cast.ToInt64(req.CategoryID), model.CateTypeNews); err != nil { + return err + } + + return nil + }) + if err != nil { + err2.DbErr(err) + } + + return m.ID +} + +func (r *News) Update(req request.NewsUpdate) { + m, err := dao.NewNews().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + images := make(map[string][]string) + _ = json.Unmarshal([]byte(m.CoverImages), &images) + + if req.CoverResource != nil { + for sourceType, imgs := range req.CoverResource { + images[sourceType] = Images(imgs) + } + m.CoverImages = goutil.EncodeJSON(images) + } + + if m.CoverShow != "" { + m.CoverShow = req.CoverShow + } + + if weight := cast.ToInt64(req.Weight); weight != m.Weight { + m.Weight = weight + } + + if pinned := cast.ToInt64(req.Pinned); pinned != m.Pinned { + m.Pinned = pinned + } + + m.UpdateTime = time.Now().Unix() + + ms := make([]*model.NewsTranslations, 0) + for lang, trans := range req.Trans { + lang = strings.ToUpper(lang) + if !slices.Contains(consts.LangSupport, lang) { + continue + } + t, err := dao.NewNewsTranslations().GetByMaster(m.ID, lang) + if err != nil { + err2.DbErr(err) + } + + if t == nil { + t = new(model.NewsTranslations) + } + if trans.Title != "" { + t.Title = trans.Title + } + + if trans.PublishTime != "" { + t.PublishTime = trans.PublishTime + } + + if trans.Content != "" { + t.Content = trans.Content + } + + ms = append(ms, t) + } + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewNews(tx).Update(m); err != nil { + return err + } + + for _, trans := range ms { + trans.NewsID = m.ID + if err = dao.NewNewsTranslations(tx).Update(trans); err != nil { + return err + } + } + + if cast.ToInt64(req.CategoryID) > 0 { + if err = BizCateUpdate(tx, m.ID, cast.ToInt64(req.CategoryID), model.CateTypeNews); err != nil { + return err + } + + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return +} + +func (r *News) Delete(req request.NewsDelete) { + m, err := dao.NewNews().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewNews(tx).Delete(m.ID); err != nil { + return err + } + + if err = dao.NewNewsTranslations(tx).Delete(m.ID); err != nil { + return err + } + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return +} + +func (r *News) List(req request.NewsList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewNews().List(req.Lang, req.Title, req.Content, req.PublishTime, cast.ToInt64(req.CategoryID), -1, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} + +func (r *News) Get(req request.NewsDelete) any { + res := make(map[string]any) + for _, lang := range consts.LangSupport { + x, err := dao.NewNews().GetLang(cast.ToInt64(req.ID), lang) + if err != nil { + err2.DbErr(err) + } + + if x == nil { + res[lang] = make(map[string]any) + continue + } + + res[lang] = x.Format() + } + + return res +} diff --git a/app/admin/service/page.go b/app/admin/service/page.go new file mode 100644 index 0000000..7973676 --- /dev/null +++ b/app/admin/service/page.go @@ -0,0 +1,196 @@ +package service + +import ( + "fmt" + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/base/global" + + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type Page struct{} + +func (r *Page) Create(req request.PageCreate) int64 { + r.Pid(req.Pid) + + m := new(model.Page) + m.Name = req.Name + m.Pid = cast.ToInt64(req.Pid) + m.Page = req.Page + m.Content = req.Extra + m.Weight = cast.ToInt64(req.Weight) + m.Tags = req.Tags + m.CreateTime = time.Now().Unix() + + err := global.GetDB().Transaction(func(tx *gorm.DB) error { + if err := dao.NewPage(tx).Create(m); err != nil { + return err + } + + m.Path = r.Path(m.ID, m.Pid) + if err := dao.NewPage(tx).Update(m); err != nil { + return err + } + + // no category + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return m.ID +} + +func (r *Page) Path(id, pid int64) string { + p := "" + m, err := dao.NewPage().Get(cast.ToInt64(pid)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + p = fmt.Sprintf("/%d", id) + } else { + p = fmt.Sprintf("%s/%d", m.Path, id) + } + + return p +} + +// Pid :pid check +func (r *Page) Pid(id string) { + idx := cast.ToInt64(id) + if idx == 0 { + return + } + + e, err := dao.NewPage().Exists(idx) + if err != nil { + err2.DbErr(err) + } + + if !e { + err2.NotEsxErr() + } +} + +func (r *Page) Update(req request.PageUpdate) { + r.Pid(req.Pid) + m, err := dao.NewPage().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + oldPid := m.Pid + oldPath := m.Path + + if req.Name != "" { + m.Name = req.Name + } + + if req.Page != "" { + m.Page = req.Page + } + + if req.Extra != "" { + m.Content = req.Extra + } + + if weight := cast.ToInt64(req.Weight); weight != 0 { + m.Weight = weight + } + + if req.Tags != "" { + m.Tags = req.Tags + } + + if pid := cast.ToInt64(req.Pid); pid != m.Pid { + if pid == m.ID { + pid = 0 + } + + m.Path = fmt.Sprintf("/%d", m.ID) + if pid != 0 { + m.Path = r.Path(m.ID, m.Pid) + } + + m.Pid = pid + } + + m.UpdateTime = time.Now().Unix() + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewPage(tx).Update(m); err != nil { + return err + } + + if oldPid == m.Pid { + return nil + } + + // pid was updated + subs, err := dao.NewPage(tx).GetByPath(oldPath) + if err != nil { + return err + } + for _, sub := range subs { + parent, _ := dao.NewPage(tx).Get(sub.Pid) + sub.Path = fmt.Sprintf("%s/%d", parent.Path, sub.ID) + if err = dao.NewPage(tx).Update(sub); err != nil { + return err + } + } + + return nil + }) + if err != nil { + err2.DbErr(err) + } + + return +} + +func (r *Page) Delete(req request.PageDelete) { + m, err := dao.NewPage().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + if err = dao.NewPage().Delete(m.ID); err != nil { + err2.DbErr(err) + } + return +} + +func (r *Page) List(req request.PageList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewPage().List(req.Name, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} diff --git a/app/admin/service/process.go b/app/admin/service/process.go new file mode 100644 index 0000000..8413d6a --- /dev/null +++ b/app/admin/service/process.go @@ -0,0 +1,192 @@ +package service + +import ( + "slices" + "strings" + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/consts" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/base/global" + + "github.com/smbrave/goutil" + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type Process struct{} + +func (r *Process) Create(req request.ProcessCreate) int64 { + m := &model.Process{CreateTime: time.Now().Unix()} + mt := make([]*model.ProcessTranslations, 0) + for lang, trans := range req.Translations { + lang = strings.ToUpper(lang) + if !slices.Contains(consts.LangSupport, lang) { + continue + } + + t := new(model.ProcessTranslations) + // 图片处理,只提取object_key + t.Images = goutil.EncodeJSON(Images(trans.Images)) + t.Lang = lang + t.Content = trans.Content + t.ProcessTime = trans.ProcessTime + t.Title = trans.Title + mt = append(mt, t) + } + + err := global.GetDB().Transaction(func(tx *gorm.DB) error { + if err := dao.NewProcess(tx).Create(m); err != nil { + return err + } + + for _, trans := range mt { + trans.ProcessID = m.ID + if err := dao.NewProcessTranslations(tx).Create(trans); err != nil { + return err + } + } + + if err := BizCateCreate(tx, m.ID, cast.ToInt64(req.CategoryID), model.CateTypeProcess); err != nil { + return err + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return m.ID +} + +func (r *Process) Update(req request.ProcessUpdate) { + m, err := dao.NewProcess().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + mt := make([]*model.ProcessTranslations, 0) + for lang, trans := range req.Translations { + lang = strings.ToUpper(lang) + if !slices.Contains(consts.LangSupport, lang) { + continue + } + + t, err := dao.NewProcessTranslations().GetByMaster(m.ID, lang) + if err != nil { + err2.DbErr(err) + } + + if t == nil { + t = new(model.ProcessTranslations) + } + + if trans.Title != "" { + t.Title = trans.Title + } + if len(trans.Images) > 0 { + t.Images = goutil.EncodeJSON(Images(trans.Images)) + } + if trans.Content != "" { + t.Content = trans.Content + } + if trans.ProcessTime != "" { + t.ProcessTime = trans.ProcessTime + } + + mt = append(mt, t) + } + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + m.UpdateTime = time.Now().Unix() + _ = dao.NewProcess(tx).Update(m) + + for _, trans := range mt { + trans.ProcessID = m.ID + if err = dao.NewProcessTranslations(tx).Update(trans); err != nil { + return err + } + } + + if cast.ToInt64(req.CategoryID) != 0 { + if err = BizCateUpdate(tx, m.ID, cast.ToInt64(req.CategoryID), model.CateTypeProcess); err != nil { + return err + } + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return +} + +func (r *Process) Delete(req request.ProcessDelete) { + m, err := dao.NewProcess().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewProcess().Delete(m.ID); err != nil { + err2.DbErr(err) + } + + if err = dao.NewProcessTranslations().Delete(m.ID); err != nil { + err2.DbErr(err) + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + return +} + +func (r *Process) List(req request.ProcessList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewProcess().List(req.Lang, req.Title, req.Content, req.ProcessTime, cast.ToInt64(req.CategoryID), req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} + +func (r *Process) Get(req request.ProcessDelete) any { + res := make(map[string]any) + for _, lang := range consts.LangSupport { + x, err := dao.NewProcess().GetLang(lang, cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + res[lang] = x.Format() + } + + return res +} diff --git a/app/admin/service/resource.go b/app/admin/service/resource.go new file mode 100644 index 0000000..1a4197f --- /dev/null +++ b/app/admin/service/resource.go @@ -0,0 +1,203 @@ +package service + +import ( + "fmt" + "net/http" + "strings" + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/base/global" + + "github.com/smbrave/goutil" + "github.com/spf13/cast" + "gorm.io/gorm" +) + +var methods = map[string]struct{}{ + http.MethodGet: {}, + http.MethodPut: {}, + http.MethodPost: {}, + http.MethodPatch: {}, + http.MethodDelete: {}, +} + +var types = map[string]struct{}{ + model.ResourceTypeApi: {}, + model.ResourceTypePage: {}, +} + +type Resource struct{} + +func (r *Resource) Create(req request.ResourceCreate) int64 { + if _, ok := types[req.Type]; !ok { + err2.ParamErr("type", req.Type) + } + + if req.Method != "" { + if _, ok := methods[strings.ToUpper(req.Method)]; !ok { + err2.ParamErr("method", req.Method) + } + } + + // pid check + r.PidCheck(cast.ToInt64(req.Pid)) + + m := new(model.Resource) + m.Name = req.Name + m.Type = req.Type + m.Path = req.Path + m.Code = goutil.Md5(req.Path) + m.Method = req.Method + m.CreateTime = time.Now().Unix() + m.Pid = cast.ToInt64(req.Pid) + + err := global.GetDB().Transaction(func(tx *gorm.DB) error { + if err := dao.NewResource().Create(m); err != nil { + return err + } + + resource, _ := dao.NewResource().Get(m.Pid) + if resource == nil { + m.Maps = fmt.Sprintf("/%d", m.ID) + } else { + m.Maps = fmt.Sprintf("%s/%d", resource.Maps, m.ID) + } + + if err := dao.NewResource().Update(m); err != nil { + return err + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return m.ID +} + +func (r *Resource) PidCheck(pid int64) { + if pid == 0 { + return + } + + resource, err := dao.NewResource().Get(pid) + if err != nil { + err2.DbErr(err) + } + + if resource == nil { + err2.NotEsxErr() + } +} + +func (r *Resource) Update(req request.ResourceUpdate) { + m, err := dao.NewResource().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + if req.Name != "" { + m.Name = req.Name + } + + if _, ok := types[req.Type]; !ok { + err2.ParamErr("type", req.Type) + } + + if req.Method != "" { + if _, ok := methods[strings.ToUpper(req.Method)]; !ok { + err2.ParamErr("method", req.Method) + } + } + + if cast.ToInt64(req.Pid) != m.Pid { + r.PidCheck(m.Pid) + } + + if req.Path != "" { + m.Path = req.Path + } + + pid := cast.ToInt64(req.Pid) + if pid == m.ID { + err2.ParamErr("pid not error") + } + + m.UpdateTime = time.Now().Unix() + oldMaps := m.Maps + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + + // 查询上级的maps更新m的maps + if pid != 0 { + // 先更新本级 + m.Pid = pid + c, _ := dao.NewResource().Get(pid) + m.Maps = fmt.Sprintf("%s/%d", c.Maps, m.ID) + if err = dao.NewResource().Update(m); err != nil { + return err + } + + // 再更新下级 + slavers, _ := dao.NewResource().GetMaps(oldMaps) + for _, slaver := range slavers { + master, _ := dao.NewResource().Get(slaver.Pid) + if master != nil { + slaver.Maps = fmt.Sprintf("%s/%d", master.Maps, slaver.ID) + if err = dao.NewResource().Update(slaver); err != nil { + return err + } + } + } + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + return +} + +func (r *Resource) Delete(req request.ResourceDelete) { + m, err := dao.NewResource().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + if err = dao.NewResource().Delete(m.ID); err != nil { + err2.DbErr(err) + } + return +} + +func (r *Resource) List(req request.ResourceList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewResource().List(req.Name, req.Type, req.Code, req.Method, req.Path, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} diff --git a/app/admin/service/role.go b/app/admin/service/role.go new file mode 100644 index 0000000..ae45ddf --- /dev/null +++ b/app/admin/service/role.go @@ -0,0 +1,250 @@ +package service + +import ( + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/cache" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/app/common/utils" + "yintai-api/base/global" + + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type Role struct{} + +func (r *Role) Create(req request.RoleCreate) int64 { + if req.Name == "" { + err2.ParamErr("name is empty.") + } + + m := new(model.Role) + m.Name = req.Name + m.Desc = req.Desc + m.CreateTime = time.Now().Unix() + + err := global.GetDB().Transaction(func(tx *gorm.DB) error { + if err := dao.NewRole(tx).Create(m); err != nil { + return err + } + + if len(req.Resource) == 0 { + return nil + } + + resource := make([]int64, 0, len(req.Resource)) + for _, s := range req.Resource { + if v := cast.ToInt64(s); v > 0 { + resource = append(resource, v) + } + } + + mr, err := dao.NewResource(tx).GetByIds(resource) + if err != nil { + return err + } + + rs := make([]*model.RoleResource, 0, len(resource)) + for _, v := range mr { + rs = append(rs, &model.RoleResource{ + RoleID: m.ID, + ResourceID: v.ID, + }) + } + + if err = dao.NewRoleResource(tx).Create(rs); err != nil { + return err + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + try := 3 + for try > 0 { + if err = utils.LazyLoadPermission(m.ID); err == nil { + break + } + try-- + } + + return m.ID +} + +func (r *Role) Update(req request.RoleUpdate) { + m, err := dao.NewRole().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + if req.Name != "" { + m.Name = req.Name + } + + if req.Desc != "" { + m.Desc = req.Desc + } + + m.UpdateTime = time.Now().Unix() + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewRole(tx).Update(m); err != nil { + return err + } + + if len(req.Resource) == 0 { + return nil + } + + resource := make([]int64, 0, len(req.Resource)) + for _, s := range req.Resource { + if v := cast.ToInt64(s); v > 0 { + resource = append(resource, v) + } + } + mr, err := dao.NewResource(tx).GetByIds(resource) + if err != nil { + return err + } + + rs := make([]*model.RoleResource, 0, len(resource)) + for _, v := range mr { + rs = append(rs, &model.RoleResource{ + RoleID: m.ID, + ResourceID: v.ID, + }) + } + + if err = dao.NewRoleResource(tx).Delete(m.ID); err != nil { + return err + } + + if err = dao.NewRoleResource(tx).Create(rs); err != nil { + return err + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + try := 3 + for try > 0 { + if err = utils.LazyLoadPermission(m.ID); err == nil { + break + } + try-- + } + + return +} + +func (r *Role) Delete(req request.RoleDelete) { + m, err := dao.NewRole().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + + if m == nil { + err2.NotEsxErr() + return + } + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewRole(tx).Delete(m.ID); err != nil { + return err + } + + if err = dao.NewRoleResource(tx).Delete(m.ID); err != nil { + return err + } + + if cast.ToBool(req.DelAccount) == true { + users, err := dao.NewUserRole(tx).GetAccount(m.ID) + if err != nil { + return err + } + + if users == nil { + return nil + } + + userIds := make([]int64, 0, len(users)) + for _, user := range users { + userIds = append(userIds, user.ID) + } + + if err = dao.NewUser().BatchDelete(userIds); err != nil { + return err + } + + if err = dao.NewUserRole(tx).RoleDelete(m.ID); err != nil { + return err + } + } + + return nil + }) + + if err != nil { + err2.DbErr(err) + } + + cache.GetRedis().Expire(cache.NewKeys().Api(m.ID), 1) + + return +} + +func (r *Role) List(req request.RoleList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewRole().List(req.Name, req.Desc, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} + +func (r *Role) Get(req request.RoleGet) any { + resources, err := dao.NewResource().RoleGet(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + res := make([]map[string]any, 0, len(resources)) + for _, resource := range resources { + res = append(res, resource.Format()) + } + + return res +} + +func (r *Role) Accounts(req request.RoleGet) any { + users, err := dao.NewUserRole().GetAccount(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr(err) + } + res := make([]map[string]any, 0, len(users)) + for _, user := range users { + res = append(res, user.Format()) + } + + return res +} diff --git a/app/admin/service/token.go b/app/admin/service/token.go new file mode 100644 index 0000000..c2e026f --- /dev/null +++ b/app/admin/service/token.go @@ -0,0 +1,29 @@ +package service + +import ( + "encoding/json" + "yintai-api/app/common/cache" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" +) + +type Token struct{} + +func NewToken() *Token { + return &Token{} +} + +func (t *Token) UserInfo(token string) *model.UserWithRole { + key := cache.NewKeys().User() + token + tx := cache.GetRedis().Get(key) + if tx.Val() == "" { + err2.TokenErr() + } + + user := new(model.UserWithRole) + if err := json.Unmarshal([]byte(tx.Val()), user); err != nil { + err2.TokenErr("parse user info error", err) + } + + return user +} diff --git a/app/admin/service/user.go b/app/admin/service/user.go new file mode 100644 index 0000000..b8b3deb --- /dev/null +++ b/app/admin/service/user.go @@ -0,0 +1,263 @@ +package service + +import ( + "fmt" + "strings" + "time" + "yintai-api/app/admin/request" + "yintai-api/app/common/cache" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + "yintai-api/base/global" + + "github.com/smbrave/goutil" + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type User struct { + user *model.UserWithRole +} + +func NewUser(u *model.UserWithRole) *User { + return &User{user: u} +} + +func (a *User) Create(req request.UserCreate) int64 { + // 通过 account 判断用户是否存在 + exist, err := dao.NewUser().AccountExists(req.Account) + if err != nil { + err2.DbErr("", err) + } + if exist { + err2.EsxErr() + } + + req.Password = strings.TrimSpace(req.Password) + if len(req.Password) < 1 { + err2.ParamErr(": password too short") + } + + if cast.ToInt64(req.Role) != 0 { + // 检查 role 是否存在 + exist, err = dao.NewRole().Exists(cast.ToInt64(req.Role)) + if err != nil { + err2.DbErr("", err) + } + + if !exist { + err2.NotEsxErr(" check role") + } + } + + // 通过事务来创建账号以及分配用户角色 + m := new(model.User) + m.Username = req.Username + m.Account = req.Account + m.Password = goutil.Md5(req.Password + model.Salt) + m.CreateTime = time.Now().Unix() + m.Status = model.UserStatusNormal + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewUser(tx).Create(m); err != nil { + return err + } + + if cast.ToInt64(req.Role) != 0 { + var role *model.Role + // 获取这个角色的页面和路由的所有权限 + role, err = dao.NewRole(tx).Get(cast.ToInt64(req.Role)) + if err != nil { + return err + } + m := new(model.UserRole) + m.UserID = cast.ToInt64(role.ID) + m.RoleID = cast.ToInt64(role.ID) + if err = dao.NewUserRole(tx).Create(m); err != nil { + return err + } + } + + return nil + }) + + if err != nil { + err2.DbErr("", err) + } + + return m.ID +} + +func (a *User) Update(req request.UserUpdate) { + var user *model.User + var err error + user, err = dao.NewUser().AccountGet(a.user.Account) + if err != nil { + err2.DbErr("", err) + } + if user == nil { + err2.NotEsxErr("") + return + } + + if req.Username != "" { + user.Username = req.Username + } + + if cast.ToInt64(req.Status) != 0 { + user.Status = cast.ToInt64(req.Status) + } + + if cast.ToInt64(req.Role) != 0 { + role, err := dao.NewRole().Exists(cast.ToInt64(req.Role)) + if err != nil { + err2.DbErr("", err) + } + if !role { + err2.NotEsxErr() + } + } + + // 不能修改密码 + //req.Password = strings.TrimSpace(req.Password) + //if len(req.Password) < 1 { + // err2.ParamErr(": password too short") + //} + // + //user.Password = goutil.Md5(req.Password + Salt) + + err = global.GetDB().Transaction(func(tx *gorm.DB) error { + if err = dao.NewUser(tx).Update(user); err != nil { + return err + } + + if cast.ToInt64(req.Role) != 0 { + if err = dao.NewUserRole(tx).Delete(user.ID); err != nil { + return err + } + if err = dao.NewUserRole(tx).Create(&model.UserRole{UserID: user.ID, RoleID: cast.ToInt64(req.Role)}); err != nil { + return err + } + } + + // 禁用即下线 + if user.Status == model.UserStatusBaned { + a.expireByAccount(user.Account) + } + + return nil + }) + + if err != nil { + err2.DbErr("", err) + } + +} + +func (a *User) Delete(req request.UserDelete) { + var user *model.User + var err error + user, err = dao.NewUser().Get(cast.ToInt64(req.ID)) + if err != nil { + err2.DbErr("", err) + } + if user == nil { + err2.NotEsxErr("") + return + } + + if err = dao.NewUser().Delete(user.ID); err != nil { + err2.DbErr("", err) + } + + a.expireByAccount(user.Account) + return +} + +func (a *User) List(req request.UserList) (any, int64) { + req.Pagination() + users, count, err := dao.NewUser().List(req.Username, req.Account, cast.ToInt64(req.Role), req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr("", err) + } + + res := make([]map[string]any, 0) + for _, m := range users { + res = append(res, m.Format()) + } + + return res, count +} + +func (a *User) Login(req request.UserLogin) any { + user, err := dao.NewUser().GetWithRole(req.Account) + if err != nil { + err2.DbErr("", err) + } + + if user == nil { + err2.NotEsxErr("") + return "" + } + + if user.Password != goutil.Md5(req.Password+model.Salt) { + err2.PriErr(": wrong password") + } + + token := fmt.Sprintf("%s", goutil.RandomStr(64)) + expire := time.Hour * 24 * 30 + redix := cache.GetRedis() + key := cache.NewKeys().User() + token + if err = redix.Set(key, goutil.EncodeJSON(user), expire).Err(); err != nil { + err2.DbErr("", err) + } + + // 保存用户的user_id 到token + // 通过用户的account来创建一个 k-v account是key,其他的是value + if oldToken := redix.Get(cache.NewKeys().User2Token() + user.Account).Val(); oldToken != "" { + a.expire(oldToken) + } + + redix.Set(cache.NewKeys().User2Token()+user.Account, token, expire) + + resource, err := dao.NewResource().RoleGet(user.RoleID) + if err != nil { + err2.DbErr("", err) + } + + userResource := make([]map[string]any, 0) + for _, v := range resource { + if v.Type != "page" { + continue + } + userResource = append(userResource, v.Format()) + } + + res := make(map[string]any) + res["resource"] = userResource + res["token"] = token + + return res +} + +func (a *User) Logout(req request.UserLogout) { + a.expire(req.Token) + return +} + +func (a *User) expireByAccount(account string) { + key := cache.NewKeys().User2Token() + account + token := cache.GetRedis().Get(key).Val() + if token == "" { + return + } + + a.expire(token) +} + +func (a *User) expire(token string) { + redix := cache.GetRedis() + key := cache.NewKeys().User() + token + redix.Del(key) + return +} diff --git a/app/api/controller/category.go b/app/api/controller/category.go new file mode 100644 index 0000000..854f242 --- /dev/null +++ b/app/api/controller/category.go @@ -0,0 +1,22 @@ +package controller + +import ( + "net/http" + "yintai-api/app/api/request" + "yintai-api/app/api/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Category struct{} + +func (r *Category) List(ctx *gin.Context) { + var req request.CategoryList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Category).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} diff --git a/app/api/controller/config.go b/app/api/controller/config.go new file mode 100644 index 0000000..c95306f --- /dev/null +++ b/app/api/controller/config.go @@ -0,0 +1,22 @@ +package controller + +import ( + "net/http" + "yintai-api/app/api/request" + "yintai-api/app/api/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Config struct{} + +func (r *Config) List(ctx *gin.Context) { + var req request.ConfigList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Config).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} diff --git a/app/api/controller/document.go b/app/api/controller/document.go new file mode 100644 index 0000000..1f86b79 --- /dev/null +++ b/app/api/controller/document.go @@ -0,0 +1,29 @@ +package controller + +import ( + "net/http" + "yintai-api/app/api/request" + "yintai-api/app/api/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Document struct{} + +func (r *Document) List(ctx *gin.Context) { + var req request.DocumentList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Document).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} + +func (r *Document) Get(ctx *gin.Context) { + var req request.DocumentDelete + req.ID = ctx.Param("id") + res := new(service.Document).Get(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} diff --git a/app/api/controller/job.go b/app/api/controller/job.go new file mode 100644 index 0000000..ab9ff60 --- /dev/null +++ b/app/api/controller/job.go @@ -0,0 +1,29 @@ +package controller + +import ( + "net/http" + "yintai-api/app/api/request" + "yintai-api/app/api/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Job struct{} + +func (r *Job) List(ctx *gin.Context) { + var req request.JobList2 + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Job).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} + +func (r *Job) Get(ctx *gin.Context) { + var req request.JobDelete + req.ID = ctx.Param("id") + res := new(service.Job).Get(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} diff --git a/app/api/controller/news.go b/app/api/controller/news.go new file mode 100644 index 0000000..fa5d983 --- /dev/null +++ b/app/api/controller/news.go @@ -0,0 +1,38 @@ +package controller + +import ( + "net/http" + "yintai-api/app/api/request" + "yintai-api/app/api/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type News struct{} + +func (r *News) List(ctx *gin.Context) { + var req request.NewsList2 + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.News).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} + +func (r *News) Get(ctx *gin.Context) { + var req request.NewsDelete + req.ID = ctx.Param("id") + res := new(service.News).Get(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} + +func (r *News) PreNext(ctx *gin.Context) { + var req request.PreNext + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res := new(service.News).PreNext(req) + ctx.JSON(http.StatusOK, response.NewRsp(res)) +} diff --git a/app/api/controller/page.go b/app/api/controller/page.go new file mode 100644 index 0000000..9b8e109 --- /dev/null +++ b/app/api/controller/page.go @@ -0,0 +1,22 @@ +package controller + +import ( + "net/http" + "yintai-api/app/api/request" + "yintai-api/app/api/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Page struct{} + +func (r *Page) List(ctx *gin.Context) { + var req request.PageList + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Page).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} diff --git a/app/api/controller/process.go b/app/api/controller/process.go new file mode 100644 index 0000000..46279e9 --- /dev/null +++ b/app/api/controller/process.go @@ -0,0 +1,22 @@ +package controller + +import ( + "net/http" + "yintai-api/app/api/request" + "yintai-api/app/api/service" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" +) + +type Process struct{} + +func (r *Process) List(ctx *gin.Context) { + var req request.ProcessList2 + if err := ctx.Bind(&req); err != nil { + err2.ParamErr(err) + } + res, count := new(service.Process).List(req) + ctx.JSON(http.StatusOK, response.NewListRsp(count, res)) +} diff --git a/app/api/request/category.go b/app/api/request/category.go new file mode 100644 index 0000000..44172a8 --- /dev/null +++ b/app/api/request/category.go @@ -0,0 +1,27 @@ +package request + +import "yintai-api/app/common/request" + +type CategoryCreate struct { + Name string `json:"name"` + NameEn string `json:"name_en"` + Pid string `json:"pid"` + Type string `json:"type"` +} + +type CategoryUpdate struct { + ID string `json:"id"` + CategoryCreate +} + +type CategoryDelete struct { + ID string `form:"id"` +} + +type CategoryList struct { + request.Paginate + Pid string `form:"pid"` + Name string `form:"name"` + NameEn string `form:"name_en"` + Type string `form:"type"` +} diff --git a/app/api/request/config.go b/app/api/request/config.go new file mode 100644 index 0000000..6293138 --- /dev/null +++ b/app/api/request/config.go @@ -0,0 +1,19 @@ +package request + +import "yintai-api/app/common/request" + +type ConfigCreate struct { +} + +type ConfigUpdate struct { + ID string `json:"id"` + ConfigCreate +} + +type ConfigDelete struct { + ID string `form:"id"` +} + +type ConfigList struct { + request.Paginate +} diff --git a/app/api/request/document.go b/app/api/request/document.go new file mode 100644 index 0000000..978e18d --- /dev/null +++ b/app/api/request/document.go @@ -0,0 +1,36 @@ +package request + +import "yintai-api/app/common/request" + +type DocTrans struct { + Path string `json:"path"` + FileName string `json:"file_name"` + Title string `json:"title"` + SubTitle string `json:"sub_title"` + Desc string `json:"desc"` + Extra string `json:"extra"` +} + +type DocumentCreate struct { + Trans map[string]DocTrans `json:"translations"` + CategoryID string `json:"category_id"` +} + +type DocumentUpdate struct { + ID string `json:"id"` + DocumentCreate +} + +type DocumentDelete struct { + ID string `form:"id"` +} + +type DocumentList struct { + request.Paginate + Lang string `form:"lang"` + Category string `form:"category_id"` + FileName string `form:"file_name"` + Title string `form:"title"` + SubTitle string `form:"sub_title"` + Desc string `form:"desc"` +} diff --git a/app/api/request/job.go b/app/api/request/job.go new file mode 100644 index 0000000..9eddbe4 --- /dev/null +++ b/app/api/request/job.go @@ -0,0 +1,52 @@ +package request + +import "yintai-api/app/common/request" + +type JobTrans struct { + Name string `json:"name"` + Desc string `json:"description"` + Requirement string `json:"requirement"` + ContractInfo string `json:"contract_info"` +} + +type JobCreate struct { + JobType string `json:"job_type"` + JobArea string `json:"job_area"` + JobUnit string `json:"job_unit"` + RecruitCount string `json:"recruit_count"` + City string `json:"city"` + Translations map[string]JobTrans `json:"translations"` +} + +type JobUpdate struct { + ID string `json:"id"` + JobCreate +} + +type JobDelete struct { + ID string `form:"id"` +} + +type JobList struct { + request.Paginate + Lang string `form:"lang" binding:"required"` + City string `form:"city"` + Title string `form:"title"` + JobType string `form:"job_type"` + JobArea string `form:"job_area"` + JobUnit string `form:"job_unit"` + Desc string `form:"desc"` + Reqs string `form:"reqs"` +} + +type JobList2 struct { + request.Paginate + Lang string `form:"lang"` + City string `form:"city"` + Title string `form:"title"` + JobType string `form:"job_type"` + JobArea string `form:"job_area"` + JobUnit string `form:"job_unit"` + Desc string `form:"desc"` + Reqs string `form:"reqs"` +} diff --git a/app/api/request/news.go b/app/api/request/news.go new file mode 100644 index 0000000..c544aa2 --- /dev/null +++ b/app/api/request/news.go @@ -0,0 +1,51 @@ +package request + +import "yintai-api/app/common/request" + +type NewsTranslation struct { + Title string `json:"title"` + Content string `json:"content"` + PublishTime string `json:"publish_time"` +} + +type NewsCreate struct { + CategoryID string `json:"category_id"` + CoverResource map[string][]string `json:"cover_resource"` + CoverShow string `json:"cover_show"` + Trans map[string]NewsTranslation `json:"translations"` +} + +type NewsUpdate struct { + ID string `json:"id"` + NewsCreate +} + +type NewsDelete struct { + ID string `form:"id"` +} + +type NewsList struct { + request.Paginate + Lang string `form:"lang" binding:"required"` + Title string `form:"title"` + Content string `form:"content"` + PublishTime string `form:"publish_time"` + CategoryID string `form:"category_id"` +} + +type NewsList2 struct { + request.Paginate + Lang string `form:"lang"` + Title string `form:"title"` + Content string `form:"content"` + PublishTime string `form:"publish_time"` + CategoryID string `form:"category_id"` + Pinned string `form:"pinned"` +} + +type PreNext struct { + ID string `form:"id"` + CategoryID string `form:"category_id"` + Sort string `form:"sort"` + Lang string `form:"lang"` +} diff --git a/app/api/request/page.go b/app/api/request/page.go new file mode 100644 index 0000000..bbaa44d --- /dev/null +++ b/app/api/request/page.go @@ -0,0 +1,28 @@ +package request + +import ( + "yintai-api/app/common/request" +) + +type PageCreate struct { + Pid string `json:"pid"` + Name string `json:"name"` + Page string `json:"page"` + Extra string `json:"content"` + Weight string `json:"weight"` + Tags string `json:"tags"` +} + +type PageUpdate struct { + ID string `json:"id"` + PageCreate +} + +type PageDelete struct { + ID string `form:"id"` +} + +type PageList struct { + request.Paginate + Name string `form:"name"` +} diff --git a/app/api/request/process.go b/app/api/request/process.go new file mode 100644 index 0000000..e79c8bb --- /dev/null +++ b/app/api/request/process.go @@ -0,0 +1,42 @@ +package request + +import "yintai-api/app/common/request" + +type ProcessTranslation struct { + Title string `json:"title"` + Images []string `json:"images"` + Content string `json:"content"` + ProcessTime string `json:"process_time"` +} + +type ProcessCreate struct { + CategoryID string `json:"category_id"` + Translations map[string]ProcessTranslation `json:"translations"` +} + +type ProcessUpdate struct { + ID string `json:"id"` + ProcessCreate +} + +type ProcessDelete struct { + ID string `form:"id"` +} + +type ProcessList struct { + request.Paginate + Lang string `form:"lang" binding:"required"` + Title string `form:"title"` + Content string `form:"content"` + ProcessTime string `form:"process_time"` + CategoryID string `form:"category_id"` +} + +type ProcessList2 struct { + request.Paginate + Lang string `form:"lang"` + Title string `form:"title"` + Content string `form:"content"` + ProcessTime string `form:"process_time"` + CategoryID string `form:"category_id"` +} diff --git a/app/api/service/category.go b/app/api/service/category.go new file mode 100644 index 0000000..85c8054 --- /dev/null +++ b/app/api/service/category.go @@ -0,0 +1,32 @@ +package service + +import ( + "slices" + "yintai-api/app/api/request" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + + "github.com/spf13/cast" +) + +type Category struct{} + +func (r *Category) List(req request.CategoryList) (any, int64) { + if !slices.Contains(model.CategoryTypes, req.Type) { + err2.ParamErr() + } + + req.Pagination() + lists, count, err := dao.NewCategory().List(cast.ToInt64(req.Pid), req.Name, req.NameEn, req.Type, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} diff --git a/app/api/service/config.go b/app/api/service/config.go new file mode 100644 index 0000000..c5caf04 --- /dev/null +++ b/app/api/service/config.go @@ -0,0 +1,24 @@ +package service + +import ( + "yintai-api/app/api/request" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" +) + +type Config struct{} + +func (r *Config) List(req request.ConfigList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewConfig().List(req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} diff --git a/app/api/service/document.go b/app/api/service/document.go new file mode 100644 index 0000000..a6e0f0f --- /dev/null +++ b/app/api/service/document.go @@ -0,0 +1,87 @@ +package service + +import ( + "fmt" + "slices" + "sync" + "yintai-api/app/api/request" + "yintai-api/app/common/consts" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cast" +) + +type Document struct{} + +func (r *Document) List(req request.DocumentList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewDocument().List(req.Lang, cast.ToInt64(req.Category), req.FileName, req.Title, req.SubTitle, req.Desc, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + listLang := make([]string, 0) + for _, list := range lists { + listLang = append(listLang, fmt.Sprintf("%d_%s", list.ID, list.Lang)) + } + + res := make([]any, 0) + var store sync.Map + var wg sync.WaitGroup + for _, list := range lists { + wg.Add(1) + go func(list *model.DocumentPage) { + defer wg.Done() + store.Store(fmt.Sprintf("%d_%s", list.ID, list.Lang), list.Format()) + for _, lang := range consts.LangSupport { + lang = fmt.Sprintf("%d_%s", list.ID, list.Lang) + if !slices.Contains(listLang, lang) { + log.Debugf("%s is writen", list.Lang) + store.Store(lang, r.One(lang, list.ID)) + } + + } + }(list) + } + wg.Wait() + + written := make(map[string]bool) + for _, list := range lists { + for _, lang := range consts.LangSupport { + lang = fmt.Sprintf("%d_%s", list.ID, list.Lang) + if _, ok := written[lang]; ok { + continue + } + + v, ok := store.Load(lang) + if !ok { + continue + } + + res = append(res, v) + written[lang] = true + } + } + + return res, count +} + +func (r *Document) One(lang string, id int64) map[string]any { + x, err := dao.NewDocument().GetLang(lang, id) + if err != nil { + err2.DbErr(err) + } + + return x.Format() +} + +func (r *Document) Get(req request.DocumentDelete) any { + res := make([]map[string]any, 0) + for _, lang := range consts.LangSupport { + res = append(res, r.One(lang, cast.ToInt64(req.ID))) + } + + return res +} diff --git a/app/api/service/job.go b/app/api/service/job.go new file mode 100644 index 0000000..1b8c620 --- /dev/null +++ b/app/api/service/job.go @@ -0,0 +1,114 @@ +package service + +import ( + "fmt" + "slices" + "yintai-api/app/api/request" + "yintai-api/app/common/consts" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + + "github.com/spf13/cast" +) + +type Job struct{} + +func (r *Job) List(req request.JobList2) (any, int64) { + req.Pagination() + lists, count, err := dao.NewJob().List(req.Lang, req.City, req.Title, req.Desc, req.Reqs, req.JobType, req.JobArea, req.JobUnit, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + listLang := make([]string, 0) + for _, list := range lists { + listLang = append(listLang, fmt.Sprintf("%d_%s", list.ID, list.Lang)) + } + res := make([]map[string]any, 0) + for _, list := range lists { + temp := list.Format() + temp["category"] = r.GetCate(list)[list.Lang] + res = append(res, temp) + + for _, lang := range consts.LangSupport { + lang = fmt.Sprintf("%d_%s", list.ID, list.Lang) + if slices.Contains(listLang, lang) { + continue + } + + temp = r.One(lang, list.ID) + temp["category"] = r.GetCate(list)[list.Lang] + res = append(res, temp) + } + } + + return res, count +} + +func (r *Job) One(lang string, id int64) map[string]any { + x, err := dao.NewJob().GetLang(id, lang) + if err != nil { + err2.DbErr(err) + } + return x.Format() +} + +func (r *Job) Get(req request.JobDelete) any { + res := make([]map[string]any, 0) + for _, lang := range consts.LangSupport { + x, err := dao.NewJob().GetLang(cast.ToInt64(req.ID), lang) + if err != nil { + err2.DbErr(err) + } + + if x == nil { + continue + } + + format := x.Format() + format["category"] = r.GetCate(x)[x.Lang] + res = append(res, format) + } + + return res +} + +func (r *Job) GetCate(params *model.JobPage) map[string]map[string]any { + // type:id + s := map[string]int64{ + model.CateTypeJobType: params.JobType, + model.CateTypeJobArea: params.JobArea, + model.CateTypeJobUnit: params.JobUnit, + } + + res := make(map[string]map[string]any) + + for t, id := range s { + x, err := dao.NewCategory().GetWithType(id, t) + if err != nil { + err2.DbErr(err) + } + + if x == nil { + x = new(model.Category) + } + + for _, lang := range consts.LangSupport { + name := "" + switch lang { + case consts.LangZH: + name = x.Name + case consts.LangEN: + name = x.NameEn + } + if _, ok := res[lang]; !ok { + res[lang] = make(map[string]any) + } + + res[lang][t] = x.ID + res[lang][t+"_name"] = name + } + } + + return res +} diff --git a/app/api/service/news.go b/app/api/service/news.go new file mode 100644 index 0000000..41f8ce8 --- /dev/null +++ b/app/api/service/news.go @@ -0,0 +1,124 @@ +package service + +import ( + "fmt" + "math" + "slices" + "yintai-api/app/api/request" + "yintai-api/app/common/consts" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + "yintai-api/app/common/model" + + "github.com/spf13/cast" +) + +type News struct{} + +func (r *News) List(req request.NewsList2) (any, int64) { + req.Pagination() + lists, count, err := dao.NewNews().List(req.Lang, req.Title, req.Content, req.PublishTime, cast.ToInt64(req.CategoryID), cast.ToInt64(req.Pinned), req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + res := r.ListFormat(lists) + + return res, count +} + +func (r *News) One(lang string, id int64) map[string]any { + x, err := dao.NewNews().GetLang(id, lang) + if err != nil { + err2.DbErr(err) + } + + if x == nil { + return nil + } + + return x.Format() +} + +func (r *News) Get(req request.NewsDelete) any { + res := make([]map[string]any, 0) + for _, lang := range consts.LangSupport { + data := r.One(lang, cast.ToInt64(req.ID)) + if data == nil { + continue + } + + res = append(res, data) + } + + _ = dao.NewNews().Incr(cast.ToInt64(req.ID)) + + return res +} + +func (r *News) PreNext(req request.PreNext) any { + news := make([]*model.NewsPage, 0) + var err error + if news, err = dao.NewNews().GetN(cast.ToInt64(req.CategoryID), req.Lang, 1, math.MaxInt32, req.Sort); err != nil { + err2.DbErr(err) + } + + res := make([]any, 0) + for i, v := range news { + if v.News.ID == cast.ToInt64(req.ID) { + if i > 0 { + v := news[i-1] + res = append(res, r.preFormat(v)) + } + + if i+1 < len(news) { + v := news[i+1] + res = append(res, r.preFormat(v)) + } + break + } + } + + return res +} + +func (r *News) preFormat(v *model.NewsPage) any { + res := make(map[string]any) + res["id"] = v.ID + res["title"] = v.Title + res["weight"] = v.Weight + res["lang"] = v.Lang + return res +} + +func (r *News) ListFormat(lists []*model.NewsPage) any { + listLang := make([]string, 0) + for _, list := range lists { + if list == nil { + continue + } + listLang = append(listLang, fmt.Sprintf("%d_%s", list.ID, list.Lang)) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + if list == nil { + continue + } + res = append(res, list.Format()) + for _, lang := range consts.LangSupport { + langFormat := fmt.Sprintf("%d_%s", list.ID, lang) + if slices.Contains(listLang, langFormat) { + continue + } + + data := r.One(lang, list.ID) + if data == nil { + continue + } + + res = append(res, data) + } + } + + return res +} diff --git a/app/api/service/page.go b/app/api/service/page.go new file mode 100644 index 0000000..5507f2d --- /dev/null +++ b/app/api/service/page.go @@ -0,0 +1,24 @@ +package service + +import ( + "yintai-api/app/api/request" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" +) + +type Page struct{} + +func (r *Page) List(req request.PageList) (any, int64) { + req.Pagination() + lists, count, err := dao.NewPage().List(req.Name, req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + } + + return res, count +} diff --git a/app/api/service/process.go b/app/api/service/process.go new file mode 100644 index 0000000..64f6b6b --- /dev/null +++ b/app/api/service/process.go @@ -0,0 +1,49 @@ +package service + +import ( + "fmt" + "slices" + "yintai-api/app/api/request" + "yintai-api/app/common/consts" + "yintai-api/app/common/dao" + err2 "yintai-api/app/common/err" + + "github.com/spf13/cast" +) + +type Process struct{} + +func (r *Process) List(req request.ProcessList2) (any, int64) { + req.Pagination() + lists, count, err := dao.NewProcess().List(req.Lang, req.Title, req.Content, req.ProcessTime, cast.ToInt64(req.CategoryID), req.Page, req.Size, req.Sort) + if err != nil { + err2.DbErr(err) + } + listLang := make([]string, 0) + for _, list := range lists { + listLang = append(listLang, fmt.Sprintf("%d_%s", list.ID, list.Lang)) + } + + res := make([]map[string]any, 0) + for _, list := range lists { + res = append(res, list.Format()) + for _, lang := range consts.LangSupport { + lang = fmt.Sprintf("%d_%s", list.ID, list.Lang) + if slices.Contains(listLang, lang) { + continue + } + res = append(res, r.Get(lang, list.ID)) + } + } + + return res, count +} + +func (r *Process) Get(lang string, id int64) map[string]any { + x, err := dao.NewProcess().GetLang(lang, id) + if err != nil { + err2.DbErr(err) + } + + return x.Format() +} diff --git a/app/common/cache/keys.go b/app/common/cache/keys.go new file mode 100644 index 0000000..6b0c6b7 --- /dev/null +++ b/app/common/cache/keys.go @@ -0,0 +1,33 @@ +package cache + +import ( + "fmt" + "yintai-api/app/common/model" + + "github.com/smbrave/goutil" +) + +type Keys struct{} + +func NewKeys() *Keys { + return &Keys{} +} + +func (k Keys) User() string { + key := "home:user:info:" + return key +} + +func (k Keys) User2Token() string { + key := "home:user:u2token:" + return key +} + +func (k Keys) UserToken(account string) string { + key := fmt.Sprintf("%s_%s_%s", account, model.Salt, account) + return goutil.Md5(key) +} + +func (k Keys) Api(roleID int64) string { + return fmt.Sprintf("%s%d", "home:user:api:", roleID) +} diff --git a/app/common/cache/redis.go b/app/common/cache/redis.go new file mode 100644 index 0000000..2ad1a6c --- /dev/null +++ b/app/common/cache/redis.go @@ -0,0 +1,11 @@ +package cache + +import ( + "yintai-api/base/global" + + "github.com/go-redis/redis" +) + +func GetRedis() *redis.Client { + return global.GetRedis() +} diff --git a/app/common/consts/consts.go b/app/common/consts/consts.go new file mode 100644 index 0000000..abfb270 --- /dev/null +++ b/app/common/consts/consts.go @@ -0,0 +1,8 @@ +package consts + +const ( + LangZH = "ZH" + LangEN = "EN" +) + +var LangSupport = []string{LangEN, LangZH} diff --git a/app/common/dao/base.go b/app/common/dao/base.go new file mode 100644 index 0000000..e8f92da --- /dev/null +++ b/app/common/dao/base.go @@ -0,0 +1,15 @@ +package dao + +import ( + "yintai-api/base/global" + + "gorm.io/gorm" +) + +func getDB(db ...*gorm.DB) *gorm.DB { + if len(db) > 0 { + return db[0] + } + + return global.GetDB() +} diff --git a/app/common/dao/category.go b/app/common/dao/category.go new file mode 100644 index 0000000..5f2248a --- /dev/null +++ b/app/common/dao/category.go @@ -0,0 +1,148 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type Category struct { + db *gorm.DB +} + +func NewCategory(db ...*gorm.DB) *Category { + return &Category{db: getDB(db...)} +} + +func (r *Category) TableName() string { + return "yt_category" +} + +func (r *Category) Create(m *model.Category) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *Category) Update(m *model.Category) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *Category) Delete(path string) error { + return r.db.Table(r.TableName()).Where("path LIKE ?", fmt.Sprintf("%s%%", path)).Delete(&model.Category{}).Error +} + +func (r *Category) Get(id int64) (*model.Category, error) { + var res model.Category + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *Category) GetWithType(id int64, t string) (*model.Category, error) { + var res model.Category + if err := r.db.Table(r.TableName()).Where("id = ? AND type = ?", id, t).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *Category) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *Category) GetByPath(path string) ([]*model.Category, error) { + var res []*model.Category + if err := r.db.Table(r.TableName()).Where("path LIKE ?", fmt.Sprintf("%s%%", path)).Find(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + + return res, nil + +} + +func (r *Category) List(pid int64, name, nameEn, types string, page, size int, sort string) ([]*model.Category, int64, error) { + var req []*model.Category + var count int64 + tx := r.db.Table(r.TableName()).Where("type = ?", types) + if pid != 0 { + tx = tx.Where("pid = ?", pid) + } + + if name != "" { + tx = tx.Where("name LIKE ?", fmt.Sprintf("%%%s%%", name)) + } + + if nameEn != "" { + tx = tx.Where("name_en LIKE ?", fmt.Sprintf("%%%s%%", nameEn)) + } + + if sort != "" { + tx = tx.Order(sort) + } else { + tx = tx.Order("create_time desc") + } + + if err := tx.Count(&count).Error; err != nil { + return nil, 0, err + } + + if err := tx.Offset((page - 1) * size).Limit(size).Find(&req).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, 0, nil + } + return nil, 0, err + } + + return req, count, nil +} + +func (c *Category) GetByBiz(BizID int64, BizType string) (*model.Category, error) { + var res model.Category + tx := c.db.Table(c.TableName() + " c ").Joins(fmt.Sprintf(" LEFT JON %s r ON c.id = r.cateory_id", NewCategoryRelation().TableName())) + tx = tx.Where("r.biz_id = ? AND c.type = ?", BizID, BizType) + + if err := tx.First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + + return &res, nil +} + +func (c *Category) GetByIds(ids ...int64) (map[int64]*model.Category, error) { + resMap := make(map[int64]*model.Category) + var res []*model.Category + if len(ids) > 0 { + tx := c.db.Table(c.TableName()).Where("id IN (?)", ids).Find(&res) + if tx.Error != nil { + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { + return resMap, nil + } + + return resMap, tx.Error + } + } + + for _, re := range res { + resMap[re.ID] = re + } + + return resMap, nil +} diff --git a/app/common/dao/category_relation.go b/app/common/dao/category_relation.go new file mode 100644 index 0000000..e86089d --- /dev/null +++ b/app/common/dao/category_relation.go @@ -0,0 +1,73 @@ +package dao + +import ( + "errors" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type CategoryRelation struct { + db *gorm.DB +} + +func NewCategoryRelation(db ...*gorm.DB) *CategoryRelation { + return &CategoryRelation{db: getDB(db...)} +} + +func (r *CategoryRelation) TableName() string { + return "yt_category_relation" +} + +func (r *CategoryRelation) Create(m *model.CategoryRelation) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *CategoryRelation) Update(m *model.CategoryRelation) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *CategoryRelation) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&CategoryRelation{}).Error +} + +func (r *CategoryRelation) DeleteByCondition(bizID int64, bizType string) error { + return r.db.Table(r.TableName()).Where("biz_id = ? AND type = ?", bizID, bizType).Delete(&CategoryRelation{}).Error +} + +func (r *CategoryRelation) Get(id int64) (*model.CategoryRelation, error) { + var res model.CategoryRelation + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *CategoryRelation) GetByIds(ids ...int64) ([]*model.CategoryRelation, error) { + var res []*model.CategoryRelation + if len(ids) == 0 { + return res, nil + } + if err := r.db.Table(r.TableName()).Where("id in (?)", ids).Find(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return res, nil +} + +func (r *CategoryRelation) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *CategoryRelation) List(...any) ([]*model.CategoryRelation, int64, error) { + panic("implement me") +} diff --git a/app/common/dao/config.go b/app/common/dao/config.go new file mode 100644 index 0000000..b2bbcf9 --- /dev/null +++ b/app/common/dao/config.go @@ -0,0 +1,55 @@ +package dao + +import ( + "errors" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type Config struct { + db *gorm.DB +} + +func NewConfig(db ...*gorm.DB) *Config { + return &Config{db: getDB(db...)} +} + +func (r *Config) TableName() string { + return "yt_config" +} + +func (r *Config) Create(m *model.Config) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *Config) Update(m *model.Config) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *Config) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&Config{}).Error +} + +func (r *Config) Get(id int64) (*model.Config, error) { + var res model.Config + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *Config) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *Config) List(...any) ([]*model.Config, int64, error) { + panic("implement me") +} diff --git a/app/common/dao/config_translations.go b/app/common/dao/config_translations.go new file mode 100644 index 0000000..c2fff3d --- /dev/null +++ b/app/common/dao/config_translations.go @@ -0,0 +1,55 @@ +package dao + +import ( + "errors" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type ConfigTranslations struct { + db *gorm.DB +} + +func NewConfigTranslations(db ...*gorm.DB) *ConfigTranslations { + return &ConfigTranslations{db: getDB(db...)} +} + +func (r *ConfigTranslations) TableName() string { + return "yt_config_translations" +} + +func (r *ConfigTranslations) Create(m *model.ConfigTranslations) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *ConfigTranslations) Update(m *model.ConfigTranslations) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *ConfigTranslations) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&ConfigTranslations{}).Error +} + +func (r *ConfigTranslations) Get(id int64) (*model.ConfigTranslations, error) { + var res model.ConfigTranslations + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *ConfigTranslations) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *ConfigTranslations) List(...any) ([]*model.ConfigTranslations, int64, error) { + panic("implement me") +} diff --git a/app/common/dao/document.go b/app/common/dao/document.go new file mode 100644 index 0000000..4571adf --- /dev/null +++ b/app/common/dao/document.go @@ -0,0 +1,121 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type Document struct { + db *gorm.DB +} + +func NewDocument(db ...*gorm.DB) *Document { + return &Document{db: getDB(db...)} +} + +func (r *Document) TableName() string { + return "yt_document" +} + +func (r *Document) Create(m *model.Document) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *Document) Update(m *model.Document) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *Document) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&Document{}).Error +} + +func (r *Document) Get(id int64) (*model.Document, error) { + var res model.Document + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *Document) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *Document) List(lang string, categoryID int64, fileName, title, subTitle, desc string, page, size int, sort string) ([]*model.DocumentPage, int64, error) { + var list []*model.DocumentPage + var count int64 + tx := r.db.Table(r.TableName() + " AS p ") + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s AS t ON p.id = t.document_id ", NewDocumentTranslations().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s l ON l.biz_id = p.id AND l.type = ?", NewCategoryRelation().TableName()), model.CateTypeFile) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s c ON c.id = l.category_id", NewCategory().TableName())) + if lang != "" { + tx = tx.Where("t.lang = ?", lang) + } + + if fileName != "" { + tx = tx.Where("t.name LIKE ?", "%"+fileName+"%") + } + + if title != "" { + tx = tx.Where("t.title LIKE ?", "%"+title+"%") + } + + if subTitle != "" { + tx = tx.Where("t.sub_title LIKE ?", "%"+subTitle+"%") + } + + if desc != "" { + tx = tx.Where("t.description LIKE ?", "%"+desc+"%") + } + + if cast.ToInt64(categoryID) != 0 { + tx = tx.Where("c.id = ?", cast.ToInt64(categoryID)) + } + + if err := tx.Count(&count).Error; err != nil { + return nil, 0, err + } + + if sort == "" { + tx = tx.Order("p.weight DESC") + } else { + tx = tx.Order(sort) + } + + if err := tx.Offset((page - 1) * size).Limit(size).Select("p.*,t.*,c.name as category_name,c.name_en as category_en,c.id as category_id").Find(&list).Error; err != nil { + return nil, 0, err + } + + return list, count, nil +} + +func (r *Document) GetLang(lang string, id int64) (*model.DocumentPage, error) { + var res *model.DocumentPage + tx := r.db.Table(r.TableName() + " AS p ") + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s AS t ON p.id = t.document_id ", NewDocumentTranslations().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s l ON l.biz_id = p.id AND l.type = ?", NewCategoryRelation().TableName()), model.CateTypeFile) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s c ON c.id = l.category_id", NewCategory().TableName())) + tx = tx.Where("t.lang = ?", lang) + tx = tx.Where("p.id = ?", id) + field := "p.*,t.*,c.name as category_name,c.name_en as category_en,c.id as category_id" + + if err := tx.Select(field).Find(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + + return res, nil +} diff --git a/app/common/dao/document_translations.go b/app/common/dao/document_translations.go new file mode 100644 index 0000000..38824a7 --- /dev/null +++ b/app/common/dao/document_translations.go @@ -0,0 +1,67 @@ +package dao + +import ( + "errors" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type DocumentTranslations struct { + db *gorm.DB +} + +func NewDocumentTranslations(db ...*gorm.DB) *DocumentTranslations { + return &DocumentTranslations{db: getDB(db...)} +} + +func (r *DocumentTranslations) TableName() string { + return "yt_document_translations" +} + +func (r *DocumentTranslations) Create(m *model.DocumentTranslations) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *DocumentTranslations) Update(m *model.DocumentTranslations) error { + return r.db.Table(r.TableName()).Where("document_id = ? AND lang = ?", m.DocumentID, m.Lang).Save(m).Error +} + +func (r *DocumentTranslations) Delete(masterID int64) error { + return r.db.Table(r.TableName()).Where("document_id = ?", masterID).Delete(&DocumentTranslations{}).Error +} + +func (r *DocumentTranslations) Get(id int64) (*model.DocumentTranslations, error) { + var res model.DocumentTranslations + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *DocumentTranslations) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *DocumentTranslations) List(...any) ([]*model.DocumentTranslations, int64, error) { + panic("implement me") +} + +func (r *DocumentTranslations) GetByMaster(masterID int64, lang string) (*model.DocumentTranslations, error) { + var res model.DocumentTranslations + tx := r.db.Table(r.TableName()).Where("document_id = ? AND lang = ?", masterID, lang).First(&res) + if tx.Error != nil { + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, tx.Error + } + return &res, nil +} diff --git a/app/common/dao/job.go b/app/common/dao/job.go new file mode 100644 index 0000000..488f0e4 --- /dev/null +++ b/app/common/dao/job.go @@ -0,0 +1,124 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type Job struct { + db *gorm.DB +} + +func NewJob(db ...*gorm.DB) *Job { + return &Job{db: getDB(db...)} +} + +func (r *Job) TableName() string { + return "yt_job" +} + +func (r *Job) Create(m *model.Job) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *Job) Update(m *model.Job) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *Job) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&Job{}).Error +} + +func (r *Job) Get(id int64) (*model.Job, error) { + var res model.Job + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *Job) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *Job) List(lang, city, title, desc, reqs, jobType, jobArea, jobUnit string, page, size int, sort string) ([]*model.JobPage, int64, error) { + var list []*model.JobPage + var count int64 + tx := r.db.Table(r.TableName() + " AS p ") + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s t ON p.id = t.job_id ", NewJobTranslations().TableName())) + if lang != "" { + tx = tx.Where("t.lang = ?", lang) + } + + if city != "" { + tx = tx.Where("p.city = ?", city) + } + + if title != "" { + tx = tx.Where("t.name LIKE ?", "%"+title+"%") + } + + if desc != "" { + tx = tx.Where("t.description LIKE ?", "%"+desc+"%") + } + + if reqs != "" { + tx = tx.Where("t.requirement LIKE ?", "%"+reqs+"%") + } + + if v := cast.ToInt64(jobType); v != 0 { + tx = tx.Where("p.job_type = ?", v) + } + + if v := cast.ToInt64(jobArea); v != 0 { + tx = tx.Where("p.job_area = ?", v) + } + + if v := cast.ToInt64(jobUnit); v != 0 { + tx = tx.Where("p.job_unit = ?", v) + } + + if err := tx.Count(&count).Error; err != nil { + return nil, 0, err + } + + if sort == "" { + tx = tx.Order(sort) + } else { + tx = tx.Order(sort) + } + + if err := tx.Offset((page - 1) * size).Limit(size).Select("p.*,t.*").Find(&list).Error; err != nil { + return nil, 0, err + } + + return list, count, nil +} + +func (r *Job) GetLang(id int64, lang string) (*model.JobPage, error) { + var res *model.JobPage + tx := r.db.Table(r.TableName() + " AS p ") + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s t ON p.id = t.job_id ", NewJobTranslations().TableName())) + tx = tx.Where("t.lang = ?", lang) + tx = tx.Where("p.id = ?", id) + + if err := tx.Select("p.*,t.*").First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + + return res, nil +} diff --git a/app/common/dao/job_translations.go b/app/common/dao/job_translations.go new file mode 100644 index 0000000..1199619 --- /dev/null +++ b/app/common/dao/job_translations.go @@ -0,0 +1,67 @@ +package dao + +import ( + "errors" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type JobTranslations struct { + db *gorm.DB +} + +func NewJobTranslations(db ...*gorm.DB) *JobTranslations { + return &JobTranslations{db: getDB(db...)} +} + +func (r *JobTranslations) TableName() string { + return "yt_job_translations" +} + +func (r *JobTranslations) Create(m *model.JobTranslations) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *JobTranslations) Update(m *model.JobTranslations) error { + return r.db.Table(r.TableName()).Where("job_id = ? AND lang = ?", m.JobID, m.Lang).Save(m).Error +} + +func (r *JobTranslations) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("job_id = ?", id).Delete(&JobTranslations{}).Error +} + +func (r *JobTranslations) Get(id int64) (*model.JobTranslations, error) { + var res model.JobTranslations + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *JobTranslations) GetByMaster(masterID int64, lang string) (*model.JobTranslations, error) { + var res model.JobTranslations + tx := r.db.Table(r.TableName()).Where("job_id = ? AND lang = ?", masterID, lang).First(&res) + if tx.Error != nil { + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, tx.Error + } + return &res, nil +} + +func (r *JobTranslations) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *JobTranslations) List(...any) ([]*model.JobTranslations, int64, error) { + panic("implement me") +} diff --git a/app/common/dao/news.go b/app/common/dao/news.go new file mode 100644 index 0000000..732cf24 --- /dev/null +++ b/app/common/dao/news.go @@ -0,0 +1,174 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type News struct { + db *gorm.DB +} + +func NewNews(db ...*gorm.DB) *News { + return &News{db: getDB(db...)} +} + +func (r *News) TableName() string { + return "yt_news" +} + +func (r *News) Create(m *model.News) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *News) Update(m *model.News) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *News) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&News{}).Error +} + +func (r *News) Get(id int64) (*model.News, error) { + var res model.News + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *News) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *News) List(lang, title, content, publishTime string, categoryID, pinned int64, page, size int, sort string) ([]*model.NewsPage, int64, error) { + var list []*model.NewsPage + var count int64 + tx := r.db.Table(r.TableName() + " AS p ") + tx = tx.Joins(fmt.Sprintf("LEFT JOIN %s AS t ON p.id = t.news_id ", NewNewsTranslations().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s l ON l.biz_id = p.id AND l.type = ?", NewCategoryRelation().TableName()), model.CateTypeNews) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s c ON c.id = l.category_id", NewCategory().TableName())) + if lang != "" { + tx = tx.Where("t.lang = ?", lang) + } + + if title != "" { + tx = tx.Where("t.title LIKE ?", "%"+title+"%") + } + + if content != "" { + tx = tx.Where("t.content LIKE ?", "%"+content+"%") + } + + if publishTime != "" { + tx = tx.Where("t.publish_time = ?", publishTime) + } + + if cast.ToInt64(categoryID) != 0 { + tx = tx.Where("c.id = ?", cast.ToInt64(categoryID)) + } + + if pinned > 0 { + tx = tx.Where("p.pinned = ?", pinned) + } + + if err := tx.Count(&count).Error; err != nil { + return nil, 0, err + } + + if sort != "" { + tx = tx.Order(sort) + } else { + tx = tx.Order("pinned desc,weight desc,create_time desc") + } + + if err := tx.Offset((page - 1) * size).Limit(size).Select("p.*,t.*,c.name as category_name,c.name_en as category_en,c.id as category_id").Find(&list).Error; err != nil { + return nil, 0, err + } + + return list, count, nil +} + +func (r *News) GetLang(id int64, lang string) (*model.NewsPage, error) { + var res *model.NewsPage + tx := r.db.Table(r.TableName() + " AS p ") + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s AS t ON p.id = t.news_id ", NewNewsTranslations().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s l ON l.biz_id = p.id AND l.type = ?", NewCategoryRelation().TableName()), model.CateTypeNews) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s c ON c.id = l.category_id", NewCategory().TableName())) + tx = tx.Where("p.id = ?", id) + tx = tx.Where("t.lang = ?", lang) + + if err := tx.Select("p.*,t.*,c.name as category_name,c.name_en as category_en,c.id as category_id").First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + + return res, nil +} + +func (r *News) GetN(categoryID int64, lang string, page, size int, sort string) ([]*model.NewsPage, error) { + var res []*model.NewsPage + tx := r.db.Table(r.TableName() + " AS p ") + tx = tx.Joins(fmt.Sprintf("LEFT JOIN %s AS t ON p.id = t.news_id ", NewNewsTranslations().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s l ON l.biz_id = p.id AND l.type = ?", NewCategoryRelation().TableName()), model.CateTypeNews) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s c ON c.id = l.category_id", NewCategory().TableName())) + tx = tx.Where("c.id = ?", cast.ToInt64(categoryID)) + tx = tx.Where("t.lang = ?", lang) + if sort != "" { + tx = tx.Order(sort) + } else { + tx = tx.Order("pinned desc,weight desc,create_time desc") + } + tx = tx.Offset((page - 1) * size).Limit(size) + if err := tx.Select("p.*,t.title").Find(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return res, nil +} + +func (r *News) PureAll() ([]*model.News, error) { + var res []*model.News + tx := r.db.Table(r.TableName()).Find(&res) + if tx.Error != nil { + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, tx.Error + } + + return res, nil +} + +func (r *News) Incr(id int64, counts ...int) error { + count := 1 + if len(counts) > 0 { + count = counts[0] + } + + tx := r.db.Table(r.TableName()).Where("id = ?", id).Updates(map[string]any{"count": gorm.Expr("count + ?", count)}) + if tx.Error != nil { + return tx.Error + } + + if tx.RowsAffected == 0 { + return gorm.ErrRecordNotFound + } + + return nil +} diff --git a/app/common/dao/news_translations.go b/app/common/dao/news_translations.go new file mode 100644 index 0000000..0433d2b --- /dev/null +++ b/app/common/dao/news_translations.go @@ -0,0 +1,67 @@ +package dao + +import ( + "errors" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type NewsTranslations struct { + db *gorm.DB +} + +func NewNewsTranslations(db ...*gorm.DB) *NewsTranslations { + return &NewsTranslations{db: getDB(db...)} +} + +func (r *NewsTranslations) TableName() string { + return "yt_news_translations" +} + +func (r *NewsTranslations) Create(m *model.NewsTranslations) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *NewsTranslations) Update(m *model.NewsTranslations) error { + return r.db.Table(r.TableName()).Where("news_id = ? AND lang = ?", m.NewsID, m.Lang).Save(m).Error +} + +func (r *NewsTranslations) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("news_id = ?", id).Delete(&NewsTranslations{}).Error +} + +func (r *NewsTranslations) Get(id int64) (*model.NewsTranslations, error) { + var res model.NewsTranslations + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *NewsTranslations) GetByMaster(masterID int64, lang string) (*model.NewsTranslations, error) { + var res model.NewsTranslations + tx := r.db.Table(r.TableName()).Where("news_id = ? AND lang = ?", masterID, lang).First(&res) + if tx.Error != nil { + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, tx.Error + } + return &res, nil +} + +func (r *NewsTranslations) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *NewsTranslations) List(...any) ([]*model.NewsTranslations, int64, error) { + panic("implement me") +} diff --git a/app/common/dao/page.go b/app/common/dao/page.go new file mode 100644 index 0000000..4719818 --- /dev/null +++ b/app/common/dao/page.go @@ -0,0 +1,87 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type Page struct { + db *gorm.DB +} + +func NewPage(db ...*gorm.DB) *Page { + return &Page{db: getDB(db...)} +} + +func (r *Page) TableName() string { + return "yt_page" +} + +func (r *Page) Create(m *model.Page) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *Page) Update(m *model.Page) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *Page) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&Page{}).Error +} + +func (r *Page) Get(id int64) (*model.Page, error) { + var res model.Page + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *Page) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *Page) List(name string, page, size int, sort string) ([]*model.Page, int64, error) { + var list []*model.Page + var count int64 + tx := r.db.Table(r.TableName()).Where("name LIKE ?", fmt.Sprintf("%%%s%%", name)) + if err := tx.Count(&count).Error; err != nil { + return nil, 0, err + } + + if sort != "" { + tx = tx.Order(sort) + } else { + tx = tx.Order("id,pid,weight desc") + } + + if err := tx.Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, 0, err + } + return nil, 0, err + } + return list, count, nil +} + +func (r *Page) GetByPath(path string) ([]*model.Page, error) { + var list []*model.Page + tx := r.db.Table(r.TableName()).Where("path LIKE ?", fmt.Sprintf("%s%%", path)).Find(&list) + if tx.Error != nil { + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, tx.Error + } + return list, nil +} diff --git a/app/common/dao/process.go b/app/common/dao/process.go new file mode 100644 index 0000000..a59c672 --- /dev/null +++ b/app/common/dao/process.go @@ -0,0 +1,114 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "github.com/spf13/cast" + "gorm.io/gorm" +) + +type Process struct { + db *gorm.DB +} + +func NewProcess(db ...*gorm.DB) *Process { + return &Process{db: getDB(db...)} +} + +func (r *Process) TableName() string { + return "yt_process" +} + +func (r *Process) Create(m *model.Process) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *Process) Update(m *model.Process) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *Process) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&Process{}).Error +} + +func (r *Process) Get(id int64) (*model.Process, error) { + var res model.Process + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *Process) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *Process) List(lang, title, content, processTime string, categoryID int64, page, size int, sort string) ([]*model.ProcessPage, int64, error) { + var list []*model.ProcessPage + var count int64 + tx := r.db.Table(r.TableName() + " AS p ").Joins(fmt.Sprintf("LEFT JOIN %s AS t ON p.id = t.process_id ", NewProcessTranslations().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s l ON l.biz_id = p.id AND l.type = ?", NewCategoryRelation().TableName()), model.CateTypeProcess) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s c ON c.id = l.category_id", NewCategory().TableName())) + if lang != "" { + tx = tx.Where("t.lang = ?", lang) + } + + if title != "" { + tx = tx.Where("t.title LIKE ?", "%"+title+"%") + } + + if content != "" { + tx = tx.Where("t.content LIKE ?", "%"+content+"%") + } + + if processTime != "" { + tx = tx.Where("t.process_time = ?", processTime) + } + + if cast.ToInt64(categoryID) != 0 { + tx = tx.Where("c.id = ?", cast.ToInt64(categoryID)) + } + + if err := tx.Count(&count).Error; err != nil { + return nil, 0, err + } + + if sort == "" { + tx = tx.Order(sort) + } else { + tx = tx.Order(sort) + } + + if err := tx.Offset((page - 1) * size).Limit(size).Select("p.*,t.*,c.name as category_name,c.name_en as category_en,c.id as category_id").Find(&list).Error; err != nil { + return nil, 0, err + } + + return list, count, nil +} + +func (r *Process) GetLang(lang string, id int64) (*model.ProcessPage, error) { + var res *model.ProcessPage + tx := r.db.Table(r.TableName() + " AS p ").Joins(fmt.Sprintf("LEFT JOIN %s AS t ON p.id = t.process_id ", NewProcessTranslations().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s l ON l.biz_id = p.id AND l.type = ?", NewCategoryRelation().TableName()), model.CateTypeProcess) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s c ON c.id = l.category_id", NewCategory().TableName())) + tx = tx.Where("t.lang = ?", lang) + tx = tx.Where("p.id = ?", id) + field := "p.*,t.*,c.name as category_name,c.name_en as category_en,c.id as category_id" + if err := tx.Select(field).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + + return res, nil +} diff --git a/app/common/dao/process_translations.go b/app/common/dao/process_translations.go new file mode 100644 index 0000000..1d17d1a --- /dev/null +++ b/app/common/dao/process_translations.go @@ -0,0 +1,67 @@ +package dao + +import ( + "errors" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type ProcessTranslations struct { + db *gorm.DB +} + +func NewProcessTranslations(db ...*gorm.DB) *ProcessTranslations { + return &ProcessTranslations{db: getDB(db...)} +} + +func (r *ProcessTranslations) TableName() string { + return "yt_process_translations" +} + +func (r *ProcessTranslations) Create(m *model.ProcessTranslations) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *ProcessTranslations) Update(m *model.ProcessTranslations) error { + return r.db.Table(r.TableName()).Where("process_id = ? AND lang = ?", m.ProcessID, m.Lang).Save(m).Error +} + +func (r *ProcessTranslations) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("process_id = ?", id).Delete(&ProcessTranslations{}).Error +} + +func (r *ProcessTranslations) Get(id int64) (*model.ProcessTranslations, error) { + var res model.ProcessTranslations + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *ProcessTranslations) GetByMaster(masterID int64, lang string) (*model.ProcessTranslations, error) { + var res model.ProcessTranslations + tx := r.db.Table(r.TableName()).Where("process_id = ? AND lang = ?", masterID, lang).First(&res) + if tx.Error != nil { + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, tx.Error + } + return &res, nil +} + +func (r *ProcessTranslations) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *ProcessTranslations) List(...any) ([]*model.ProcessTranslations, int64, error) { + panic("implement me") +} diff --git a/app/common/dao/resource.go b/app/common/dao/resource.go new file mode 100644 index 0000000..e69cef9 --- /dev/null +++ b/app/common/dao/resource.go @@ -0,0 +1,126 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type Resource struct { + db *gorm.DB +} + +func NewResource(db ...*gorm.DB) *Resource { + return &Resource{db: getDB(db...)} +} + +func (r *Resource) TableName() string { + return "yt_resource" +} + +func (r *Resource) Create(m *model.Resource) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *Resource) Update(m *model.Resource) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *Resource) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&Resource{}).Error +} + +func (r *Resource) Get(id int64) (*model.Resource, error) { + var res model.Resource + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *Resource) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *Resource) List(name, types, code, method, path string, page, size int, sort string) ([]*model.Resource, int64, error) { + var res []*model.Resource + var count int64 + tx := r.db.Table(r.TableName()) + if name != "" { + tx = tx.Where("name LIKE ?", fmt.Sprintf("%%%s%%", name)) + } + + if types != "" { + tx = tx.Where("type = ?", types) + } + + if path != "" { + tx = tx.Where("path LIKE ?", fmt.Sprintf("%%%s%%", path)) + } + + if method != "" { + tx = tx.Where("method = ?", method) + } + + if err := tx.Count(&count).Error; err != nil { + return nil, 0, err + } + + if sort != "" { + tx = tx.Order(sort) + } else { + tx = tx.Order("id desc") + } + + if err := tx.Offset((page - 1) * size).Limit(size).Find(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, 0, nil + } + return nil, 0, err + } + return res, count, nil +} + +func (r *Resource) GetByIds(ids []int64) ([]*model.Resource, error) { + var res []*model.Resource + if err := r.db.Table(r.TableName()).Where("id in (?)", ids).Find(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return res, nil +} + +func (r *Resource) RoleGet(roleId int64) ([]*model.Resource, error) { + var res []*model.Resource + tx := r.db.Table(NewRoleResource().TableName()+" as rr").Where("rr.role_id = ?", roleId) + tx = tx.Joins(fmt.Sprintf(" JOIN %s r ON r.id = rr.resource_id", r.TableName())) + if err := tx.Select("r.*").Order("id desc").Find(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return res, nil +} + +func (r *Resource) GetMaps(maps string) ([]*model.Resource, error) { + var res []*model.Resource + if err := r.db.Table(r.TableName()).Where("maps LIKE ?", fmt.Sprintf("%s/%%", maps)).Find(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return res, nil +} diff --git a/app/common/dao/role.go b/app/common/dao/role.go new file mode 100644 index 0000000..29a87ee --- /dev/null +++ b/app/common/dao/role.go @@ -0,0 +1,83 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type Role struct { + db *gorm.DB +} + +func NewRole(db ...*gorm.DB) *Role { + return &Role{db: getDB(db...)} +} + +func (r *Role) TableName() string { + return "yt_role" +} + +func (r *Role) Create(m *model.Role) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *Role) Update(m *model.Role) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *Role) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&Role{}).Error +} + +func (r *Role) Get(id int64) (*model.Role, error) { + var res model.Role + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *Role) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *Role) List(name, desc string, page, size int, sort string) ([]*model.Role, int64, error) { + var res []*model.Role + var count int64 + tx := r.db.Table(r.TableName()) + if name != "" { + tx = tx.Where("name LIKE ?", fmt.Sprintf("%%%s%%", name)) + } + + if desc != "" { + tx = tx.Where("desc LIKE ?", fmt.Sprintf("%%%s%%", desc)) + } + + if err := tx.Count(&count).Error; err != nil { + return nil, 0, err + } + + if sort != "" { + tx = tx.Order(sort) + } else { + tx = tx.Order("id desc") + } + + if err := tx.Offset((page - 1) * size).Limit(size).Find(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, 0, nil + } + return nil, 0, err + } + return res, count, nil +} diff --git a/app/common/dao/role_resource.go b/app/common/dao/role_resource.go new file mode 100644 index 0000000..282a549 --- /dev/null +++ b/app/common/dao/role_resource.go @@ -0,0 +1,55 @@ +package dao + +import ( + "errors" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type RoleResource struct { + db *gorm.DB +} + +func NewRoleResource(db ...*gorm.DB) *RoleResource { + return &RoleResource{db: getDB(db...)} +} + +func (r *RoleResource) TableName() string { + return "yt_role_resource" +} + +func (r *RoleResource) Create(m []*model.RoleResource) error { + return r.db.Table(r.TableName()).CreateInBatches(m, 100).Error +} + +func (r *RoleResource) Update(m *model.RoleResource) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *RoleResource) Delete(roleID int64) error { + return r.db.Table(r.TableName()).Where("role_id = ?", roleID).Delete(&RoleResource{}).Error +} + +func (r *RoleResource) Get(id int64) (*model.RoleResource, error) { + var res model.RoleResource + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *RoleResource) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *RoleResource) List(...any) ([]*model.RoleResource, int64, error) { + panic("implement me") +} diff --git a/app/common/dao/user.go b/app/common/dao/user.go new file mode 100644 index 0000000..c8b033c --- /dev/null +++ b/app/common/dao/user.go @@ -0,0 +1,139 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type User struct { + db *gorm.DB +} + +func NewUser(db ...*gorm.DB) *User { + return &User{db: getDB(db...)} +} + +func (r *User) TableName() string { + return "yt_user" +} + +func (r *User) Create(m *model.User) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *User) Update(m *model.User) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *User) Delete(id int64) error { + return r.db.Table(r.TableName()).Where("id = ?", id).Delete(&User{}).Error +} + +func (r *User) BatchDelete(ids []int64) error { + return r.db.Table(r.TableName()).Where("id in (?)", ids).Delete(&User{}).Error +} + +func (r *User) Get(id int64) (*model.User, error) { + var res model.User + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *User) AccountGet(account string) (*model.User, error) { + var res model.User + if err := r.db.Table(r.TableName()).Where("account = ?", account).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *User) GetWithRole(account string) (*model.UserWithRole, error) { + var res *model.UserWithRole + tx := r.db.Table(r.TableName()+" as u ").Where("u.account = ?", account).Where("u.status = ?", model.UserStatusNormal) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s AS ur ON ur.user_id = u.id", NewUserRole().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s AS r ON r.id = ur.role_id", NewRole().TableName())) + + if err := tx.Select("u.*,r.id as role_id,r.name as role_name").First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return res, nil +} + +func (r *User) GetUserId(userId int64) (*model.UserWithRole, error) { + var res *model.UserWithRole + tx := r.db.Table(r.TableName()+" as u ").Where("u.id = ?", userId).Where("u.status = ?", model.UserStatusNormal) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s AS ur ON ur.user_id = u.id", NewUserRole().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s AS r ON r.id = ur.role_id", NewRole().TableName())) + + if err := tx.Select("u.*,r.id as role_id,r.name as role_name").First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return res, nil +} + +func (r *User) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *User) AccountExists(account string) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("account = ?", account).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *User) List(username, account string, role int64, page, size int, sort string) ([]*model.UserWithRole, int64, error) { + var res []*model.UserWithRole + var count int64 + tx := r.db.Table(r.TableName() + " as u") + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s AS ur ON ur.user_id = u.id", NewUserRole().TableName())) + tx = tx.Joins(fmt.Sprintf(" LEFT JOIN %s AS r ON r.id = ur.role_id", NewRole().TableName())) + + if username != "" { + tx = tx.Where("u.username LIKE ?", fmt.Sprintf("%%%s%%", username)) + } + + if account != "" { + tx = tx.Where("u.account LIKE ?", fmt.Sprintf("%%%s%%", account)) + } + + if role != 0 { + tx = tx.Where("r.role = ?", role) + } + + if err := tx.Count(&count).Error; err != nil { + return nil, 0, err + } + + tx = tx.Order("id DESC") + if sort != "" { + tx = tx.Order(sort) + } + + if err := tx.Limit(size).Offset((page - 1) * size).Select("u.*,r.id as role_id,r.name as role_name").Find(&res).Error; err != nil { + return nil, 0, err + } + return res, count, nil +} diff --git a/app/common/dao/user_role.go b/app/common/dao/user_role.go new file mode 100644 index 0000000..af72ce9 --- /dev/null +++ b/app/common/dao/user_role.go @@ -0,0 +1,70 @@ +package dao + +import ( + "errors" + "fmt" + "yintai-api/app/common/model" + + "gorm.io/gorm" +) + +type UserRole struct { + db *gorm.DB +} + +func NewUserRole(db ...*gorm.DB) *UserRole { + return &UserRole{db: getDB(db...)} +} + +func (r *UserRole) TableName() string { + return "yt_user_role" +} + +func (r *UserRole) Create(m *model.UserRole) error { + return r.db.Table(r.TableName()).Create(m).Error +} + +func (r *UserRole) Update(m *model.UserRole) error { + return r.db.Table(r.TableName()).Save(m).Error +} + +func (r *UserRole) Delete(userID int64) error { + return r.db.Table(r.TableName()).Where("user_id = ? ", userID).Delete(&UserRole{}).Error +} + +func (r *UserRole) RoleDelete(roleID int64) error { + return r.db.Table(r.TableName()).Where("role_id = ? ", roleID).Delete(&UserRole{}).Error +} + +func (r *UserRole) Get(id int64) (*model.UserRole, error) { + var res model.UserRole + if err := r.db.Table(r.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + +func (r *UserRole) Exists(id int64) (bool, error) { + var count int64 + if err := r.db.Table(r.TableName()).Where("id = ?", id).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *UserRole) List(...any) ([]*model.UserRole, int64, error) { + panic("implement me") +} + +func (r *UserRole) GetAccount(roleID int64) ([]*model.User, error) { + var res []*model.User + tx := r.db.Table(r.TableName()+" as ur ").Where("ur.role_id = ? ", roleID) + tx = tx.Joins(fmt.Sprintf(" JOIN %s u ON u.id = ur.user_id", NewUser().TableName())) + if err := tx.Order("id desc").Select("u.*").Find(&res).Error; err != nil { + return nil, err + } + return res, nil +} diff --git a/app/common/err/err.go b/app/common/err/err.go new file mode 100644 index 0000000..3bfc5fc --- /dev/null +++ b/app/common/err/err.go @@ -0,0 +1,51 @@ +package err + +import ( + "yintai-api/base/config" + + "github.com/gogap/errors" +) + +func errCatch(template errors.ErrCodeTemplate, err ...interface{}) { + panic(template.New().Append(err...)) +} + +func ParamErr(err ...interface{}) { + errCatch(config.ErrParam, err) +} + +func DbErr(err ...interface{}) { + errCatch(config.ErrDb, err) +} + +func PriErr(err ...interface{}) { + errCatch(config.ErrPriv, err) +} + +func EsxErr(err ...interface{}) { + errCatch(config.ErrExist, err) +} + +func NotEsxErr(err ...interface{}) { + errCatch(config.ErrNotExists, err) +} + +func InnerErr(err ...interface{}) { + errCatch(config.ErrInternal, err) +} + +func TokenErr(err ...interface{}) { + errCatch(config.ErrNeedLogin, err) +} + +func ExtNotSupportErr(err ...interface{}) { + errCatch(config.ErrExt, err) +} + +func TranslateErr(err ...interface{}) { + errCatch(config.TranslateErr, err) +} + +func InUseErr(err ...interface{}) { + errCatch(config.InUseErr, err) +} diff --git a/app/common/middleware/recovery.go b/app/common/middleware/recovery.go new file mode 100644 index 0000000..c8e793f --- /dev/null +++ b/app/common/middleware/recovery.go @@ -0,0 +1,36 @@ +package middleware + +import ( + "fmt" + "net/http" + "runtime" + "yintai-api/app/common/response" + + "github.com/gin-gonic/gin" + "github.com/gogap/errors" + log "github.com/sirupsen/logrus" +) + +func Recovery(ctx *gin.Context) { + defer func() { + if err := recover(); err != nil { + var rsp response.BaseResponse + if e, ok := err.(errors.ErrCode); ok { + rsp.Code = int(e.Code()) + rsp.Message = e.Error() + ctx.JSON(http.StatusOK, rsp) + log.Errorf("[%s][%s][%s] %v", ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.URL.RawQuery, e) + } else { + var buf [2 << 10]byte + stack := string(buf[:runtime.Stack(buf[:], true)]) + log.Errorf("[%s][%s][%s] Internal ERROR:::%v stack:%s", ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.URL.RawQuery, err, stack) + rsp.Code = -1 + rsp.Message = fmt.Sprintf("%v", err) + ctx.JSON(http.StatusOK, rsp) + } + ctx.Abort() + } + }() + + ctx.Next() +} diff --git a/app/common/model/base.go b/app/common/model/base.go new file mode 100644 index 0000000..dd0184c --- /dev/null +++ b/app/common/model/base.go @@ -0,0 +1,10 @@ +package model + +const ( + UserStatusNormal = 1 + UserStatusBaned = 2 +) + +const ( + Salt string = "yintai_api" +) diff --git a/app/common/model/category.go b/app/common/model/category.go new file mode 100644 index 0000000..776d8ae --- /dev/null +++ b/app/common/model/category.go @@ -0,0 +1,61 @@ +package model + +type Category struct { + ID int64 + Pid int64 + Name string + NameEn string + Path string + Type string + CreateTime int64 + UpdateTime int64 +} + +func (r *Category) Format() map[string]any { + res := make(map[string]any) + res["id"] = r.ID + res["pid"] = r.Pid + res["name"] = r.Name + res["name_en"] = r.NameEn + + return res +} + +func (r *Category) FormatEn() map[string]any { + res := make(map[string]any) + res["id"] = r.ID + res["pid"] = r.Pid + res["name"] = r.NameEn + + return res +} + +const ( + CateTypeProcess = "process" // 历程 + CateTypeNews = "news" // 新闻 + CateTypeLogic = "logic" // + CateTypeFile = "file" // 文件 + + CateTypeJob = "job" // 工作 + CateTypeJobType = "job_type" // 职业类别 + CateTypeJobArea = "job_area" // 业务领域 + CateTypeJobUnit = "job_unit" //所属板块 + +) + +var CategoryTypes = []string{ + CateTypeProcess, + CateTypeNews, + CateTypeJob, + CateTypeLogic, + CateTypeFile, + CateTypeJobType, + CateTypeJobArea, + CateTypeJobUnit, +} + +var CateJobTypes = []string{ + CateTypeJobType, + CateTypeJob, + CateTypeJobArea, +} diff --git a/app/common/model/category_relation.go b/app/common/model/category_relation.go new file mode 100644 index 0000000..f6cbcea --- /dev/null +++ b/app/common/model/category_relation.go @@ -0,0 +1,15 @@ +package model + +type CategoryRelation struct { + ID int64 + CategoryId int64 + Type string + BizId int64 + CreateTime int64 +} + +func (r *CategoryRelation) Format() map[string]any { + res := make(map[string]any) + + return res +} diff --git a/app/common/model/config.go b/app/common/model/config.go new file mode 100644 index 0000000..96647f9 --- /dev/null +++ b/app/common/model/config.go @@ -0,0 +1,14 @@ +package model + +type Config struct { + ID int64 + Name string + CreateTime int64 + UpdateTime int64 +} + +func (r *Config) Format() map[string]any { + res := make(map[string]any) + + return res +} diff --git a/app/common/model/config_translations.go b/app/common/model/config_translations.go new file mode 100644 index 0000000..f01be8a --- /dev/null +++ b/app/common/model/config_translations.go @@ -0,0 +1,13 @@ +package model + +type ConfigTranslations struct { + ConfigID int64 + Lang string + Config string +} + +func (r *ConfigTranslations) Format() map[string]any { + res := make(map[string]any) + + return res +} diff --git a/app/common/model/document.go b/app/common/model/document.go new file mode 100644 index 0000000..3b20225 --- /dev/null +++ b/app/common/model/document.go @@ -0,0 +1,51 @@ +package model + +import ( + "time" + "yintai-api/app/common/service" +) + +type Document struct { + ID int64 + Code string + Weight int64 + CreateTime int64 + UpdateTime int64 +} + +type DocumentPage struct { + Document + DocumentTranslations + CategoryID int64 + CategoryName string + CategoryEn string +} + +func (d *DocumentPage) Format() map[string]any { + res := make(map[string]any) + res["id"] = d.ID + res["code"] = d.Code + res["create_time"] = time.Now().Format("2006-01-02 15:04:05") + res["lang"] = d.Lang + res["name"] = d.Name + res["title"] = d.Title + res["sub_title"] = d.SubTitle + res["image_title"] = d.ImageTitle + res["desc"] = d.Description + res["path"] = service.NewQiniu().QuarterUrl(d.StoragePath) + res["cover"] = service.NewQiniu().QuarterUrl(d.Cover) + res["extra"] = d.Extra + res["category_id"] = d.CategoryID + res["category_name"] = d.CategoryName + res["categroy_name_en"] = d.CategoryEn + res["weight"] = d.Weight + + return res + +} + +func (r *Document) Format() map[string]any { + res := make(map[string]any) + + return res +} diff --git a/app/common/model/document_translations.go b/app/common/model/document_translations.go new file mode 100644 index 0000000..27053f0 --- /dev/null +++ b/app/common/model/document_translations.go @@ -0,0 +1,22 @@ +package model + +type DocumentTranslations struct { + DocumentID int64 + Lang string + Name string + Title string + SubTitle string + ImageTitle string + Description string + StorageType string + StoragePath string + Size int64 + Cover string + Extra string +} + +func (r *DocumentTranslations) Format() map[string]any { + res := make(map[string]any) + + return res +} diff --git a/app/common/model/job.go b/app/common/model/job.go new file mode 100644 index 0000000..bdfceb5 --- /dev/null +++ b/app/common/model/job.go @@ -0,0 +1,42 @@ +package model + +import ( + "time" +) + +type Job struct { + ID int64 + RecruitCount int64 + City string + JobType int64 + JobArea int64 + JobUnit int64 + CreateTime int64 + UpdateTime int64 +} + +func (r *Job) Format() map[string]any { + res := make(map[string]any) + + return res +} + +type JobPage struct { + Job + JobTranslations +} + +func (r *JobPage) Format() map[string]any { + res := make(map[string]any) + res["id"] = r.ID + res["city"] = r.City + res["recruit_count"] = r.RecruitCount + res["create_time"] = time.Unix(r.CreateTime, 0).Format("2006-01-02 15:04:05") + res["lang"] = r.Lang + res["title"] = r.Name + res["description"] = r.Description + res["requirement"] = r.Requirement + res["contract_info"] = r.ContractInfo + + return res +} diff --git a/app/common/model/job_translations.go b/app/common/model/job_translations.go new file mode 100644 index 0000000..0cbffe9 --- /dev/null +++ b/app/common/model/job_translations.go @@ -0,0 +1,16 @@ +package model + +type JobTranslations struct { + JobID int64 + Lang string + Name string + Description string + Requirement string + ContractInfo string +} + +func (r *JobTranslations) Format() map[string]any { + res := make(map[string]any) + + return res +} diff --git a/app/common/model/news.go b/app/common/model/news.go new file mode 100644 index 0000000..f0f491e --- /dev/null +++ b/app/common/model/news.go @@ -0,0 +1,61 @@ +package model + +import ( + "encoding/json" + "time" + "yintai-api/app/common/service" +) + +type News struct { + ID int64 + CoverImages string + CoverShow string + Count int64 + CreateTime int64 + UpdateTime int64 + Weight int64 + Pinned int64 +} + +func (r *News) Format() map[string]any { + res := make(map[string]any) + + return res +} + +type NewsPage struct { + News + NewsTranslations + CategoryID int64 + CategoryName string + CategoryEn string +} + +func (r *NewsPage) Format() map[string]any { + res := make(map[string]any) + images := make(map[string][]string, 0) + _ = json.Unmarshal([]byte(r.CoverImages), &images) + for k, imgs := range images { + for idx, img := range imgs { + img = service.NewQiniu().QuarterUrl(img) + images[k][idx] = img + } + } + + res["id"] = r.ID + res["covers"] = images + res["covers_show"] = r.CoverShow + res["count"] = r.Count + res["create_time"] = time.Unix(r.CreateTime, 0).Format("2006-01-02 15:04:05") + res["lang"] = r.Lang + res["title"] = r.Title + res["content"] = r.Content + res["publish_time"] = r.PublishTime + res["category_id"] = r.CategoryID + res["category_name"] = r.CategoryName + res["category_name_en"] = r.CategoryEn + res["weight"] = r.Weight + res["pinned"] = r.Pinned + + return res +} diff --git a/app/common/model/news_translations.go b/app/common/model/news_translations.go new file mode 100644 index 0000000..a777175 --- /dev/null +++ b/app/common/model/news_translations.go @@ -0,0 +1,15 @@ +package model + +type NewsTranslations struct { + NewsID int64 + Lang string + Title string + PublishTime string + Content string +} + +func (r *NewsTranslations) Format() map[string]any { + res := make(map[string]any) + + return res +} diff --git a/app/common/model/page.go b/app/common/model/page.go new file mode 100644 index 0000000..5074108 --- /dev/null +++ b/app/common/model/page.go @@ -0,0 +1,30 @@ +package model + +import "time" + +type Page struct { + ID int64 + Name string + Page string + Pid int64 + Path string + Weight int64 + Content string + Tags string + CreateTime int64 + UpdateTime int64 +} + +func (r *Page) Format() map[string]any { + res := make(map[string]any) + res["id"] = r.ID + res["name"] = r.Name + res["page"] = r.Page + res["pid"] = r.Pid + res["path"] = r.Path + res["weight"] = r.Weight + res["content"] = r.Content + res["tags"] = r.Tags + res["create_time"] = time.Unix(r.CreateTime, 0).Format("2006-01-02 15:04:05") + return res +} diff --git a/app/common/model/process.go b/app/common/model/process.go new file mode 100644 index 0000000..8d21012 --- /dev/null +++ b/app/common/model/process.go @@ -0,0 +1,43 @@ +package model + +import ( + "encoding/json" + "time" + "yintai-api/app/common/service" +) + +type Process struct { + ID int64 + CreateTime int64 + UpdateTime int64 +} + +type ProcessPage struct { + Process + ProcessTranslations + CategoryID int64 + CategoryName string + CategoryEn string +} + +func (r *ProcessPage) Format() map[string]any { + res := make(map[string]any) + images := make([]string, 0) + _ = json.Unmarshal([]byte(r.Images), &images) + for _, img := range images { + img = service.NewQiniu().QuarterUrl(img) + images = append(images, img) + } + res["images"] = images + res["id"] = r.ID + res["create_time"] = time.Unix(r.CreateTime, 0).Format("2006-01-02 15:04:05") + res["lang"] = r.Lang + res["title"] = r.Title + res["content"] = r.Content + res["process_time"] = r.ProcessTime + res["category_id"] = r.CategoryID + res["category_name"] = r.CategoryName + res["category_name_en"] = r.CategoryEn + + return res +} diff --git a/app/common/model/process_translations.go b/app/common/model/process_translations.go new file mode 100644 index 0000000..44f9cda --- /dev/null +++ b/app/common/model/process_translations.go @@ -0,0 +1,33 @@ +package model + +import ( + "encoding/json" + "yintai-api/app/common/service" +) + +type ProcessTranslations struct { + ProcessID int64 + Lang string + Title string + Images string + Content string + ProcessTime string +} + +func (r *ProcessTranslations) Format() map[string]any { + res := make(map[string]any) + images := make([]string, 0) + _ = json.Unmarshal([]byte(r.Images), &images) + for _, img := range images { + img = service.NewQiniu().QuarterUrl(img) + images = append(images, img) + } + res["process_id"] = r.ProcessID + res["images"] = images + res["lang"] = r.Lang + res["title"] = r.Title + res["content"] = r.Content + res["process_time"] = r.ProcessTime + + return res +} diff --git a/app/common/model/resource.go b/app/common/model/resource.go new file mode 100644 index 0000000..6ac6e23 --- /dev/null +++ b/app/common/model/resource.go @@ -0,0 +1,35 @@ +package model + +import "time" + +type Resource struct { + ID int64 + Name string + Type string + Code string + Path string + Method string + Pid int64 + Maps string + CreateTime int64 + UpdateTime int64 +} + +const ( + ResourceTypeApi = "api" + ResourceTypePage = "page" +) + +func (r *Resource) Format() map[string]any { + res := make(map[string]any) + res["id"] = r.ID + res["name"] = r.Name + res["type"] = r.Type + res["path"] = r.Path + //res["code"] = r.Code + res["pid"] = r.Pid + res["method"] = r.Method + res["create_time"] = time.Unix(r.CreateTime, 0).Format("2006-01-02 15:04:05") + + return res +} diff --git a/app/common/model/role.go b/app/common/model/role.go new file mode 100644 index 0000000..0894d38 --- /dev/null +++ b/app/common/model/role.go @@ -0,0 +1,21 @@ +package model + +import "time" + +type Role struct { + ID int64 + Name string + Desc string + CreateTime int64 + UpdateTime int64 +} + +func (r *Role) Format() map[string]any { + res := make(map[string]any) + res["id"] = r.ID + res["name"] = r.Name + res["desc"] = r.Desc + res["create_time"] = time.Unix(r.CreateTime, 0).Format("2006-01-02 15:04:05") + + return res +} diff --git a/app/common/model/role_resource.go b/app/common/model/role_resource.go new file mode 100644 index 0000000..05e18bb --- /dev/null +++ b/app/common/model/role_resource.go @@ -0,0 +1,13 @@ +package model + +type RoleResource struct { + ID int64 + RoleID int64 + ResourceID int64 +} + +func (r *RoleResource) Format() map[string]any { + res := make(map[string]any) + + return res +} diff --git a/app/common/model/user.go b/app/common/model/user.go new file mode 100644 index 0000000..d6f91ff --- /dev/null +++ b/app/common/model/user.go @@ -0,0 +1,38 @@ +package model + +import "time" + +type User struct { + ID int64 + Username string + Account string + Password string + Status int64 + CreateTime int64 + UpdateTime int64 +} + +type UserWithRole struct { + User + RoleID int64 + RoleName string +} + +func (u *User) Format() map[string]any { + res := make(map[string]any) + res["id"] = u.ID + res["username"] = u.Username + res["account"] = u.Account + res["status"] = u.Status + res["create_time"] = time.Unix(u.CreateTime, 0).Format("2006-01-02 15:04:05") + + return res +} + +func (u *UserWithRole) Format() map[string]any { + res := u.User.Format() + res["role_id"] = u.RoleID + res["role_name"] = u.RoleName + + return res +} diff --git a/app/common/model/user_role.go b/app/common/model/user_role.go new file mode 100644 index 0000000..9cbd140 --- /dev/null +++ b/app/common/model/user_role.go @@ -0,0 +1,7 @@ +package model + +type UserRole struct { + ID int64 + UserID int64 + RoleID int64 +} diff --git a/app/common/request/base.go b/app/common/request/base.go new file mode 100644 index 0000000..9804474 --- /dev/null +++ b/app/common/request/base.go @@ -0,0 +1,17 @@ +package request + +type Paginate struct { + Page int `form:"page"` + Size int `form:"size"` + Sort string `form:"sort"` +} + +func (p *Paginate) Pagination() { + if p.Page <= 0 { + p.Page = 1 + } + + if p.Size <= 0 { + p.Size = 10 + } +} diff --git a/app/common/response/response.go b/app/common/response/response.go new file mode 100644 index 0000000..5486159 --- /dev/null +++ b/app/common/response/response.go @@ -0,0 +1,66 @@ +package response + +import ( + "encoding/json" +) + +type BaseResponse struct { + Code int `json:"code"` + Message string `json:"message"` +} + +type CommonResponse struct { + BaseResponse + Data interface{} `json:"data"` +} + +type RawResponse struct { + BaseResponse + Data json.RawMessage `json:"data"` +} + +type ListResponse struct { + BaseResponse + Data struct { + Total int64 `json:"total"` + TotalAmount string `json:"total_amount,omitempty"` + TotalSize string `json:"total_size,omitempty"` + Items interface{} `json:"items"` + } `json:"data,omitempty"` +} + +var ( + rspOk *BaseResponse +) + +func init() { + rspOk = new(BaseResponse) + rspOk.Message = "OK" + rspOk.Code = 0 +} + +func NewRspOk() *BaseResponse { + return rspOk +} + +func NewListRsp(total int64, items interface{}) *ListResponse { + rsp := &ListResponse{ + BaseResponse: BaseResponse{ + Code: rspOk.Code, + Message: rspOk.Message, + }, + } + rsp.Data.Total = total + rsp.Data.Items = items + return rsp +} + +func NewRsp(data interface{}) *CommonResponse { + return &CommonResponse{ + BaseResponse: BaseResponse{ + Code: rspOk.Code, + Message: rspOk.Message, + }, + Data: data, + } +} diff --git a/app/common/service/qiniu.go b/app/common/service/qiniu.go new file mode 100644 index 0000000..7d87328 --- /dev/null +++ b/app/common/service/qiniu.go @@ -0,0 +1,97 @@ +package service + +import ( + "fmt" + "net/url" + "strings" + "time" + err2 "yintai-api/app/common/err" + "yintai-api/base/global" + "yintai-api/base/global/storage" +) + +type Qiniu struct{} + +func NewQiniu() *Qiniu { + return &Qiniu{} +} + +func (q Qiniu) Save(savePath, objectKey string) string { + cdnType := "qiniu" + err := global.GetStorage(cdnType).Put(savePath, objectKey, nil) + if err != nil { + s := fmt.Sprintf("upload file[%s] objeckt[%s] error :%s", savePath, objectKey, err.Error()) + err2.InnerErr(s, err) + } + + return objectKey +} + +func (q Qiniu) Delete(objectName string) error { + storage := global.GetStorage("qiniu") + if storage == nil { + err2.InnerErr(" storage not exists") + } + + if err := storage.Del(objectName); err != nil { + if strings.Contains(err.Error(), "no such file") { + return nil + } + + return err + } + + return nil +} + +func (q Qiniu) QuarterUrl(objectKey string) string { + return global.GetStorage("qiniu").Url(objectKey, time.Hour/4) +} + +func (q Qiniu) ForeverUrl(objectKey string) string { + return global.GetStorage("qiniu").Url(objectKey, 0) +} + +func (q Qiniu) Url(objectKey string, expire time.Duration) string { + if objectKey == "" { + return "" + } + + return global.GetStorage("qiniu").Url(objectKey, expire) +} + +func (q Qiniu) PublicUrl(objectKey string) string { + return q.Url(objectKey, 0) +} + +func (q Qiniu) Stat(objectKey string) (*storage.ObjectInfo, error) { + return global.GetStorage("qiniu").Stat(objectKey) +} + +func (q Qiniu) ObjectKey(s string) string { + s = strings.TrimSpace(s) + if len(s) == 0 { + return "" + } + + s, _ = url.QueryUnescape(s) + + domain := global.GetConfig().Storage.QiniuDomain + start := strings.Index(s, domain) + if start == -1 { + start = 0 + } else { + start = len(domain) + 1 + } + end := strings.Index(s, "?") + if end == -1 { + end = len(s) + } + + p := s[start:end] + return p +} + +func (q Qiniu) UploadToken() string { + return global.GetStorage().UploadToken() +} diff --git a/app/common/service/translate.go b/app/common/service/translate.go new file mode 100644 index 0000000..6aec3eb --- /dev/null +++ b/app/common/service/translate.go @@ -0,0 +1,102 @@ +package service + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "yintai-api/base/config" + + "github.com/tidwall/gjson" +) + +type BaiduTranslate struct { + APIKey string + SecretKey string +} + +func NewBaiduTranslate() *BaiduTranslate { + return &BaiduTranslate{ + APIKey: config.GetConfig().Baidu.TranslateApiKey, + SecretKey: config.GetConfig().Baidu.TranslateSecret, + } +} + +func (t *BaiduTranslate) accessToken() (string, error) { + client := &http.Client{} + target := fmt.Sprintf("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s&", t.APIKey, t.SecretKey) + request, err := http.NewRequest("GET", target, nil) + if err != nil { + return "", err + } + + resp, err := client.Do(request) + if err != nil { + return "", err + } + + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + accessToken := gjson.ParseBytes(body).Get("access_token").String() + if accessToken == "" { + return "", fmt.Errorf("access token is empty") + } + return accessToken, nil +} + +// ZH2EN zh to en +func (t *BaiduTranslate) ZH2EN(text string) (string, error) { + return t.Translate(text, "zh", "en") +} + +func (t *BaiduTranslate) Translate(text, from, to string) (string, error) { + if len(text) == 0 { + return "", nil + } + + token, err := t.accessToken() + if err != nil { + return "", err + } + + client := &http.Client{} + target := fmt.Sprintf("https://aip.baidubce.com/rpc/2.0/mt/texttrans/v1?access_token=%s", token) + params := make(map[string]string) + params["from"] = from + params["to"] = to + params["q"] = text + + body, err := json.Marshal(params) + if err != nil { + return "", fmt.Errorf("json marshal error: %w", err) + } + req, err := http.NewRequest("POST", target, strings.NewReader(string(body))) + if err != nil { + return "", err + } + + resp, err := client.Do(req) + if err != nil { + return "", err + } + + defer resp.Body.Close() + body, err = io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + result := gjson.ParseBytes(body) + var res string + for _, v := range result.Get("result.trans_result").Array() { + res += gjson.Parse(v.String()).Get("dst").String() + } + if len(res) == 0 { + return "", fmt.Errorf("translate error") + } + return res, nil +} diff --git a/app/common/utils/utils.go b/app/common/utils/utils.go new file mode 100644 index 0000000..1875066 --- /dev/null +++ b/app/common/utils/utils.go @@ -0,0 +1,94 @@ +package utils + +import ( + "fmt" + "math/rand/v2" + "strings" + "time" + "yintai-api/app/common/cache" + "yintai-api/app/common/dao" + "yintai-api/app/common/model" + "yintai-api/base/config" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cast" +) + +func Distinct[T comparable](params []T) []T { + seed := make(map[T]struct{}) + res := make([]T, 0, len(params)) + for _, param := range params { + if _, ok := seed[param]; ok { + continue + } + res = append(res, param) + } + + return res +} + +func SourceFormat(method, urls string) string { + member := fmt.Sprintf("%s_%s", method, urls) + member = strings.TrimRight(member, "/") + member = strings.Replace(member, "/", "_", -1) + return member +} + +func PermissionInit() { + roles, _, err := dao.NewRole().List("", "", 1, 999999, "") + if err != nil { + panic(config.ErrDb.New()) + } + + for _, role := range roles { + if err = LazyLoadPermission(role.ID); err != nil { + panic(config.ErrDb.New().Append("init permission err:" + err.Error())) + } + } +} + +// LazyLoadPermission 只有当角色的权限发生变化时,才需要。 +// 比如新增,修改,删除 +func LazyLoadPermission(roleID int64) error { + resources, err := dao.NewResource().RoleGet(roleID) + if err != nil { + return err + } + + res := make([]string, 0) + for _, resource := range resources { + if resource.Type != model.ResourceTypeApi { + continue + } + res = append(res, SourceFormat(resource.Method, resource.Path)) + } + + redix := cache.GetRedis() + key := cache.NewKeys().Api(roleID) + if len(res) == 0 { + res = append(res, " ") + } + return redix.SAdd(key, res).Err() +} + +func AutoIncrementNews() { + ticker := time.NewTicker(time.Hour * 18) + defer ticker.Stop() + run := func() { + all, err := dao.NewNews().PureAll() + if err != nil { + log.Errorf("auto increment news: query failed: %v", err) + return + } + + for _, v := range all { + if err := dao.NewNews().Incr(cast.ToInt64(v.ID), rand.IntN(9)+1); err != nil { + log.Errorf("auto increment news: incr news id=%v failed: %v", v.ID, err) + } + } + } + + for range ticker.C { + run() + } +} diff --git a/app/routers/admin_router.go b/app/routers/admin_router.go new file mode 100644 index 0000000..cd4ad90 --- /dev/null +++ b/app/routers/admin_router.go @@ -0,0 +1,88 @@ +package routers + +import ( + "yintai-api/app/admin/controller" + "yintai-api/app/admin/middleware" + middleware2 "yintai-api/app/common/middleware" + + "github.com/gin-gonic/gin" +) + +// 管理接口路由 +func initAdminRouter(engine *gin.Engine) { + // token 管理 + noAdminGroup := engine.Group("/yt") + adminGroup := engine.Group("/yt") + + adminGroup.Use(middleware2.Recovery).Use(middleware.Login).Use(middleware.Permission()) + noAdminGroup.Use(middleware2.Recovery) + + user := new(controller.User) + adminGroup.POST("/user", user.Create) + adminGroup.GET("/user", user.List) + adminGroup.PUT("/user", user.Update) + adminGroup.DELETE("/user", user.Delete) + adminGroup.GET("/user/logout", user.Logout) + noAdminGroup.POST("/user/login", user.Login) + + resource := new(controller.Resource) + adminGroup.GET("/resource", resource.List) + adminGroup.PUT("/resource", resource.Update) + adminGroup.POST("/resource", resource.Create) + adminGroup.DELETE("/resource", resource.Delete) + + role := new(controller.Role) + adminGroup.GET("/role", role.List) + adminGroup.PUT("/role", role.Update) + adminGroup.POST("/role", role.Create) + adminGroup.DELETE("/role", role.Delete) + adminGroup.GET("/role/:id", role.Get) + adminGroup.GET("/role/account/:id", role.Accounts) + + translator := new(controller.Translate) + adminGroup.GET("/translate", translator.Translate2En) + + category := new(controller.Category) + adminGroup.GET("/category", category.List) + adminGroup.PUT("/category", category.Update) + adminGroup.POST("/category", category.Create) + adminGroup.DELETE("/category", category.Delete) + + process := new(controller.Process) + adminGroup.GET("/process", process.List) + adminGroup.PUT("/process", process.Update) + adminGroup.POST("/process", process.Create) + adminGroup.DELETE("/process", process.Delete) + adminGroup.GET("/process/:id", process.Get) + + news := new(controller.News) + adminGroup.GET("/news", news.List) + adminGroup.PUT("/news", news.Update) + adminGroup.POST("/news", news.Create) + adminGroup.DELETE("/news", news.Delete) + adminGroup.GET("/news/:id", news.Get) + + job := new(controller.Job) + adminGroup.GET("/job", job.List) + adminGroup.PUT("/job", job.Update) + adminGroup.POST("/job", job.Create) + adminGroup.DELETE("/job", job.Delete) + adminGroup.GET("/job/:id", job.Get) + + document := new(controller.Document) + adminGroup.GET("/doc", document.List) + adminGroup.PUT("/doc", document.Update) + adminGroup.POST("/doc", document.Create) + adminGroup.DELETE("/doc", document.Delete) + adminGroup.GET("/doc/:id", document.Get) + + page := new(controller.Page) + adminGroup.GET("/page", page.List) + adminGroup.PUT("/page", page.Update) + adminGroup.POST("/page", page.Create) + adminGroup.DELETE("/page", page.Delete) + + file := new(controller.Files) + adminGroup.POST("/upload", file.Upload) + adminGroup.GET("/upload/token", file.UploadToken) +} diff --git a/app/routers/api_router.go b/app/routers/api_router.go new file mode 100644 index 0000000..d16e0b8 --- /dev/null +++ b/app/routers/api_router.go @@ -0,0 +1,35 @@ +package routers + +import ( + "yintai-api/app/api/controller" + middleware2 "yintai-api/app/common/middleware" + + "github.com/gin-gonic/gin" +) + +// 客户端接口路由 +func initApiRouter(engine *gin.Engine) { + apiGroup := engine.Group("/yt/api") + apiGroup.Use(middleware2.Recovery) + + category := new(controller.Category) + apiGroup.GET("/category", category.List) + + process := new(controller.Process) + apiGroup.GET("/process", process.List) + + document := new(controller.Document) + apiGroup.GET("/doc", document.List) + + page := new(controller.Page) + apiGroup.GET("/page", page.List) + + news := new(controller.News) + apiGroup.GET("/news", news.List) + apiGroup.GET("/news/:id", news.Get) + apiGroup.GET("/news/prenext", news.PreNext) + + job := new(controller.Job) + apiGroup.GET("/job", job.List) + apiGroup.GET("/job/:id", job.Get) +} diff --git a/app/routers/routers.go b/app/routers/routers.go new file mode 100644 index 0000000..bb472d5 --- /dev/null +++ b/app/routers/routers.go @@ -0,0 +1,16 @@ +package routers + +import ( + "github.com/gin-gonic/gin" +) + +// InitRouter 初始化路由 +func InitRouter(engine *gin.Engine) { + //engine.LoadHTMLGlob("conf/static/views/**/*") + //engine.StaticFS("/static", http.Dir("conf/static/")) + + // 管理端router + initAdminRouter(engine) + // 客户端router + initApiRouter(engine) +} diff --git a/app/worker/worker.go b/app/worker/worker.go new file mode 100644 index 0000000..a6e6903 --- /dev/null +++ b/app/worker/worker.go @@ -0,0 +1,16 @@ +package worker + +import ( + "github.com/go-co-op/gocron" +) + +type Worker struct { +} + +func NewWorker() *Worker { + return &Worker{} +} + +func (w *Worker) Run(cron *gocron.Scheduler) { + cron.StartAsync() +} diff --git a/base/config/config.go b/base/config/config.go new file mode 100644 index 0000000..335bf1d --- /dev/null +++ b/base/config/config.go @@ -0,0 +1,78 @@ +package config + +import ( + "os" + "strings" + + "github.com/go-viper/mapstructure/v2" + log "github.com/sirupsen/logrus" + "github.com/smbrave/goutil" + "github.com/spf13/viper" +) + +var ( + config *Config + configEnv string +) + +type Chat struct { + Token string `toml:"token"` + BaseUrl string `toml:"base_url"` + Model string `toml:"model"` +} + +type Baidu struct { + TranslateApiKey string `toml:"translate_api_key"` + TranslateSecret string `toml:"translate_api_secret"` +} + +type Config struct { + Chat *Chat `toml:"chat"` + Baidu *Baidu `toml:"baidu"` +} + +func GetEnv() string { + return configEnv +} + +func IsProdEnv() bool { + return configEnv == "prod" +} + +func IsDevEnv() bool { + return configEnv == "dev" +} + +func IsTestEnv() bool { + return configEnv == "test" +} + +func GetConfig() *Config { + return config +} + +func LoadConfig() { + configEnv = os.Getenv("CONFIG_ENV") + if configEnv == "" { + configEnv = "prod" + } + + var envConfig Config + + viper.SetConfigFile("conf/server.conf." + configEnv) + viper.SetConfigType("toml") + viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.SetEnvPrefix("conf") + if err := viper.ReadInConfig(); err != nil { + panic(err) + } + if err := viper.Unmarshal(&envConfig, func(decoderConfig *mapstructure.DecoderConfig) { + decoderConfig.TagName = "toml" + }); err != nil { + panic(err) + } + + config = &envConfig + log.Infof("load real config[%s] ", goutil.EncodeJSONIndent(config)) +} diff --git a/base/config/error.go b/base/config/error.go new file mode 100644 index 0000000..f0656ff --- /dev/null +++ b/base/config/error.go @@ -0,0 +1,36 @@ +package config + +import "github.com/gogap/errors" + +// 这里是统一错误码 +var ( + ErrPriv = errors.T(196, "没有权限") + ErrDb = errors.T(197, "系统繁忙请稍后再试") + ErrRemote = errors.T(198, "系统繁忙请稍后再试") + ErrInternal = errors.T(199, "系统繁忙请稍后再试") + ErrLimit = errors.T(200, "访问限制请稍后再试") + ErrBusy = errors.T(201, "系统繁忙请稍后再试") + ErrVersion = errors.T(202, "请升级最新版本") + ErrData = errors.T(203, "数据错误") + ErrWordCheck = errors.T(204, "加载敏感词失败") + ErrOcrApi = errors.T(205, "读取图片失败") + + ErrLimitDay = errors.T(12002, "今日次数已用完") //客户端特殊错误码勿动,弹出钻石购买窗口 + ErrLimitMonth = errors.T(12003, "本月次数已用完") //客户端特殊错误码勿动,弹出钻石购买窗口 + ErrLimitMasonry = errors.T(12004, "钻石已用完") //客户端特殊错误码勿动,弹出钻石购买窗口 + + // 5位数的错误码为合理错误 不打错误日志 + ErrParam = errors.T(11000, "参数错误") + ErrNoData = errors.T(11001, "数据不存在") + ErrExist = errors.T(11002, "数据已存在") + ErrNoLink = errors.T(11003, "没有链接") + ErrLinkFormat = errors.T(11004, "链接格式不对") + ErrBlockSelf = errors.T(11005, "不能屏蔽自己") + ErrVipBuyMasonry = errors.T(11006, "会员才可以购买砖石") + ErrExists = errors.T(11007, "数据已存在") + ErrNotExists = errors.T(11008, "数据不存在") + ErrNeedLogin = errors.T(11009, "请登录") + ErrExt = errors.T(11010, "不支持的文件格式") + TranslateErr = errors.T(11011, "翻译失败") + InUseErr = errors.T(11012, "正在使用,如需删除请需求关联") +) diff --git a/base/config/keys.go b/base/config/keys.go new file mode 100644 index 0000000..7c6ecaa --- /dev/null +++ b/base/config/keys.go @@ -0,0 +1,18 @@ +package config + +var ( + ConfigKeyResultExpire = "server.extractor.result.expire" + ConfigKeyResultCache = "server.extractor.result.cache" + ConfigKeyImageUnavailableProxy = "server.image.unavailable.proxy" + ConfigKeyVideoUnavailableProxy = "server.video.unavailable.proxy" + ConfigKeyUnavailableDomain = "server.unavailable.domain" + ConfigKeyUnavailableDonwloadLimit = "server.unavailable.download.limit" + + ConfigKeyThreadingDomain = "server.threading.domain" + + ConfigKeyVideoPlayDomain = "server.video.play.domain" + ConfigKeyExtractorTimeout = "server.extractor.timeout" + ConfigKeyExtractorOffline = "server.extractor.offline" + ConfigKeyWebDownloadUrl = "server.web.download.url" + ConfigKeyMasonryMonth = "server.masonry.month" +) diff --git a/base/consts/consts.go b/base/consts/consts.go new file mode 100644 index 0000000..78336aa --- /dev/null +++ b/base/consts/consts.go @@ -0,0 +1,9 @@ +package consts + +const ( + ServerPicDiamond = "server.pic.diamond" + ServerVideoDiamond = "server.video.diamond" + ServerFreeDiamond = "server.free.diamond" + + RedisKeyWeixinUserInfo = "weixin_user_info" +) diff --git a/base/global/db.go b/base/global/db.go new file mode 100644 index 0000000..2515bcb --- /dev/null +++ b/base/global/db.go @@ -0,0 +1,61 @@ +package global + +import ( + "fmt" + "time" + + log "github.com/sirupsen/logrus" + "github.com/smbrave/goutil" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +var ( + db *gorm.DB = nil +) + +func GetDB() *gorm.DB { + return db +} + +func InitDB() { + cfg := GetConfig() + if cfg.Mysql.Host == "" { + return + } + + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", cfg.Mysql.User, + cfg.Mysql.Pass, cfg.Mysql.Host, cfg.Mysql.Port, cfg.Mysql.Db) + + var err error + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) + if err != nil { + log.Errorf("open dsn[%s] error[%s]", dsn, err) + panic(err) + } + + //全都设置了才会启用连接池配置 + if cfg.Mysql.MaxIdle > 0 && cfg.Mysql.MaxOpen > 0 && cfg.Mysql.MaxLifeTime > 0 { + dbSql, err := db.DB() + if err != nil { + log.Errorf("get db error[%s]", err) + } + dbSql.SetMaxOpenConns(cfg.Mysql.MaxOpen) + dbSql.SetMaxIdleConns(cfg.Mysql.MaxIdle) + dbSql.SetConnMaxLifetime(time.Minute * time.Duration(cfg.Mysql.MaxLifeTime)) + } + + db.Logger = &goutil.GORMLogger{Threshold: int64(cfg.Mysql.Threshold)} +} + +func DBPing() error { + d, err := db.DB() + if err != nil { + return err + } + + if err := d.Ping(); err != nil { + return err + } + return nil +} diff --git a/base/global/gin.go b/base/global/gin.go new file mode 100644 index 0000000..42c041a --- /dev/null +++ b/base/global/gin.go @@ -0,0 +1,14 @@ +package global + +import "github.com/gin-gonic/gin" + +var ( + ginEngine *gin.Engine = nil +) + +func GetGin() *gin.Engine { + if ginEngine == nil { + ginEngine = gin.New() + } + return ginEngine +} diff --git a/base/global/global.go b/base/global/global.go new file mode 100644 index 0000000..534cb9e --- /dev/null +++ b/base/global/global.go @@ -0,0 +1,20 @@ +package global + +import ( + "os" + + "github.com/mattn/go-colorable" + log "github.com/sirupsen/logrus" +) + +func Init() { + if os.Getenv("CONFIG_ENV") != "prod" { + log.SetFormatter(&LogFile{ + TextFormatter: log.TextFormatter{ + ForceColors: true, + }, + }) + log.SetOutput(colorable.NewColorableStdout()) + } + +} diff --git a/base/global/init_server.go b/base/global/init_server.go new file mode 100644 index 0000000..9a8a638 --- /dev/null +++ b/base/global/init_server.go @@ -0,0 +1,39 @@ +package global + +import ( + "io" + "net/http" + + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" + "github.com/smbrave/goutil" +) + +func InitServer(cfgToml string) { + LoadConfig(cfgToml) + + InitDB() + InitRedis() + InitStorage() + + GetGin().NoRoute(func(ctx *gin.Context) { + requestUri := ctx.Request.RequestURI + requestIp := ctx.GetHeader("x-remote-ip") + ignorePath := []string{ + "", + "/", + "/favicon.ico", + "/robots.txt", + "/.well-known/apple-app-site-association", + } + if goutil.StrInSlice(requestUri, ignorePath) { + return + } + if ctx.Request.Method == http.MethodPost || ctx.Request.Method == http.MethodPut { + body, _ := io.ReadAll(ctx.Request.Body) + log.Warnf("no route [%s][%s][%s][%s]\n%s", requestIp, ctx.Request.Host, ctx.Request.Method, requestUri, string(body)) + } else { + log.Warnf("no route [%s][%s][%s][%s]", requestIp, ctx.Request.Host, ctx.Request.Method, requestUri) + } + }) +} diff --git a/base/global/load.go b/base/global/load.go new file mode 100644 index 0000000..2e49530 --- /dev/null +++ b/base/global/load.go @@ -0,0 +1,87 @@ +package global + +import ( + "strings" + + "github.com/go-viper/mapstructure/v2" + log "github.com/sirupsen/logrus" + "github.com/smbrave/goutil" + "github.com/spf13/viper" +) + +var ( + config *Config +) + +type Mysql struct { + Host string `toml:"host"` + Port int `toml:"port"` + User string `toml:"user"` + Pass string `toml:"pass"` + Db string `toml:"db"` + Threshold int `toml:"threshold"` + //链接池配置 + MaxIdle int `toml:"max_idle"` + MaxOpen int `toml:"max_open"` + MaxLifeTime int `toml:"max_life_time"` +} + +type Redis struct { + Address string `toml:"address"` //服务redis的地址,不配置这个不初始化 +} + +//type QiniuConf struct { +// Bucket string `toml:"bucket"` +// Domain string `toml:"domain"` +// AccessKey string `toml:"access_key"` +// SecretKey string `toml:"secret_key"` +//} + +type Storage struct { + QiniuBucket string `toml:"qiniu_bucket"` + QiniuDomain string `toml:"qiniu_domain"` + QiniuAccessKey string `toml:"qiniu_access_key"` + QiniuSecretKey string `toml:"qiniu_secret_key"` + + MinioBucket string `toml:"minio_bucket"` + MinioDomain string `toml:"minio_domain"` + MinioAccessKey string `toml:"minio_access_key"` + MinioSecretKey string `toml:"minio_secret_key"` +} + +type Server struct { + Address string `toml:"address"` + LogLevel int `toml:"log_level"` //日志级别 +} + +type Config struct { + Server *Server `toml:"server"` + Mysql *Mysql `toml:"mysql"` + Redis *Redis `toml:"redis"` + Storage *Storage `toml:"storage"` +} + +func LoadConfig(file string) { + viper.SetConfigFile(file) + viper.SetConfigType("toml") + viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.SetEnvPrefix("conf") + var envConfig Config + if err := viper.ReadInConfig(); err != nil { + panic(err) + } + if err := viper.Unmarshal(&envConfig, func(decoderConfig *mapstructure.DecoderConfig) { + decoderConfig.TagName = "toml" + }); err != nil { + panic(err) + } + + config = &envConfig + // 打印配置文件 + log.Infof("load real config[%s] ", goutil.EncodeJSONIndent(config)) +} + +func GetConfig() *Config { + return config +} diff --git a/base/global/log.go b/base/global/log.go new file mode 100644 index 0000000..a5c2c41 --- /dev/null +++ b/base/global/log.go @@ -0,0 +1,42 @@ +package global + +import ( + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/rifflock/lfshook" + log "github.com/sirupsen/logrus" + "github.com/smbrave/goutil" + + "os" + "time" +) + +func InitLog() { + logLevel := GetConfig().Server.LogLevel + logfile := "log/server.log" + writer, err := rotatelogs.New( + logfile+".%Y%m%d", + rotatelogs.WithLinkName(logfile), + rotatelogs.WithMaxAge(time.Duration(86400*7)*time.Second), + rotatelogs.WithRotationTime(time.Duration(86400)*time.Second), + ) + if err != nil { + panic(err) + } + + pathMap := lfshook.WriterMap{ + log.TraceLevel: writer, + log.DebugLevel: writer, + log.InfoLevel: writer, + log.WarnLevel: writer, + log.ErrorLevel: writer, + log.FatalLevel: writer, + log.PanicLevel: writer, + } + + log.AddHook(lfshook.NewHook(pathMap, new(goutil.LogFile))) + + log.SetOutput(os.Stdout) + log.SetReportCaller(true) + log.SetFormatter(new(goutil.LogFile)) + log.SetLevel(log.Level(logLevel)) +} diff --git a/base/global/logFile.go b/base/global/logFile.go new file mode 100644 index 0000000..aa681ce --- /dev/null +++ b/base/global/logFile.go @@ -0,0 +1,52 @@ +package global + +import ( + "fmt" + "path/filepath" + "strings" + + log "github.com/sirupsen/logrus" +) + +type LogFile struct { + log.TextFormatter +} + +const ( + red = "38;2;255;0;0" + yellow = "38;2;255;165;0" + gray = "38;2;100;100;100" + def = "38;2;100;150;200" + green = "38;2;85;107;47" + + // red = "31" + // yellow = "33" + // gray = "37" + // def = "36" +) + +func (f *LogFile) Format(entry *log.Entry) ([]byte, error) { + var levelColor string + switch entry.Level { + case log.DebugLevel, log.TraceLevel: + levelColor = green + case log.WarnLevel: + levelColor = yellow + case log.ErrorLevel, log.FatalLevel, log.PanicLevel: + levelColor = red + case log.InfoLevel: + levelColor = def + default: + levelColor = def + } + + funcs := strings.SplitN(entry.Caller.Function, ".", 2) + msg := fmt.Sprintf("\033[%sm[%s] [%s] [%s:%d] %s\033[0m \n", + levelColor, + entry.Time.Format("2006-01-02 15:04:05,000"), + strings.ToUpper(entry.Level.String()), + funcs[0]+"/"+filepath.Base(entry.Caller.File), + entry.Caller.Line, + entry.Message) + return []byte(msg), nil +} diff --git a/base/global/redis.go b/base/global/redis.go new file mode 100644 index 0000000..059a735 --- /dev/null +++ b/base/global/redis.go @@ -0,0 +1,27 @@ +package global + +import ( + "github.com/go-redis/redis" +) + +var ( + redisClient *redis.Client = nil +) + +func InitRedis() { + cfg := GetConfig() + if cfg.Redis.Address == "" { + return + } + + redisClient = redis.NewClient(&redis.Options{ + Addr: cfg.Redis.Address, + }) +} + +func GetRedis() *redis.Client { + if redisClient == nil { + InitRedis() + } + return redisClient +} diff --git a/base/global/storage.go b/base/global/storage.go new file mode 100644 index 0000000..d7b207a --- /dev/null +++ b/base/global/storage.go @@ -0,0 +1,24 @@ +package global + +import ( + "time" + "yintai-api/base/global/storage" +) + +var ( + qiniuStorage storage.Storage +) + +func InitStorage() error { + qiniuStorage = storage.NewQiniu(&storage.QiniuConfig{ + Bucket: GetConfig().Storage.QiniuBucket, + AK: GetConfig().Storage.QiniuAccessKey, + SK: GetConfig().Storage.QiniuSecretKey, + Timeout: time.Second * 60, + }) + return nil +} + +func GetStorage(...string) storage.Storage { + return qiniuStorage +} diff --git a/base/global/storage/minio.go b/base/global/storage/minio.go new file mode 100644 index 0000000..7f1c49f --- /dev/null +++ b/base/global/storage/minio.go @@ -0,0 +1,199 @@ +package storage + +import ( + "context" + "fmt" + "net/http" + "net/url" + "path" + "strings" + "time" + + "github.com/minio/minio-go" + + log "github.com/sirupsen/logrus" +) + +type MinioConfig struct { + Bucket string + BaseUrl string + Endpoint string + AccessKey string + SecretKey string + Timeout time.Duration +} + +type Minio struct { + inited bool + config *MinioConfig + client *minio.Client +} + +func NewMinio(cfg *MinioConfig) Storage { + if cfg.Endpoint == "" { + u, _ := url.Parse(cfg.BaseUrl) + cfg.Endpoint = u.Host + } + return &Minio{ + config: cfg, + inited: false, + } +} + +func (s *Minio) Init() error { + if s.inited { + return nil + } + + c, err := minio.New(s.config.Endpoint, s.config.AccessKey, s.config.SecretKey, false) + if err != nil { + return err + } + s.client = c + s.inited = true + return nil +} + +func (s *Minio) Put(fileName, objectName string, onProcess func(fsize, uploaded int64)) error { + if err := s.Init(); err != nil { + return err + } + ctx, cancel := context.WithTimeout(context.Background(), s.config.Timeout) + defer cancel() + + objectName = strings.TrimLeft(objectName, "/ ") + ext := strings.TrimLeft(path.Ext(fileName), ".") + contentType := ext2ContentType(ext) + if contentType == "" { + contentType = detectContentType(fileName) + } + + _, err := s.client.FPutObjectWithContext(ctx, s.config.Bucket, objectName, fileName, + minio.PutObjectOptions{ContentType: contentType}) + if err != nil { + log.Errorf("upload file to minio error:%s", err.Error()) + return err + } + + return nil +} + +func (s *Minio) Del(objectName string) error { + if err := s.Init(); err != nil { + return err + } + + err := s.client.RemoveObject(s.config.Bucket, objectName) + if err != nil { + log.Errorf("delete file to minio error:%s", err.Error()) + return err + } + return nil +} + +func (s *Minio) Get(objectName, fileName string) error { + if err := s.Init(); err != nil { + return err + } + + return s.client.FGetObject(s.config.Bucket, objectName, fileName, minio.GetObjectOptions{}) +} + +func (s *Minio) List(objectPrefix string) ([]string, error) { + if err := s.Init(); err != nil { + return nil, err + } + + done := make(chan struct{}) + defer close(done) + infos := s.client.ListObjectsV2(s.config.Bucket, objectPrefix, true, done) + result := make([]string, 0) + for info := range infos { + result = append(result, info.Key) + } + return result, nil +} + +func (s *Minio) Url(objectName string, expire time.Duration) string { + if err := s.Init(); err != nil { + return err.Error() + } + + if expire > time.Hour*24*7 || expire == -1 { + expire = time.Hour * 24 * 7 + } + var params url.Values + u, err := s.client.PresignedGetObject(s.config.Bucket, objectName, expire, params) + if err != nil { + log.Errorf("error:%s", err.Error()) + return fmt.Sprintf("%s/%s/%s", s.config.BaseUrl, s.config.Bucket, objectName) + } + + return u.String() + +} + +func (s *Minio) Stat(objectName string) (*ObjectInfo, error) { + if err := s.Init(); err != nil { + return nil, err + } + + objectName = strings.TrimLeft(objectName, "/ ") + stat, err := s.client.StatObject(s.config.Bucket, objectName, minio.StatObjectOptions{}) + if err != nil { + log.Errorf("stat object from minio error:%s", err.Error()) + return nil, err + } + + info := &ObjectInfo{ + Size: stat.Size, + Hash: stat.ETag, + MimeType: stat.ContentType, + PutTime: stat.LastModified.Unix(), + } + return info, nil +} + +func (s *Minio) Fetch(urlStr, objectName string) error { + if err := s.Init(); err != nil { + return err + } + + // 从 URL 下载文件 + resp, err := http.Get(urlStr) + if err != nil { + log.Errorf("fetch file from url error:%s", err.Error()) + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("fetch file from url failed, status: %d", resp.StatusCode) + } + + // 获取文件名和 Content-Type + objectName = strings.TrimLeft(objectName, "/ ") + // 优先根据文件扩展名推断 Content-Type,确保图片能正确预览 + ext := strings.TrimLeft(path.Ext(objectName), ".") + contentType := ext2ContentType(ext) + if contentType == "" { + contentType = resp.Header.Get("Content-Type") + } + + // 上传到 MinIO + ctx, cancel := context.WithTimeout(context.Background(), s.config.Timeout) + defer cancel() + + _, err = s.client.PutObjectWithContext(ctx, s.config.Bucket, objectName, resp.Body, resp.ContentLength, + minio.PutObjectOptions{ContentType: contentType}) + if err != nil { + log.Errorf("upload fetched file to minio error:%s", err.Error()) + return err + } + + return nil +} + +func (m *Minio) UploadToken() string { + panic("implement me") +} diff --git a/base/global/storage/qiniu.go b/base/global/storage/qiniu.go new file mode 100644 index 0000000..001c2be --- /dev/null +++ b/base/global/storage/qiniu.go @@ -0,0 +1,225 @@ +package storage + +import ( + "context" + "errors" + "fmt" + "os" + "time" + + "github.com/qiniu/go-sdk/v7/auth/qbox" + "github.com/qiniu/go-sdk/v7/storage" +) + +type QiniuConfig struct { + Bucket string + AK string + SK string + Timeout time.Duration +} + +type Qiniu struct { + config *QiniuConfig +} + +func NewQiniu(cfg *QiniuConfig) Storage { + + return &Qiniu{ + config: cfg, + } +} + +func (s *Qiniu) Put(fileName, objectName string, onProcess func(fsize, uploaded int64)) error { + mac := qbox.NewMac(s.config.AK, s.config.SK) + putPolicy := storage.PutPolicy{ + Scope: s.config.Bucket, + Expires: uint64(time.Now().Add(12 * time.Hour).Unix()), + } + upToken := putPolicy.UploadToken(mac) + cfg := storage.Config{} + cfg.UseHTTPS = false + cfg.UseCdnDomains = false + + // 构建表单上传的对象 + formUploader := storage.NewFormUploader(&cfg) + ret := storage.PutRet{} + + // 可选配置 + putExtra := storage.PutExtra{ + Params: map[string]string{}, + OnProgress: onProcess, + } + err := formUploader.PutFile(context.Background(), &ret, upToken, objectName, fileName, &putExtra) + if err != nil { + return err + } + return nil +} + +func (s *Qiniu) PutResume(fileName, objectName string, onProcess func(fsize, uploaded int64)) error { + mac := qbox.NewMac(s.config.AK, s.config.SK) + putPolicy := storage.PutPolicy{ + Scope: s.config.Bucket, + } + upToken := putPolicy.UploadToken(mac) + cfg := storage.Config{} + cfg.UseHTTPS = false + cfg.UseCdnDomains = false + + file, err := os.Open(fileName) + if err != nil { + return err + } + + fileInfo, _ := file.Stat() + fileSize := fileInfo.Size() + + resumeUploader := storage.NewResumeUploaderV2(&cfg) + ret := storage.PutRet{} + + recorder, err := storage.NewFileRecorder(os.TempDir()) + if err != nil { + return err + } + + partSize := int64(1024 * 1024 * 1) + partUploaded := int64(0) + putExtra := storage.RputV2Extra{ + Recorder: recorder, + Notify: func(partNumber int64, ret *storage.UploadPartsRet) { + partUploaded += 1 + onProcess(fileSize, partUploaded*partSize) + }, + PartSize: partSize, + } + err = resumeUploader.PutFile(context.Background(), &ret, upToken, objectName, fileName, &putExtra) + if err != nil { + return err + } + return nil +} + +func (s *Qiniu) Del(objectName string) error { + mac := qbox.NewMac(s.config.AK, s.config.SK) + cfg := storage.Config{ + UseHTTPS: false, + } + bucketManager := storage.NewBucketManager(mac, &cfg) + return bucketManager.Delete(s.config.Bucket, objectName) +} + +func (s *Qiniu) List(objectPrefix string) ([]string, error) { + mac := qbox.NewMac(s.config.AK, s.config.SK) + cfg := storage.Config{ + UseHTTPS: false, + } + result := make([]string, 0) + marker := "" + bucketManager := storage.NewBucketManager(mac, &cfg) + for { + entries, _, nextMarker, hasNext, err := bucketManager.ListFiles(s.config.Bucket, objectPrefix, "", marker, 1000) + if err != nil { + return nil, err + } + for _, entry := range entries { + result = append(result, entry.Key) + } + if hasNext { + marker = nextMarker + } else { + break + } + } + + return result, nil +} + +func (s *Qiniu) Get(objectName, fileName string) error { + mac := qbox.NewMac(s.config.AK, s.config.SK) + cfg := storage.Config{ + UseHTTPS: false, + } + + bucketManager := storage.NewBucketManager(mac, &cfg) + + domains, err := bucketManager.ListBucketDomains(s.config.Bucket) + if err != nil { + return err + } + if len(domains) <= 0 { + return errors.New("bucket no domain") + } + url := fmt.Sprintf("http://%s/%s", domains[0].Domain, objectName) + return Download(url, fileName) +} + +func (s *Qiniu) Url(objectName string, expire time.Duration) string { + mac := qbox.NewMac(s.config.AK, s.config.SK) + cfg := storage.Config{ + UseHTTPS: false, + } + + bucketManager := storage.NewBucketManager(mac, &cfg) + domains, err := bucketManager.ListBucketDomains(s.config.Bucket) + if err != nil { + return "" + } + if len(domains) <= 0 { + return "" + } + + domain := "https://" + domains[0].Domain + if expire == 0 { + return storage.MakePublicURLv2(domain, objectName) + } else { + deadline := time.Now().Add(expire).Unix() + return storage.MakePrivateURLv2(mac, domain, objectName, deadline) + } +} + +func (s *Qiniu) Stat(objectName string) (*ObjectInfo, error) { + mac := qbox.NewMac(s.config.AK, s.config.SK) + cfg := storage.Config{ + UseHTTPS: false, + } + + bucketManager := storage.NewBucketManager(mac, &cfg) + fileInfo, err := bucketManager.Stat(s.config.Bucket, objectName) + if err != nil { + return nil, err + } + + info := new(ObjectInfo) + info.Size = fileInfo.Fsize + info.Hash = fileInfo.Hash + info.MimeType = fileInfo.MimeType + info.PutTime = fileInfo.PutTime + return info, nil +} + +func (s *Qiniu) Fetch(url, objectName string) error { + mac := qbox.NewMac(s.config.AK, s.config.SK) + cfg := storage.Config{ + UseHTTPS: false, + } + + bucketManager := storage.NewBucketManager(mac, &cfg) + + _, err := bucketManager.Fetch(url, s.config.Bucket, objectName) + if err != nil { + return err + } + + return nil +} + +func (s *Qiniu) UploadToken() string { + mac := qbox.NewMac(s.config.AK, s.config.SK) + putPolicy := storage.PutPolicy{ + Scope: s.config.Bucket, + Expires: uint64(time.Now().Add(12 * time.Hour).Unix()), + } + upToken := putPolicy.UploadToken(mac) + + return upToken +} diff --git a/base/global/storage/storage.go b/base/global/storage/storage.go new file mode 100644 index 0000000..9324edd --- /dev/null +++ b/base/global/storage/storage.go @@ -0,0 +1,21 @@ +package storage + +import "time" + +type ObjectInfo struct { + Size int64 + MimeType string + Hash string + PutTime int64 +} + +type Storage interface { + Put(fileName, objectName string, onProcess func(fsize, uploaded int64)) error + Get(objectName, fileName string) error + Del(objectName string) error + Url(objectName string, expire time.Duration) string + Stat(objectName string) (*ObjectInfo, error) + List(objectPrefix string) ([]string, error) + Fetch(url, objectName string) error + UploadToken() string +} diff --git a/base/global/storage/util.go b/base/global/storage/util.go new file mode 100644 index 0000000..a99f44f --- /dev/null +++ b/base/global/storage/util.go @@ -0,0 +1,156 @@ +package storage + +import ( + "io" + "net/http" + "os" + "strconv" + "strings" +) + +func contentType2Ext(contentType string) string { + if strings.Contains(contentType, "image/jpeg") { + return "jpg" + } else if strings.Contains(contentType, "audio/mpeg") { + return "mp3" + } else if strings.Contains(contentType, "video/mp4") { + return "mp4" + } + return "" +} + +func ext2ContentType(ext string) string { + ext = strings.ToLower(ext) + switch ext { + // 图片格式 + case "jpg", "jpeg": + return "image/jpeg" + case "png": + return "image/png" + case "gif": + return "image/gif" + case "webp": + return "image/webp" + case "bmp": + return "image/bmp" + case "svg", "svgz": + return "image/svg+xml" + case "ico": + return "image/x-icon" + case "tiff", "tif": + return "image/tiff" + case "avif": + return "image/avif" + case "apng": + return "image/apng" + // 音频格式 + case "mp3": + return "audio/mpeg" + case "wav": + return "audio/wav" + case "ogg": + return "audio/ogg" + case "flac": + return "audio/flac" + case "aac": + return "audio/aac" + case "m4a": + return "audio/mp4" + // 视频格式 + case "mp4": + return "video/mp4" + case "avi": + return "video/x-msvideo" + case "mov", "qt": + return "video/quicktime" + case "webm": + return "video/webm" + case "flv": + return "video/x-flv" + case "mkv": + return "video/x-matroska" + case "wmv": + return "video/x-ms-wmv" + case "m3u8": + return "application/vnd.apple.mpegurl" + case "ts": + return "video/mp2t" + // 文档格式 + case "pdf": + return "application/pdf" + case "doc": + return "application/msword" + case "docx": + return "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + case "xls": + return "application/vnd.ms-excel" + case "xlsx": + return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + case "ppt": + return "application/vnd.ms-powerpoint" + case "pptx": + return "application/vnd.openxmlformats-officedocument.presentationml.presentation" + case "txt": + return "text/plain" + case "html", "htm": + return "text/html" + case "css": + return "text/css" + case "js": + return "application/javascript" + case "json": + return "application/json" + case "xml": + return "application/xml" + case "zip": + return "application/zip" + case "rar": + return "application/x-rar-compressed" + case "7z": + return "application/x-7z-compressed" + case "gz": + return "application/gzip" + default: + return "" + } +} + +// detectContentType 从文件内容检测 MIME 类型 +func detectContentType(filePath string) string { + f, err := os.Open(filePath) + if err != nil { + return "" + } + defer f.Close() + + // 读取前 512 字节用于检测 + buf := make([]byte, 512) + n, err := f.Read(buf) + if err != nil && err != io.EOF { + return "" + } + + return http.DetectContentType(buf[:n]) +} + +func Download(url, path string) error { + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + stat, err := f.Stat() + if err != nil { + return err + } + defer f.Close() + + req, _ := http.NewRequest("GET", url, nil) + req.Header.Set("Range", "bytes="+strconv.FormatInt(stat.Size(), 10)+"-") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + _, err = io.Copy(f, resp.Body) + if err != nil { + return err + } + + return nil +} diff --git a/cmd/yintai-api/yintai-api.go b/cmd/yintai-api/yintai-api.go new file mode 100644 index 0000000..788b549 --- /dev/null +++ b/cmd/yintai-api/yintai-api.go @@ -0,0 +1,24 @@ +package main + +import ( + "yintai-api/app/common/utils" + "yintai-api/app/routers" + "yintai-api/base/config" + "yintai-api/base/global" +) + +func main() { + config.LoadConfig() + global.InitServer("conf/server.conf." + config.GetEnv()) + global.Init() + routers.InitRouter(global.GetGin()) + global.InitRedis() + global.InitLog() + + utils.PermissionInit() + go utils.AutoIncrementNews() + + if err := global.GetGin().Run(global.GetConfig().Server.Address); err != nil { + panic(config.ErrInternal.New().Append(err)) + } +} diff --git a/cmd/yintai-worker/yintai-worker.go b/cmd/yintai-worker/yintai-worker.go new file mode 100644 index 0000000..eea4522 --- /dev/null +++ b/cmd/yintai-worker/yintai-worker.go @@ -0,0 +1,15 @@ +package main + +import ( + "yintai-api/base/config" + "yintai-api/base/global" +) + +func main() { + config.LoadConfig() + global.InitServer("conf/server.conf." + config.GetEnv()) + + if err := global.GetGin().Run(global.GetConfig().Server.Address); err != nil { + panic(config.ErrInternal.New().Append(err)) + } +} diff --git a/conf/admin.json b/conf/admin.json new file mode 100644 index 0000000..5ac53b9 --- /dev/null +++ b/conf/admin.json @@ -0,0 +1,30 @@ +{ + "admin.push.suggest": [ + { + "name": "拍照写作", + "intent": "intent://com.getui.push/main?#Intent;scheme=gtpushscheme;launchFlags=0x4000000;package=com.batiao.aiwrite;component=com.batiao.aiwrite/.ui.MainActivity;S.gttask=;S.payload=payloadStr;end", + "params": "{\"page\": \"photos_write\",\"params\": \"source=ve_02,type=member\"}" + } + ], + "admin.package.suggest": [ + { + "name": "装样大师", + "package": "com.devcon.flaunt", + "prefix": "E" + } + ], + "admin.h5.domain": "https://corp-uni.yua8.cn/", + "admin.corp.menus": { + "home.show": true, + "trade.show": true, + "trade/data.show": true, + "trade/order.show": true, + "trade/goods.show": true, + "user.show": true, + "user/list.show": true, + "admin.show": true, + "admin/list.show": true, + "prev.show":true, + "prev/list.show":true + } +} \ No newline at end of file diff --git a/conf/default.json b/conf/default.json new file mode 100644 index 0000000..f06d14c --- /dev/null +++ b/conf/default.json @@ -0,0 +1,25 @@ +{ + "client.weixin.open.appid": "wx31efd88bd33ae068", + "client.login.type": [ + "weixin", + "apple", + "phone" + ], + "client.default.store": "3", + "client.nologin.pay.enable": true, + "client.guide.enable": true, + "client.guide.pay.enable": false, + "client.pay.enable": true, + "client.weixin.share": { + "content": "你的“戏精”专属神器,一键解锁社交高光时刻!", + "image": "http://cdn.batiao8.com/flaunt/logo/108.png", + "link": "https://qianhu.wejianzhan.com/site/wjzo3n62/38a0c404-e04d-40b9-8023-1136ab9804a2", + "title": "装样大师" + }, + "server.qr.share.link": "https://qianhu.wejianzhan.com/site/wjzo3n62/38a0c404-e04d-40b9-8023-1136ab9804a2", + "server.weixin.service": { + "corpid": "ww43c49db2e88a17f8", + "secret": "ib8F_FAsRD8tLy_nUWzSGeDnZOcqX920mhhpBKlEF7k", + "kfid": "wkP3ZeXwAADrczZJ4yh-b8fW1n8HBWVQ" + } +} \ No newline at end of file diff --git a/conf/server.conf.pro b/conf/server.conf.pro new file mode 100644 index 0000000..160a772 --- /dev/null +++ b/conf/server.conf.pro @@ -0,0 +1,69 @@ +[server] +address = "0.0.0.0:9202" +#0:PAINC 1:FATAL 2:ERROR 3:WARNING 4:INFO 5:DEBUG 6:TRACE +log_level = 6 +#log_level = 6 +app_id = 10037 +app_name = "FLAT" +app_logo = "" +app_domain = "" +ingore_event = "/api/user/event" + +[mysql] +host = "" +port = +user = "" +pass = "" +db = "" +threshold = 10000 + +[elastic] +address = "http://10.0.2.133:9200" +username = "elastic" +password = "o2NJzeAODwpfAgLJ5SFY" + +[redis] +address="127.0.0.1:6379" + +[unify] +address = "http://127.0.0.1:9281" +phone_sender = "aliyun-verify-sms" +phone_pass = "m02c4g8W2ao72py78oi" +message_sender = "" +admin_token = "c39e1c6e-38f7-422c-a7d1-9e05bf65596f" +api_token = "d8e3284a-1c29-4812-b27e-1421fbfec57e" + +[weixin] +open_appid = "" +open_secret = "" +mp_appid = "" +mp_secret = "" +oa_appid = "" +oa_secret = "" +oa_token = "" +oa_aes_key = "" +qy_corpid = "" +qy_secret = "" +qy_kf_id = "" + + +[metric] +address = "http://10.0.2.157:8086" +username = "batiao" +password = "Batiao12B" + +[push] +app_id = "mJOMnMC4Gr8sk43YsFpNZ" +app_key = "Cqs4stKHYR8QCMIT2ECxM1" +master_secret = "80ixavzltw6IhRV0Ig01i8" +xiaomi_channel_id = "" + +[storage] +qiniu_domain = "https://cdn2.batiao8.com" +qiniu_bucket = "batiao-cdn2" +qiniu_access_key = "PXLslf3oy6daTpT7byNHE2b5-Y_BlZLpewfNVB4y" +qiniu_secret_key = "C2fUI3qdrQIPkwx2yB4h1Etjbb0LQ15P4aHwShWa" +minio_domain = "https://m.batiao8.com" +minio_bucket = "member" +minio_access_key = "minio" +minio_secret_key = "Minio@Bat123" \ No newline at end of file diff --git a/conf/server.conf.prod b/conf/server.conf.prod new file mode 100644 index 0000000..489f8e7 --- /dev/null +++ b/conf/server.conf.prod @@ -0,0 +1,73 @@ +[server] +address = "0.0.0.0:9360" +#0:PAINC 1:FATAL 2:ERROR 3:WARNING 4:INFO 5:DEBUG 6:TRACE +log_level = 6 +#log_level = 6 +app_id = 10060 +app_name = "FLAT" +app_logo = "" +app_domain = "" +ingore_event = "/api/user/event" + +[mysql] +host = "10.0.2.157" +port = 3307 +user = "yintai2" +pass = "CJvxA0gdh2*I4y0M" +db = "yintai2" +threshold = 10000 + +[elastic] +address = "" +username = "" +password = "" + +[redis] +address="127.0.0.1:6379" + +[unify] +address = "" +phone_sender = "" +phone_pass = "" +message_sender = "" +admin_token = "" +api_token = "" + +[weixin] +open_appid = "" +open_secret = "" +mp_appid = "" +mp_secret = "" +oa_appid = "" +oa_secret = "" +oa_token = "" +oa_aes_key = "" +qy_corpid = "" +qy_secret = "" +qy_kf_id = "" + + +[metric] +address = "http://10.0.2.157:8086" +username = "batiao" +password = "Batiao12B" + +[push] +app_id = "" +app_key = "" +master_secret = "" +xiaomi_channel_id = "" + +[storage] +qiniu_domain = "https://cdn.batiao8.com" +qiniu_bucket = "batiao-cdn" +qiniu_access_key = "PXLslf3oy6daTpT7byNHE2b5-Y_BlZLpewfNVB4y" +qiniu_secret_key = "C2fUI3qdrQIPkwx2yB4h1Etjbb0LQ15P4aHwShWa" +minio_domain = "https://m.batiao8.com" +minio_bucket = "member" +minio_access_key = "minio" +minio_secret_key = "Minio@Bat123" + +[baidu] +translate_api_key = "0eYRzSUYlqjr02qTyJ2zBoFO" +translate_api_secret = "RPmGdAJFCaSW0tqdlFgaXaWu5ug7Mvnb" \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..aee3783 --- /dev/null +++ b/go.mod @@ -0,0 +1,115 @@ +module yintai-api + +go 1.25.5 + +require ( + github.com/gin-gonic/gin v1.11.0 + github.com/go-co-op/gocron v1.37.0 + github.com/go-redis/redis v6.15.9+incompatible + github.com/go-viper/mapstructure/v2 v2.4.0 + github.com/gogap/errors v0.0.0-20210818113853-edfbba0ddea9 + github.com/mattn/go-colorable v0.1.14 + github.com/olivere/elastic/v7 v7.0.32 + github.com/sirupsen/logrus v1.9.3 + github.com/smbrave/goutil v0.0.0-20250409122131-41cca9439387 + github.com/spf13/viper v1.21.0 + gorm.io/gorm v1.31.1 +) + +require ( + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/Knetic/govaluate v3.0.0+incompatible // indirect + github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect + github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.14.1 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/fogleman/gg v1.3.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.10 // indirect + github.com/gammazero/toposort v0.1.1 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/go-redis/redis/v8 v8.11.5 // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.18.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect + github.com/gogap/stack v0.0.0-20150131034635-fef68dddd4f8 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/gomodule/redigo v1.9.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect + github.com/ipipdotnet/ipdb-go v1.3.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect + github.com/lestrrat-go/strftime v1.0.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/minio/minio-go v6.0.14+incompatible // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/qiniu/go-sdk/v7 v7.25.4 // indirect + github.com/qiniu/x v1.10.5 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.54.0 // indirect + github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/silenceper/wechat/v2 v2.1.6 // indirect + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect + github.com/smartwalle/alipay/v3 v3.2.28 // indirect + github.com/smartwalle/apple v0.0.11 // indirect + github.com/smartwalle/dbc v0.0.20 // indirect + github.com/smartwalle/ncrypto v1.0.4 // indirect + github.com/smartwalle/ngx v1.0.12 // indirect + github.com/smartwalle/nsign v1.0.9 // indirect + github.com/smartwalle/nsync v0.0.7 // indirect + github.com/smartwalle/queue v0.0.4 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/speps/go-hashids v2.0.0+incompatible // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + github.com/wechatpay-apiv3/wechatpay-go v0.2.21 // indirect + github.com/xiaoqidun/qqwry v0.0.0-20250915110312-1dd385f77d98 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/mock v0.6.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/arch v0.21.0 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/image v0.31.0 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.37.0 // indirect + google.golang.org/protobuf v1.36.9 // indirect + gorm.io/driver/mysql v1.5.4 // indirect + modernc.org/fileutil v1.0.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7962ce0 --- /dev/null +++ b/go.sum @@ -0,0 +1,415 @@ +git.u8t.cn/open/go-server v0.0.0-20260105094407-837748f41c50 h1:eWGFx8iFaK4gApX0hAWYW1moDdOnsoAALCa+BOWdymU= +git.u8t.cn/open/go-server v0.0.0-20260105094407-837748f41c50/go.mod h1:bi8WvgiHE4GjHL45wsc7D8Q2u+IU0xR61WzYehBehrA= +git.u8t.cn/open/gosdk v0.0.0-20251230090732-7ca2710e165b h1:UwDSqkVtL3MSLlJtLfvznIcltiKkAM3DohxkrR+jt0c= +git.u8t.cn/open/gosdk v0.0.0-20251230090732-7ca2710e165b/go.mod h1:tqb+Qi89QxpBN6Pyyx8kol/6ToPHp0GBex1hNJZNAMk= +git.u8t.cn/open/goutil v0.0.0-20251006031723-35e9c33cf3b5 h1:/saZDe8uRIDza1JeAR0STC6us0yERD1iHl04iAeqtOw= +git.u8t.cn/open/goutil v0.0.0-20251006031723-35e9c33cf3b5/go.mod h1:4OMSHaXbIjOUr7l1m+iUkB49JLzIDVQB87r/oU9PCdQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg= +github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw= +github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= +github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4= +github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis/v2 v2.30.0 h1:uA3uhDbCxfO9+DI/DuGeAMr9qI+noVWwGPNTFuKID5M= +github.com/alicebob/miniredis/v2 v2.30.0/go.mod h1:84TWKZlxYkfgMucPBf5SOQBYJceZeQRFIaQgNMiCX6Q= +github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf h1:TqhNAT4zKbTdLa62d2HDBFdvgSbIGB3eJE8HqhgiL9I= +github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w= +github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= +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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +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/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= +github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gammazero/toposort v0.1.1 h1:OivGxsWxF3U3+U80VoLJ+f50HcPU1MIqE1JlKzoJ2Eg= +github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= +github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= +github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +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.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +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.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +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.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogap/errors v0.0.0-20210818113853-edfbba0ddea9 h1:qvGIRaCYFKkyFK9SgRXJCc/lmQCeeg2cl3mwBKQd5W0= +github.com/gogap/errors v0.0.0-20210818113853-edfbba0ddea9/go.mod h1:tbRYYYC7g/H7QlCeX0Z2zaThWKowF4QQCFIsGgAsqRo= +github.com/gogap/stack v0.0.0-20150131034635-fef68dddd4f8 h1:AuxION6c7in+AsPmFjQTUKT6/o1suT8XEEpfU0pWsHA= +github.com/gogap/stack v0.0.0-20150131034635-fef68dddd4f8/go.mod h1:6q1WEv2BiAO4FSdwLQTJbWQYAn1/qDNJHUGJNXCj9kM= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s= +github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= +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.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/ipipdotnet/ipdb-go v1.3.3 h1:GLSAW9ypLUd6EF9QNK2Uhxew9Jzs4XMJ9gOZEFnJm7U= +github.com/ipipdotnet/ipdb-go v1.3.3/go.mod h1:yZ+8puwe3R37a/3qRftXo40nZVQbxYDLqls9o5foexs= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +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/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +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/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +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/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= +github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ= +github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +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/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJDswNVuni6km9o= +github.com/minio/minio-go v6.0.14+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E= +github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk= +github.com/qiniu/go-sdk/v7 v7.25.4 h1:ulCKlTEyrZzmNytXweOrnva49+Q4+ASjYBCSXhkRWTo= +github.com/qiniu/go-sdk/v7 v7.25.4/go.mod h1:dmKtJ2ahhPWFVi9o1D5GemmWoh/ctuB9peqTowyTO8o= +github.com/qiniu/x v1.10.5 h1:7V/CYWEmo9axJULvrJN6sMYh2FdY+esN5h8jwDkA4b0= +github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= +github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/silenceper/wechat/v2 v2.1.6 h1:2br2DxNzhksmvIBJ+PfMqjqsvoZmd/5BnMIfjKYUBgc= +github.com/silenceper/wechat/v2 v2.1.6/go.mod h1:7Iu3EhQYVtDUJAj+ZVRy8yom75ga7aDWv8RurLkVm0s= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= +github.com/smartwalle/alipay/v3 v3.2.28 h1:pANvguTjmFtfTw3le44qjQHYDrRcjCy8r8YUonFeblE= +github.com/smartwalle/alipay/v3 v3.2.28/go.mod h1:dwPyjY5y17qUsDrsVYCQqmDBtQ/qpvY8JmQMvuJYGLQ= +github.com/smartwalle/apple v0.0.11 h1:eukWXEyniffkgvKxnNqW8VVIBj09QOg4TDLjxd7dnfU= +github.com/smartwalle/apple v0.0.11/go.mod h1:SHRLD1nvwkzEHx2+AVHQxTzzlLURJ8oGhYvrGwfzp34= +github.com/smartwalle/dbc v0.0.20 h1:hGg5Dr7sDwfqWukv/EQ7Q7pxQ0l3bXRE9vy1W4mDEkk= +github.com/smartwalle/dbc v0.0.20/go.mod h1:mO9WdNrdrwUW2Ty2H6hF17aEFXySxQwKmmoJa9wO+AQ= +github.com/smartwalle/ncrypto v1.0.4 h1:P2rqQxDepJwgeO5ShoC+wGcK2wNJDmcdBOWAksuIgx8= +github.com/smartwalle/ncrypto v1.0.4/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk= +github.com/smartwalle/ngx v1.0.12 h1:jcoCyu/0HtQ1y/gbiSLzqOUZcHnVLlKOmm0awRF7Mcg= +github.com/smartwalle/ngx v1.0.12/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0= +github.com/smartwalle/nsign v1.0.9 h1:8poAgG7zBd8HkZy9RQDwasC6XZvJpDGQWSjzL2FZL6E= +github.com/smartwalle/nsign v1.0.9/go.mod h1:eY6I4CJlyNdVMP+t6z1H6Jpd4m5/V+8xi44ufSTxXgc= +github.com/smartwalle/nsync v0.0.7 h1:yyTe3T4zZ/S+H8ojtrtanS/ur5gMyfV0SbZ4KRdm8yI= +github.com/smartwalle/nsync v0.0.7/go.mod h1:nMMP9E4eXlpDrSW8amkScDfrwyQt7VKpG2PJzeS6U+I= +github.com/smartwalle/queue v0.0.4 h1:h2YfM/I1yjPOQjMkhPOVuN5cSk62fRPqzqhlyZ/5zDA= +github.com/smartwalle/queue v0.0.4/go.mod h1:zMEHt/7zLtVwATDUQi3OdBUlbQ2XUzWd7OXP8p/684c= +github.com/smbrave/goutil v0.0.0-20250409122131-41cca9439387 h1:vDRW8Dgq6iUsttBriKaHWK4Y6dI5Mh+oCAVfxQZqwsc= +github.com/smbrave/goutil v0.0.0-20250409122131-41cca9439387/go.mod h1:fIQLaFZdv8CFuemMl3jxKYKbJH9xqXhl0XyrHKAbkMM= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/speps/go-hashids v2.0.0+incompatible h1:kSfxGfESueJKTx0mpER9Y/1XHl+FVQjtCqRyYcviFbw= +github.com/speps/go-hashids v2.0.0+incompatible/go.mod h1:P7hqPzMdnZOfyIk+xrlG1QaSMw+gCBdHKsBDnhpaZvc= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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/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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= +github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/wechatpay-apiv3/wechatpay-go v0.2.21 h1:uIyMpzvcaHA33W/QPtHstccw+X52HO1gFdvVL9O6Lfs= +github.com/wechatpay-apiv3/wechatpay-go v0.2.21/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q= +github.com/xiaoqidun/qqwry v0.0.0-20250915110312-1dd385f77d98 h1:QzgLYAAaqALDmu1kCWITNzV38QvDL4HAWRAT/U7Mv2Y= +github.com/xiaoqidun/qqwry v0.0.0-20250915110312-1dd385f77d98/go.mod h1:hFWQBkHuMn+7Mt/J98d1V3WHgbTPMbbfUaRpw20Tuzo= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ= +github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw= +golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/image v0.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA= +golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-20201020160332-67f06af15bc9/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.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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.0-20210107192922-496545a6307b/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= +gorm.io/driver/mysql v1.5.4 h1:igQmHfKcbaTVyAIHNhhB888vvxh8EdQ2uSUT0LPcBso= +gorm.io/driver/mysql v1.5.4/go.mod h1:9rYxJph/u9SWkWc9yY4XJ1F/+xO0S/ChOmbk3+Z5Tvs= +gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= +modernc.org/fileutil v1.0.0 h1:Z1AFLZwl6BO8A5NldQg/xTSjGLetp+1Ubvl4alfGx8w= +modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..29cee66 --- /dev/null +++ b/start.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -x +ROOT=$(cd `dirname $0`; pwd) +cd $ROOT + +if [ "$BINARY" = "" ];then +BINARY="yintai-api" +fi + +while [ true ];do +$ROOT/bin/$BINARY $@ >> log/run.log 2>&1 +/usr/local/bin/sendproxy weixin "jiangyong,wangfuduo" "[$HOSTNAME] [$CONFIG_ENV] [$BINARY] 服务崩溃[`date +"%Y-%m-%d %H:%M:%S"`]" +sleep 10 +done + +