init: taotie-api 项目初始化
This commit is contained in:
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Binaries
|
||||||
|
tmp/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Env
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Go
|
||||||
|
*.exe
|
||||||
64
CODEBUDDY.md
Normal file
64
CODEBUDDY.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# CODEBUDDY.md
|
||||||
|
|
||||||
|
本文件为 CodeBuddy Code 在本仓库中工作时提供指导。
|
||||||
|
|
||||||
|
## 构建与运行命令
|
||||||
|
|
||||||
|
| 操作 | 命令 |
|
||||||
|
|---|---|
|
||||||
|
| 构建 | `go build -o tmp/main .` |
|
||||||
|
| 运行 | `go run .` |
|
||||||
|
| Wire 代码生成 | `go generate ./...`(修改 `wire.go` 后必须运行) |
|
||||||
|
| 整理依赖 | `go mod tidy` |
|
||||||
|
| 测试 | `go test ./...`(暂无测试文件) |
|
||||||
|
|
||||||
|
应用启动端口为 8000(配置在 `config/config.dev.yml`)。环境由 `.env` 控制(`ENV=dev` 对应 `config/config.{env}.yml`)。
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
Go 后端 API,使用 Gin + MongoDB + Wire 依赖注入。经典三层架构,DTO/DO/PO 模型分离。
|
||||||
|
|
||||||
|
### 请求流程
|
||||||
|
|
||||||
|
```
|
||||||
|
Client → Gin Router → wrap.Wrap[R,P](绑定/校验/响应)→ API Handler → Service → Repo → MongoDB
|
||||||
|
```
|
||||||
|
|
||||||
|
### 各层职责
|
||||||
|
|
||||||
|
- **API**(`api/`):路由、中间件、HTTP 处理器。`wrap.Wrap[R,P]()` 泛型包装器统一处理 JSON 绑定、参数校验(gookit/validate)和统一响应格式(`Resp{code, msg, data, ok, time}`)。
|
||||||
|
- **Service**(`service/`):业务逻辑。Wire 注入集定义在 `service.go`。
|
||||||
|
- **Repo**(`repo/`):数据访问。`BaseRepo[T]` 泛型仓储提供 CRUD 操作,支持软删除和租户隔离。子类添加实体特定查询。
|
||||||
|
- **Model**(`model/`):
|
||||||
|
- `dto/` — API 请求/响应结构体
|
||||||
|
- `do/` — Service 层输入/输出结构体
|
||||||
|
- `po/` — 数据库实体,均嵌入 `TBase`(OID、时间戳、创建人、tenantId)
|
||||||
|
|
||||||
|
### 关键设计模式
|
||||||
|
|
||||||
|
- **多租户隔离**:`BaseRepo.RawProcessFilter` 自动从 context 注入 `tenantId`(通过 `sctx` 包)。所有 PO 实体继承 `TBase.Tenant_OID`。
|
||||||
|
- **Wire 依赖注入**:`wire.go`(构建标签 `wireinject`)定义注入图,`wire_gen.go` 为自动生成代码。注入链:`Configuration → MongoDb → Repos → Services → APIs → Router → Engine`。
|
||||||
|
- **泛型 wrap 处理器**:`wrap.Wrap[R, P]()` 将请求绑定到 R,校验后调用 `func(context.Context, *R) (*P, error)`,成功返回 `Ok(c, data)`,失败返回 `Err(c, err)`。
|
||||||
|
- **错误码**:系统错误定义在 `common/errsys.go`(0=成功,50-53=内部错误,s0xxx=业务通用),用户错误定义在 `common/erruser.go`(u001-u004)。
|
||||||
|
|
||||||
|
### API 路由
|
||||||
|
|
||||||
|
```
|
||||||
|
GET / → Hello World
|
||||||
|
POST /api/v1/login → 用户登录(需 tenantNo + userName + password)
|
||||||
|
POST /api/v1/register → 用户注册
|
||||||
|
GET /api/v1/user/current → 获取当前用户信息(需认证)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置加载
|
||||||
|
|
||||||
|
1. `.env` 设置 `ENV` 变量
|
||||||
|
2. 通过 Viper 加载 `config/config.{ENV}.yml`
|
||||||
|
3. 详见 `core/config.go` 中的 `Configuration` 结构体和默认值
|
||||||
|
|
||||||
|
## 开发约定
|
||||||
|
|
||||||
|
- 修改 `wire.go` 后,务必运行 `go generate ./...` 重新生成 `wire_gen.go`。
|
||||||
|
- 新增 API 端点:在 `model/dto/` 定义 DTO,在 `api/v1/` 添加处理器,在 `api/api.go` 注册路由,实现 service 方法,按需添加 repo 方法。
|
||||||
|
- 新增实体:在 `model/po/` 创建 PO(嵌入 `TBase`),在 `model/do/` 创建 DO,创建 `XxxRepo` 继承 `BaseRepo[T]`,添加 Wire provider。
|
||||||
|
- 新增错误码:按现有模式在 `common/errsys.go` 或 `common/erruser.go` 中添加业务错误。
|
||||||
45
api/api.go
Normal file
45
api/api.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"taotie-api/api/middleware"
|
||||||
|
v1 "taotie-api/api/v1"
|
||||||
|
"taotie-api/api/wrap"
|
||||||
|
"taotie-api/core"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 依赖注入节点
|
||||||
|
var RouterProd = wire.NewSet(
|
||||||
|
NewRouter,
|
||||||
|
v1.NewUserApi,
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRouter(cfg *core.Configuration, userApi *v1.UserApi) *gin.Engine {
|
||||||
|
r := gin.Default()
|
||||||
|
|
||||||
|
// cors
|
||||||
|
r.Use(middleware.Cors())
|
||||||
|
|
||||||
|
// 定义路由
|
||||||
|
r.GET("/", func(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"message": "Hello, World!",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
apiv1 := r.Group("/api/v1")
|
||||||
|
{
|
||||||
|
apiv1.POST("/login", wrap.Wrap(userApi.Login))
|
||||||
|
apiv1.POST("/register", wrap.Wrap(userApi.Register))
|
||||||
|
}
|
||||||
|
|
||||||
|
apiv1_user := apiv1.Group("/user")
|
||||||
|
apiv1_user.Use(middleware.Auth(cfg))
|
||||||
|
{
|
||||||
|
apiv1_user.GET("/current", wrap.Wrap(userApi.GetCurrentUserInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
57
api/middleware/authMiddleware.go
Normal file
57
api/middleware/authMiddleware.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"taotie-api/common"
|
||||||
|
"taotie-api/core"
|
||||||
|
"taotie-api/utils/sctx"
|
||||||
|
"taotie-api/utils/sjwt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Auth(cfg *core.Configuration) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
authHeader := c.GetHeader("Authorization")
|
||||||
|
if authHeader == "" {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": common.ErrSysValidationFailed.Info().Id,
|
||||||
|
"msg": "缺少 Authorization 头",
|
||||||
|
"ok": false,
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(authHeader, " ", 2)
|
||||||
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": common.ErrSysValidationFailed.Info().Id,
|
||||||
|
"msg": "Authorization 格式错误,应为 Bearer <token>",
|
||||||
|
"ok": false,
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, err := sjwt.ParseToken(parts[1], cfg.JWT.SignString)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": common.ErrSysValidationFailed.Info().Id,
|
||||||
|
"msg": "token 无效或已过期",
|
||||||
|
"ok": false,
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sctx.SetCurrentUser(c, &sctx.CurrentUser{
|
||||||
|
UserId: claims.UserId,
|
||||||
|
UserName: claims.UserName,
|
||||||
|
TenantId: claims.TenantId,
|
||||||
|
})
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
19
api/middleware/corsMiddleware.go
Normal file
19
api/middleware/corsMiddleware.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/cors"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Cors() gin.HandlerFunc {
|
||||||
|
return cors.New(cors.Config{
|
||||||
|
AllowOrigins: []string{"*"}, // 允许所有来源(生产环境不建议使用)
|
||||||
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, // 允许的请求方法
|
||||||
|
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"}, // 允许的请求头部
|
||||||
|
ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin", "Authorization"}, // 允许客户端获取的响应头部
|
||||||
|
AllowCredentials: true, // 允许携带 Cookie
|
||||||
|
MaxAge: 12 * time.Hour, // 预检请求的缓存时间
|
||||||
|
})
|
||||||
|
}
|
||||||
85
api/v1/userapi.go
Normal file
85
api/v1/userapi.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"taotie-api/common"
|
||||||
|
"taotie-api/model/do/userdo"
|
||||||
|
"taotie-api/model/dto/userdto"
|
||||||
|
"taotie-api/service"
|
||||||
|
"taotie-api/utils/sctx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserApi struct {
|
||||||
|
userService *service.UserService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserApi(userService *service.UserService) *UserApi {
|
||||||
|
return &UserApi{userService}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login 登录
|
||||||
|
func (api *UserApi) Login(ctx context.Context, req *userdto.LoginReq) (*userdto.LoginRes, error) {
|
||||||
|
out, err := api.userService.Login(ctx, &userdo.LoginIn{
|
||||||
|
TenantNo: req.TenantNo,
|
||||||
|
UserName: req.UserName,
|
||||||
|
Password: req.Password,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录成功,返回 token
|
||||||
|
tokenOut, err := api.userService.GenToken(ctx, &userdo.GenTokenIn{
|
||||||
|
TenantNo: out.TenantId,
|
||||||
|
UserId: out.UserId,
|
||||||
|
UserName: out.UserName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userdto.LoginRes{
|
||||||
|
Token: tokenOut.Token,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register 注册
|
||||||
|
func (api *UserApi) Register(ctx context.Context, req *userdto.RegisterReq) (*userdto.RegisterRes, error) {
|
||||||
|
// 创建租户和用户
|
||||||
|
out, err := api.userService.Create(ctx, &userdo.CreateIn{
|
||||||
|
TenantName: req.TenantName,
|
||||||
|
UserName: req.UserName,
|
||||||
|
Password: req.Password,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动登录,生成 token
|
||||||
|
tokenOut, err := api.userService.GenToken(ctx, &userdo.GenTokenIn{
|
||||||
|
TenantNo: out.TenantId,
|
||||||
|
UserId: out.UserId,
|
||||||
|
UserName: out.UserName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userdto.RegisterRes{
|
||||||
|
Token: tokenOut.Token,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentUserInfo 获取当前用户信息
|
||||||
|
func (api *UserApi) GetCurrentUserInfo(ctx context.Context, req *userdto.GetCurrentUserInfoReq) (*userdto.GetCurrentUserInfoRes, error) {
|
||||||
|
cuser := sctx.GetCurrentUser(ctx)
|
||||||
|
if cuser == nil {
|
||||||
|
return nil, common.ErrUserNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userdto.GetCurrentUserInfoRes{
|
||||||
|
UserId: cuser.UserId,
|
||||||
|
UserName: cuser.UserName,
|
||||||
|
TenantId: cuser.TenantId,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
110
api/wrap/wrap.go
Normal file
110
api/wrap/wrap.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package wrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"reflect"
|
||||||
|
"taotie-api/common"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/v2/xerror"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gookit/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Wrap[R, P any](fn func(context.Context, *R) (*P, error)) func(c *gin.Context) {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// defer func() {
|
||||||
|
// if r := recover(); r != nil {
|
||||||
|
// Err(c, errors.New(fmt.Sprintf("panic: %v", r)))
|
||||||
|
// c.Abort()
|
||||||
|
// }
|
||||||
|
// }()
|
||||||
|
|
||||||
|
// 绑定请求参数
|
||||||
|
var req = new(R)
|
||||||
|
|
||||||
|
// 检查请求结构体是否为空结构体
|
||||||
|
reqType := reflect.TypeOf(req).Elem()
|
||||||
|
isEmptyStruct := reqType.NumField() == 0
|
||||||
|
|
||||||
|
if !isEmptyStruct {
|
||||||
|
if c.Request.ContentLength != 0 {
|
||||||
|
// 绑定请求参数
|
||||||
|
if err := c.ShouldBindJSON(req); err != nil {
|
||||||
|
er := common.ErrSysValidationFailed.Wrap(err)
|
||||||
|
Err(c, er)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验请求参数
|
||||||
|
v := validate.New(req)
|
||||||
|
if !v.Validate() {
|
||||||
|
er := common.ErrSysValidationFailed.Wrap(v.Errors.OneError())
|
||||||
|
Err(c, er)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用业务逻辑
|
||||||
|
res, err := fn(c, req)
|
||||||
|
if err != nil {
|
||||||
|
Err(c, err)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(c, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resp struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Data any `json:"data"`
|
||||||
|
Ok bool `json:"ok"`
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ok(c *gin.Context, data any) {
|
||||||
|
c.JSON(200, Resp{
|
||||||
|
Code: common.ErrSysOk.Info().Id,
|
||||||
|
Msg: common.ErrSysOk.Info().Message,
|
||||||
|
Data: data,
|
||||||
|
Ok: true,
|
||||||
|
Time: time.Now().Unix(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err 处理错误
|
||||||
|
// // 注意⚠️:这里需要想想是不是所有的错误都要返回给 web 端
|
||||||
|
func Err(c *gin.Context, err error) {
|
||||||
|
// 返回给 web 端的错误
|
||||||
|
var weberr = common.ErrSysInternal
|
||||||
|
|
||||||
|
// 提取最底层的错误
|
||||||
|
causeErr := xerror.Unwrap(err)
|
||||||
|
// 如果是自定义错误,解析错误,返回给 web 端
|
||||||
|
if causeErr != nil {
|
||||||
|
weberr = causeErr
|
||||||
|
} else {
|
||||||
|
slog.Error("【SRV-ERR】", "ERR", fmt.Errorf("%+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统内部错误的话,就不返回给 web 端,只记录。
|
||||||
|
|
||||||
|
// 记录日志
|
||||||
|
// slog.Error("【WEB-ERR】", "ERR", weberr)
|
||||||
|
// slog.Error("【SRV-ERR】", "ERR", fmt.Errorf("%+v", err))
|
||||||
|
|
||||||
|
c.JSON(200, Resp{
|
||||||
|
Code: weberr.Info().Id,
|
||||||
|
Msg: weberr.Info().Message,
|
||||||
|
Ok: false,
|
||||||
|
Time: time.Now().Unix(),
|
||||||
|
})
|
||||||
|
}
|
||||||
38
common/common.go
Normal file
38
common/common.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
type (
|
||||||
|
Map = map[string]any
|
||||||
|
MapAnyAny = map[any]any
|
||||||
|
MapAnyStr = map[any]string
|
||||||
|
MapAnyInt = map[any]int
|
||||||
|
MapAnyBool = map[any]bool
|
||||||
|
MapStrAny = map[string]any
|
||||||
|
MapStrStr = map[string]string
|
||||||
|
MapStrInt = map[string]int
|
||||||
|
MapStrBool = map[string]bool
|
||||||
|
MapIntAny = map[int]any
|
||||||
|
MapIntStr = map[int]string
|
||||||
|
MapIntInt = map[int]int
|
||||||
|
MapIntBool = map[int]bool
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
List = []Map
|
||||||
|
ListAnyAny = []MapAnyAny
|
||||||
|
ListAnyStr = []MapAnyStr
|
||||||
|
ListAnyInt = []MapAnyInt
|
||||||
|
ListAnyBool = []MapAnyBool
|
||||||
|
ListStrAny = []MapStrAny
|
||||||
|
ListStrStr = []MapStrStr
|
||||||
|
ListStrInt = []MapStrInt
|
||||||
|
ListStrBool = []MapStrBool
|
||||||
|
ListIntAny = []MapIntAny
|
||||||
|
ListIntStr = []MapIntStr
|
||||||
|
ListIntInt = []MapIntInt
|
||||||
|
ListIntBool = []MapIntBool
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
False = 0
|
||||||
|
True = 1
|
||||||
|
)
|
||||||
18
common/errsys.go
Normal file
18
common/errsys.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duke-git/lancet/v2/xerror"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrSysOk = xerror.New("ok").Id("0") // 成功
|
||||||
|
|
||||||
|
ErrSysInternal = xerror.New("未知内部错误").Id("50") // 未知内部错误
|
||||||
|
ErrSysNil = xerror.New("空指针").Id("51") // 空指针
|
||||||
|
ErrSysValidationFailed = xerror.New("校验失败").Id("52") // 校验失败
|
||||||
|
ErrSysDbError = xerror.New("数据库错误").Id("53") // 数据库错误
|
||||||
|
|
||||||
|
ErrSysDuplicate = xerror.New("重复数据").Id("s0001") // 重复数据
|
||||||
|
ErrSysInvalid = xerror.New("数据无效").Id("s0002") // 数据无效
|
||||||
|
ErrSysDataNotFound = xerror.New("数据不存在").Id("s0003") // 数据不存在
|
||||||
|
)
|
||||||
10
common/erruser.go
Normal file
10
common/erruser.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import "github.com/duke-git/lancet/v2/xerror"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUserNotFound = xerror.New("用户不存在").Id("u001") // 用户不存在
|
||||||
|
ErrUserPasswordError = xerror.New("密码错误").Id("u002") // 密码错误
|
||||||
|
ErrUserTenantNotFound = xerror.New("租户不存在").Id("u003") // 租户不存在
|
||||||
|
ErrUserGenTokenFailed = xerror.New("生成token失败").Id("u004") // 生成token失败
|
||||||
|
)
|
||||||
22
config/config.dev.yml
Normal file
22
config/config.dev.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
server:
|
||||||
|
port: 8000
|
||||||
|
|
||||||
|
log:
|
||||||
|
# const (
|
||||||
|
# LevelDebug Level = -4
|
||||||
|
# LevelInfo Level = 0
|
||||||
|
# LevelWarn Level = 4
|
||||||
|
# LevelError Level = 8
|
||||||
|
# )
|
||||||
|
path: log/app.log
|
||||||
|
level: -4
|
||||||
|
|
||||||
|
db:
|
||||||
|
mongoUri: mongodb://localhost:27017
|
||||||
|
dbName: taotie
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
# 签名字符串
|
||||||
|
signString: taotie-api-q2e
|
||||||
|
# 过期时间,单位秒
|
||||||
|
timeOut: 3600
|
||||||
84
core/config.go
Normal file
84
core/config.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Configuration struct {
|
||||||
|
Server struct {
|
||||||
|
Port string `yaml:"port"`
|
||||||
|
} `yaml:"server"`
|
||||||
|
|
||||||
|
Log struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Level slog.Level `json:"level"`
|
||||||
|
} `yaml:"log"`
|
||||||
|
|
||||||
|
DB struct {
|
||||||
|
MongoURI string `yaml:"mongoUri"`
|
||||||
|
DBName string `yaml:"dbName"`
|
||||||
|
} `yaml:"db"`
|
||||||
|
|
||||||
|
// JWT 配置
|
||||||
|
JWT struct {
|
||||||
|
SignString string `yaml:"signString"`
|
||||||
|
TimeOut int `yaml:"timeOut"`
|
||||||
|
} `yaml:"jwt"`
|
||||||
|
|
||||||
|
Viper *viper.Viper `yaml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化配置
|
||||||
|
func NewConfiguration() (cfg *Configuration, err error) {
|
||||||
|
cfg = &Configuration{}
|
||||||
|
|
||||||
|
// 加载 .env 文件
|
||||||
|
err = godotenv.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取环境配置
|
||||||
|
env := os.Getenv("ENV")
|
||||||
|
|
||||||
|
filename := ""
|
||||||
|
if env == "" {
|
||||||
|
filename = "config/config.yml"
|
||||||
|
} else {
|
||||||
|
filename = fmt.Sprintf("config/config.%v.yml", env)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取配置文件
|
||||||
|
v := viper.New()
|
||||||
|
|
||||||
|
// 设置默认值
|
||||||
|
v.SetDefault("server.port", "8000")
|
||||||
|
v.SetDefault("log.path", "log/app.log")
|
||||||
|
v.SetDefault("log.level", 0)
|
||||||
|
v.SetDefault("jwt.signString", "taotie-api-q2e")
|
||||||
|
v.SetDefault("jwt.timeOut", 3600)
|
||||||
|
v.SetDefault("db.mongoUri", "mongodb://localhost:27017")
|
||||||
|
v.SetDefault("db.dbName", "taotie")
|
||||||
|
|
||||||
|
// 设置配置文件路径
|
||||||
|
v.SetConfigFile(filename)
|
||||||
|
|
||||||
|
// 试着读取文件
|
||||||
|
err = v.ReadInConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = v.Unmarshal(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Viper = v
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
68
go.mod
Normal file
68
go.mod
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
module taotie-api
|
||||||
|
|
||||||
|
go 1.26.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/duke-git/lancet v1.4.6
|
||||||
|
github.com/duke-git/lancet/v2 v2.3.9
|
||||||
|
github.com/gin-contrib/cors v1.7.7
|
||||||
|
github.com/gin-gonic/gin v1.12.0
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
|
github.com/google/wire v0.7.0
|
||||||
|
github.com/gookit/goutil v0.7.4
|
||||||
|
github.com/gookit/validate v1.5.7
|
||||||
|
github.com/joho/godotenv v1.5.1
|
||||||
|
github.com/spf13/viper v1.21.0
|
||||||
|
go.mongodb.org/mongo-driver v1.17.9
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||||
|
github.com/bytedance/sonic v1.15.0 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||||
|
github.com/gin-contrib/sse v1.1.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.30.1 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
|
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/gookit/filter v1.2.3 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.6 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
|
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // 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/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
|
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
|
golang.org/x/arch v0.23.0 // indirect
|
||||||
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a // indirect
|
||||||
|
golang.org/x/net v0.51.0 // indirect
|
||||||
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
|
)
|
||||||
184
go.sum
Normal file
184
go.sum
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
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.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||||
|
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||||
|
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||||
|
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||||
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
|
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/duke-git/lancet v1.4.6 h1:pFTA06baQ8OceOmJB9tOsGz60y6GsfXOevIJVIFhGfg=
|
||||||
|
github.com/duke-git/lancet v1.4.6/go.mod h1:Grr6ehF0ig2nRIjeb+NmcxiJ12mkML4XQAx95tlQeJU=
|
||||||
|
github.com/duke-git/lancet/v2 v2.3.9 h1:ZxUvfoEY7YbsGIeoXRxHWIkRCAt6VN7UBKWgCCqBB3U=
|
||||||
|
github.com/duke-git/lancet/v2 v2.3.9/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
||||||
|
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.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.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||||
|
github.com/gin-contrib/cors v1.7.7 h1:Oh9joP463x7Mw72vhvJ61YQm8ODh9b04YR7vsOErD0Q=
|
||||||
|
github.com/gin-contrib/cors v1.7.7/go.mod h1:K5tW0RkzJtWSiOdikXloy8VEZlgdVNpHNw8FpjUPNrE=
|
||||||
|
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.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
||||||
|
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||||
|
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||||
|
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.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||||
|
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
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/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
|
||||||
|
github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18=
|
||||||
|
github.com/gookit/filter v1.2.3 h1:Zo7cBOtsVzAoa/jtf+Ury6zlsbJXqInFdUpbbnB2vMM=
|
||||||
|
github.com/gookit/filter v1.2.3/go.mod h1:nFLJcOV8dRgS1iiX23gUQgmHUhpuS40qCvAGgIvA1pM=
|
||||||
|
github.com/gookit/goutil v0.7.4 h1:OWgUngToNz+bPlX5aP+EMG31DraEU63uvKMwwT3vseM=
|
||||||
|
github.com/gookit/goutil v0.7.4/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU=
|
||||||
|
github.com/gookit/validate v1.5.7 h1:W0nqxAbgCJ4ND/oR+Tx8yyYn6l+PkFVvGHlG7LxpYsc=
|
||||||
|
github.com/gookit/validate v1.5.7/go.mod h1:VeThM1saXWVJIG/Yn627PJL9yvap0s4bsjPUIV8I0UI=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
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/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
||||||
|
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
|
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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
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.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||||
|
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||||
|
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||||
|
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||||
|
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||||
|
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/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
|
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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
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/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.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||||
|
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU=
|
||||||
|
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
|
||||||
|
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||||
|
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||||
|
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.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||||
|
golang.org/x/arch v0.23.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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||||
|
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||||
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
|
||||||
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||||
|
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||||
|
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||||
|
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||||
|
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
29
main.go
Normal file
29
main.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"taotie-api/core"
|
||||||
|
|
||||||
|
"github.com/gookit/validate/locales/zhcn"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 初始化应用
|
||||||
|
r, err := InitApp()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化配置
|
||||||
|
cfg, err := core.NewConfiguration()
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("初始化配置失败", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for all Validation.
|
||||||
|
// NOTICE: 必须在调用 validate.New() 前注册, 它只需要一次调用。
|
||||||
|
zhcn.RegisterGlobal()
|
||||||
|
|
||||||
|
// 启动服务器
|
||||||
|
r.Run(":" + cfg.Server.Port)
|
||||||
|
}
|
||||||
9
model/do/tenantdo/enter.go
Normal file
9
model/do/tenantdo/enter.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package tenantdo
|
||||||
|
|
||||||
|
type CreateIn struct {
|
||||||
|
TenantName string `json:"tenantName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateOut struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
35
model/do/userdo/enter.go
Normal file
35
model/do/userdo/enter.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package userdo
|
||||||
|
|
||||||
|
type LoginIn struct {
|
||||||
|
TenantNo string `json:"tenantNo"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginOut struct {
|
||||||
|
TenantId string `json:"tenantId"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenTokenIn struct {
|
||||||
|
TenantNo string `json:"tenantNo"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenTokenOut struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateIn struct {
|
||||||
|
TenantName string `json:"tenantName"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateOut struct {
|
||||||
|
TenantId string `json:"tenantId"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
}
|
||||||
29
model/dto/userdto/enter.go
Normal file
29
model/dto/userdto/enter.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package userdto
|
||||||
|
|
||||||
|
type LoginReq struct {
|
||||||
|
TenantNo string `json:"tenantNo"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginRes struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegisterReq struct {
|
||||||
|
TenantName string `json:"tenantName"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegisterRes struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetCurrentUserInfoReq struct{}
|
||||||
|
|
||||||
|
type GetCurrentUserInfoRes struct {
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
TenantId string `json:"tenantId"`
|
||||||
|
}
|
||||||
43
model/po/tbase.go
Normal file
43
model/po/tbase.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package po
|
||||||
|
|
||||||
|
import "go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
|
||||||
|
type TBase struct {
|
||||||
|
OID primitive.ObjectID `bson:"_id,omitempty"` // 主键
|
||||||
|
CreatedAt int64 `bson:"createdAt"` // 创建时间
|
||||||
|
UpdatedAt int64 `bson:"updatedAt"` // 更新时间
|
||||||
|
DeletedAt int64 `bson:"deletedAt"` // 删除时间
|
||||||
|
CreatedBy_OID primitive.ObjectID `bson:"createdBy"` // 创建人OID
|
||||||
|
Tenant_OID primitive.ObjectID `bson:"tenantId"` // 租户ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Id 获取主键
|
||||||
|
func (t *TBase) Id() string {
|
||||||
|
return t.OID.Hex()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedBy 获取创建人 Id
|
||||||
|
func (t *TBase) CreatedBy() string {
|
||||||
|
return t.CreatedBy_OID.Hex()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCreatedBy 设置创建人 Id
|
||||||
|
func (t *TBase) SetCreatedBy(createdBy string) {
|
||||||
|
t.CreatedBy_OID, _ = primitive.ObjectIDFromHex(createdBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TenantId 获取租户 Id
|
||||||
|
func (t *TBase) TenantId() string {
|
||||||
|
return t.Tenant_OID.Hex()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTenantId 设置租户 Id
|
||||||
|
func (t *TBase) SetTenantId(tenantId string) {
|
||||||
|
t.Tenant_OID, _ = primitive.ObjectIDFromHex(tenantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNow 设置当前时间
|
||||||
|
func (t *TBase) SetNow(now int64) {
|
||||||
|
t.CreatedAt = now
|
||||||
|
t.UpdatedAt = now
|
||||||
|
}
|
||||||
8
model/po/ttenant.go
Normal file
8
model/po/ttenant.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package po
|
||||||
|
|
||||||
|
type TTenant struct {
|
||||||
|
TBase `bson:",inline"`
|
||||||
|
|
||||||
|
TenantNo string `bson:"tenantNo"`
|
||||||
|
TenantName string `bson:"tenantName"`
|
||||||
|
}
|
||||||
9
model/po/tuser.go
Normal file
9
model/po/tuser.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package po
|
||||||
|
|
||||||
|
type TUser struct {
|
||||||
|
TBase `bson:",inline"`
|
||||||
|
|
||||||
|
NickName string `bson:"nickName"` // 昵称
|
||||||
|
UserName string `bson:"userName"` // 用户名
|
||||||
|
Password string `bson:"password"` // 密码
|
||||||
|
}
|
||||||
40
repo/mongodb.go
Normal file
40
repo/mongodb.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"taotie-api/core"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MongoDb struct {
|
||||||
|
client *mongo.Client
|
||||||
|
cfg *core.Configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMongoDb 创建一个新的 MongoDb 实例
|
||||||
|
func NewMongoDb(cfg *core.Configuration) (*MongoDb, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 连接数据库
|
||||||
|
client, err := mongo.Connect(ctx, options.Client().ApplyURI(cfg.DB.MongoURI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 检查连接是否成功
|
||||||
|
err = client.Ping(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &MongoDb{
|
||||||
|
client: client,
|
||||||
|
cfg: cfg,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Db 返回数据库实例
|
||||||
|
func (m *MongoDb) Db() *mongo.Database {
|
||||||
|
return m.client.Database(m.cfg.DB.DBName)
|
||||||
|
}
|
||||||
294
repo/repo.go
Normal file
294
repo/repo.go
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"taotie-api/utils/sctx"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/slice"
|
||||||
|
"github.com/google/wire"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 依赖注入节点
|
||||||
|
var RepoProd = wire.NewSet(
|
||||||
|
NewUserRepo,
|
||||||
|
NewTenantRepo,
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseRepo[T IEntity] struct {
|
||||||
|
mdb *MongoDb
|
||||||
|
tableName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessFilter 处理查询条件
|
||||||
|
func (rp *BaseRepo[T]) RawProcessFilter(ctx context.Context, qp *QueryParams) bson.M {
|
||||||
|
filter := bson.M{}
|
||||||
|
|
||||||
|
// 查询条件
|
||||||
|
if len(qp.Where) != 0 {
|
||||||
|
wh := make(map[string]any, 0)
|
||||||
|
for k, v := range qp.Where {
|
||||||
|
if (k[len(k)-2:]) == "Id" {
|
||||||
|
oid, _ := primitive.ObjectIDFromHex(v.(string))
|
||||||
|
wh[k] = oid
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wh[k] = v
|
||||||
|
}
|
||||||
|
filter = wh
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 ids
|
||||||
|
if len(qp.WhereInIds) != 0 {
|
||||||
|
oids := make([]primitive.ObjectID, 0)
|
||||||
|
for _, id := range qp.WhereInIds {
|
||||||
|
oid, _ := primitive.ObjectIDFromHex(id)
|
||||||
|
oids = append(oids, oid)
|
||||||
|
}
|
||||||
|
filter["_id"] = bson.M{"$in": oids}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤删除
|
||||||
|
filter["deletedAt"] = 0
|
||||||
|
|
||||||
|
// 过滤租户
|
||||||
|
cuser := sctx.GetCurrentUser(ctx)
|
||||||
|
if cuser != nil {
|
||||||
|
filter["tenantId"], _ = primitive.ObjectIDFromHex(cuser.TenantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessSetData 处理需要修改的数据,限制只能修改已拥有的字段
|
||||||
|
func (rp *BaseRepo[T]) RawProcessSetData(ctx context.Context, setData map[string]any) (map[string]any, error) {
|
||||||
|
// 提取结构体
|
||||||
|
t := reflect.TypeFor[T]().Elem()
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取结构体字段
|
||||||
|
fields := make([]string, 0, t.NumField())
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
var bs = t.Field(i).Tag.Get("bson")
|
||||||
|
if bs == ",inline" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields = append(fields, bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选
|
||||||
|
sd := make(map[string]any, 0)
|
||||||
|
for k, v := range setData {
|
||||||
|
if slice.Contain(fields, k) {
|
||||||
|
// 处理 ObjectID 类型
|
||||||
|
if (k[len(k)-2:]) == "Id" {
|
||||||
|
oid, _ := primitive.ObjectIDFromHex(v.(string))
|
||||||
|
sd[k] = oid
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (k[len(k)-3:]) == "Ids" {
|
||||||
|
oids := make([]primitive.ObjectID, 0)
|
||||||
|
for _, id := range v.([]any) {
|
||||||
|
oid, _ := primitive.ObjectIDFromHex(id.(string))
|
||||||
|
oids = append(oids, oid)
|
||||||
|
}
|
||||||
|
sd[k] = oids
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sd[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建
|
||||||
|
func (rp *BaseRepo[T]) Create(ctx context.Context, tdata T) (string, error) {
|
||||||
|
collection := rp.mdb.Db().Collection(rp.tableName)
|
||||||
|
|
||||||
|
// 设置默认值
|
||||||
|
cuser := sctx.GetCurrentUser(ctx)
|
||||||
|
tdata.SetCreatedBy(cuser.UserId) // 默认创建人
|
||||||
|
tdata.SetTenantId(cuser.TenantId) // 默认租户
|
||||||
|
|
||||||
|
// 设置基础时间
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
tdata.SetNow(now)
|
||||||
|
|
||||||
|
result, err := collection.InsertOne(ctx, tdata)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入成功后,返回插入的ID
|
||||||
|
return result.InsertedID.(primitive.ObjectID).Hex(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMany 创建多个
|
||||||
|
func (rp *BaseRepo[T]) CreateMany(ctx context.Context, tdatas []T) (ids []string, err error) {
|
||||||
|
collection := rp.mdb.Db().Collection(rp.tableName)
|
||||||
|
|
||||||
|
// 设置默认值
|
||||||
|
cuser := sctx.GetCurrentUser(ctx)
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
tdataanys := make([]any, 0, len(tdatas))
|
||||||
|
for _, v := range tdatas {
|
||||||
|
if cuser != nil {
|
||||||
|
v.SetCreatedBy(cuser.UserId) // 默认创建人
|
||||||
|
v.SetTenantId(cuser.TenantId) // 默认租户
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置基础时间
|
||||||
|
v.SetNow(now)
|
||||||
|
|
||||||
|
// 转换
|
||||||
|
tdataanys = append(tdataanys, v)
|
||||||
|
}
|
||||||
|
// 插入数据
|
||||||
|
result, err := collection.InsertMany(ctx, tdataanys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = make([]string, 0, len(result.InsertedIDs))
|
||||||
|
for _, id := range result.InsertedIDs {
|
||||||
|
ids = append(ids, id.(primitive.ObjectID).Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除(软删除)
|
||||||
|
func (rp *BaseRepo[T]) Delete(ctx context.Context, qp *QueryParams) error {
|
||||||
|
collection := rp.mdb.Db().Collection(rp.tableName)
|
||||||
|
|
||||||
|
filter := rp.RawProcessFilter(ctx, qp)
|
||||||
|
|
||||||
|
// 查询参数不能为空
|
||||||
|
if len(filter) == 0 {
|
||||||
|
return errors.New("查询参数不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 软删除
|
||||||
|
_, err := collection.UpdateMany(ctx, filter, bson.M{"$set": bson.M{"deletedAt": time.Now().UnixMilli()}})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp *BaseRepo[T]) Update(ctx context.Context, qp *QueryParams, setData map[string]any) error {
|
||||||
|
collection := rp.mdb.Db().Collection(rp.tableName)
|
||||||
|
|
||||||
|
sd, err := rp.RawProcessSetData(ctx, setData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有可更新的字段
|
||||||
|
if len(sd) == 0 {
|
||||||
|
return errors.New("更新参数不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新时间
|
||||||
|
sd["updatedAt"] = time.Now().UnixMilli()
|
||||||
|
|
||||||
|
filter := rp.RawProcessFilter(ctx, qp)
|
||||||
|
|
||||||
|
if len(filter) == 0 {
|
||||||
|
return errors.New("查询参数不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新数据
|
||||||
|
_, err = collection.UpdateMany(ctx, filter, bson.M{"$set": sd})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count 查询数量
|
||||||
|
func (rp *BaseRepo[T]) RawCount(ctx context.Context, qp *QueryParams) (int64, error) {
|
||||||
|
collection := rp.mdb.Db().Collection(rp.tableName)
|
||||||
|
|
||||||
|
filter := rp.RawProcessFilter(ctx, qp)
|
||||||
|
|
||||||
|
// 查询总量
|
||||||
|
total, err := collection.CountDocuments(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find 查询
|
||||||
|
func (rp *BaseRepo[T]) RawFind(ctx context.Context, qp *QueryParams) (results []T, total int64, err error) {
|
||||||
|
collection := rp.mdb.Db().Collection(rp.tableName)
|
||||||
|
|
||||||
|
filter := rp.RawProcessFilter(ctx, qp)
|
||||||
|
|
||||||
|
opts := &options.FindOptions{}
|
||||||
|
|
||||||
|
// 设置分页
|
||||||
|
if qp.Page > 0 && qp.PageSize > 0 {
|
||||||
|
opts.SetSkip(int64((qp.Page - 1) * qp.PageSize))
|
||||||
|
opts.SetLimit(int64(qp.PageSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置排序
|
||||||
|
if len(qp.OrderBy) != 0 {
|
||||||
|
opts.SetSort(qp.OrderBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行查询
|
||||||
|
cursor, err := collection.Find(ctx, filter, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
defer cursor.Close(ctx)
|
||||||
|
|
||||||
|
// 解析结果
|
||||||
|
if err = cursor.All(ctx, &results); err != nil {
|
||||||
|
// 处理空文档错误
|
||||||
|
if err == mongo.ErrNilDocument {
|
||||||
|
return nil, 0, nil
|
||||||
|
}
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询总量
|
||||||
|
total, err = collection.CountDocuments(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindOne 查询单条记录
|
||||||
|
func (rp *BaseRepo[T]) RawFindOne(ctx context.Context, qp *QueryParams) (result T, err error) {
|
||||||
|
collection := rp.mdb.Db().Collection(rp.tableName)
|
||||||
|
|
||||||
|
filter := rp.RawProcessFilter(ctx, qp)
|
||||||
|
|
||||||
|
// 执行查询
|
||||||
|
err = collection.FindOne(ctx, filter).Decode(&result)
|
||||||
|
if err != nil {
|
||||||
|
// 处理空文档错误
|
||||||
|
if err == mongo.ErrNilDocument {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
36
repo/tenantrepo.go
Normal file
36
repo/tenantrepo.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"taotie-api/common"
|
||||||
|
"taotie-api/model/po"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TenantRepo[T IEntity] struct {
|
||||||
|
BaseRepo[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTenantRepo(mdb *MongoDb) *TenantRepo[*po.TTenant] {
|
||||||
|
return &TenantRepo[*po.TTenant]{
|
||||||
|
BaseRepo: BaseRepo[*po.TTenant]{
|
||||||
|
mdb: mdb,
|
||||||
|
tableName: "ttenant",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp *TenantRepo[T]) FindOneByTenantNo(ctx context.Context, tenantNo string) (result T, err error) {
|
||||||
|
return rp.RawFindOne(ctx, &QueryParams{
|
||||||
|
Where: common.Map{
|
||||||
|
"tenantNo": tenantNo,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp *TenantRepo[T]) FindOneByTenantName(ctx context.Context, tenantName string) (result T, err error) {
|
||||||
|
return rp.RawFindOne(ctx, &QueryParams{
|
||||||
|
Where: common.Map{
|
||||||
|
"tenantName": tenantName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
18
repo/types.go
Normal file
18
repo/types.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
type IEntity interface {
|
||||||
|
Id() string
|
||||||
|
CreatedBy() string
|
||||||
|
SetCreatedBy(string)
|
||||||
|
TenantId() string
|
||||||
|
SetTenantId(string)
|
||||||
|
SetNow(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryParams struct {
|
||||||
|
Page int
|
||||||
|
PageSize int
|
||||||
|
Where map[string]any
|
||||||
|
WhereInIds []string
|
||||||
|
OrderBy []string
|
||||||
|
}
|
||||||
39
repo/userrepo.go
Normal file
39
repo/userrepo.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"taotie-api/common"
|
||||||
|
"taotie-api/model/po"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRepo[T IEntity] struct {
|
||||||
|
BaseRepo[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserRepo(mdb *MongoDb) *UserRepo[*po.TUser] {
|
||||||
|
return &UserRepo[*po.TUser]{
|
||||||
|
BaseRepo[*po.TUser]{
|
||||||
|
mdb: mdb,
|
||||||
|
tableName: "tuser",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp *UserRepo[T]) FindOneByTenantIdAndUserName(ctx context.Context, tenantId string, userName string) (result T, err error) {
|
||||||
|
result, err = rp.RawFindOne(ctx, &QueryParams{
|
||||||
|
Where: common.Map{
|
||||||
|
"tenantId": tenantId,
|
||||||
|
"userName": userName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp *UserRepo[T]) Update(ctx context.Context, qp *QueryParams, setData map[string]any) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
11
service/service.go
Normal file
11
service/service.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 依赖注入节点
|
||||||
|
var ServiceProd = wire.NewSet(
|
||||||
|
NewUserService,
|
||||||
|
NewTenantService,
|
||||||
|
)
|
||||||
45
service/tenantservice.go
Normal file
45
service/tenantservice.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"taotie-api/common"
|
||||||
|
"taotie-api/core"
|
||||||
|
"taotie-api/model/do/tenantdo"
|
||||||
|
"taotie-api/model/po"
|
||||||
|
"taotie-api/repo"
|
||||||
|
|
||||||
|
"github.com/gookit/goutil/errorx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TenantService struct {
|
||||||
|
cfg *core.Configuration
|
||||||
|
tenantRepo *repo.TenantRepo[*po.TTenant]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTenantService(cfg *core.Configuration, tenantRepo *repo.TenantRepo[*po.TTenant]) *TenantService {
|
||||||
|
return &TenantService{cfg, tenantRepo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp *TenantService) Create(ctx context.Context, in *tenantdo.CreateIn) (*tenantdo.CreateOut, error) {
|
||||||
|
// 查重
|
||||||
|
tenant, err := rp.tenantRepo.FindOneByTenantName(ctx, in.TenantName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tenant != nil {
|
||||||
|
return nil, errorx.With(common.ErrSysDuplicate, "租户名称重复")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建租户
|
||||||
|
tenant = &po.TTenant{
|
||||||
|
TenantName: in.TenantName,
|
||||||
|
}
|
||||||
|
tenantId, err := rp.tenantRepo.Create(ctx, tenant)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tenantdo.CreateOut{
|
||||||
|
Id: tenantId,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
125
service/userservice.go
Normal file
125
service/userservice.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"taotie-api/common"
|
||||||
|
"taotie-api/core"
|
||||||
|
"taotie-api/model/do/userdo"
|
||||||
|
"taotie-api/model/po"
|
||||||
|
"taotie-api/repo"
|
||||||
|
"taotie-api/utils/sctx"
|
||||||
|
"taotie-api/utils/sjwt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gookit/goutil/errorx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserService struct {
|
||||||
|
cfg *core.Configuration
|
||||||
|
userRepo *repo.UserRepo[*po.TUser]
|
||||||
|
tenantRepo *repo.TenantRepo[*po.TTenant]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserService(mdb *repo.MongoDb, cfg *core.Configuration, userRepo *repo.UserRepo[*po.TUser], tenantRepo *repo.TenantRepo[*po.TTenant]) *UserService {
|
||||||
|
return &UserService{
|
||||||
|
cfg,
|
||||||
|
userRepo,
|
||||||
|
tenantRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login 登录
|
||||||
|
func (s *UserService) Login(ctx context.Context, in *userdo.LoginIn) (*userdo.LoginOut, error) {
|
||||||
|
// 验证租户
|
||||||
|
ttenant, err := s.tenantRepo.FindOneByTenantNo(ctx, in.TenantNo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ttenant == nil {
|
||||||
|
return nil, common.ErrUserTenantNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证用户
|
||||||
|
tuser, err := s.userRepo.FindOneByTenantIdAndUserName(ctx, ttenant.Id(), in.UserName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tuser == nil {
|
||||||
|
return nil, common.ErrUserNotFound
|
||||||
|
}
|
||||||
|
if tuser.Password != in.Password {
|
||||||
|
return nil, common.ErrUserPasswordError
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userdo.LoginOut{
|
||||||
|
TenantId: ttenant.Id(),
|
||||||
|
UserId: tuser.Id(),
|
||||||
|
UserName: tuser.UserName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenToken 生成token
|
||||||
|
func (s *UserService) GenToken(ctx context.Context, in *userdo.GenTokenIn) (*userdo.GenTokenOut, error) {
|
||||||
|
token, err := sjwt.GenerateToken(in.TenantNo, in.UserId, in.UserName, s.cfg.JWT.TimeOut, s.cfg.JWT.SignString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &userdo.GenTokenOut{
|
||||||
|
Token: token,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserService) Create(ctx context.Context, in *userdo.CreateIn) (*userdo.CreateOut, error) {
|
||||||
|
// 查重:租户名称
|
||||||
|
ttenant, err := s.tenantRepo.FindOneByTenantName(ctx, in.TenantName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ttenant != nil {
|
||||||
|
return nil, errorx.With(common.ErrSysDuplicate, "租户名称重复")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先设置临时用户到 context(避免 BaseRepo.Create 中 cuser 为 nil 导致 panic)
|
||||||
|
sctx.SetCurrentUser(ctx, &sctx.CurrentUser{
|
||||||
|
UserId: "",
|
||||||
|
UserName: in.UserName,
|
||||||
|
TenantId: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
// 生成 tenantNo(基于时间戳)
|
||||||
|
tenantNo := fmt.Sprintf("T%d", time.Now().UnixMilli())
|
||||||
|
|
||||||
|
// 创建租户
|
||||||
|
ttenant = &po.TTenant{
|
||||||
|
TenantNo: tenantNo,
|
||||||
|
TenantName: in.TenantName,
|
||||||
|
}
|
||||||
|
tenantId, err := s.tenantRepo.Create(ctx, ttenant)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 context 中的 TenantId
|
||||||
|
sctx.SetCurrentUser(ctx, &sctx.CurrentUser{
|
||||||
|
UserId: "",
|
||||||
|
UserName: in.UserName,
|
||||||
|
TenantId: tenantId,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建用户
|
||||||
|
tuser := &po.TUser{
|
||||||
|
UserName: in.UserName,
|
||||||
|
Password: in.Password,
|
||||||
|
}
|
||||||
|
userId, err := s.userRepo.Create(ctx, tuser)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userdo.CreateOut{
|
||||||
|
TenantId: tenantId,
|
||||||
|
UserId: userId,
|
||||||
|
UserName: in.UserName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
42
utils/sctx/sctx.go
Normal file
42
utils/sctx/sctx.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package sctx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyKey string
|
||||||
|
|
||||||
|
const CurrentKey MyKey = "taotie"
|
||||||
|
|
||||||
|
type CurrentUser struct {
|
||||||
|
UserId string
|
||||||
|
UserName string
|
||||||
|
TenantId string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCurrentUser(ctx context.Context, cuser *CurrentUser) context.Context {
|
||||||
|
if c, ok := ctx.(*gin.Context); ok {
|
||||||
|
c.Set(CurrentKey, cuser)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.WithValue(ctx, CurrentKey, cuser)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCurrentUser(ctx context.Context) *CurrentUser {
|
||||||
|
if c, ok := ctx.(*gin.Context); ok {
|
||||||
|
if user, ok := c.Get(CurrentKey); ok {
|
||||||
|
if cuser, ok := user.(*CurrentUser); ok {
|
||||||
|
return cuser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cuser, ok := ctx.Value(CurrentKey).(*CurrentUser); ok {
|
||||||
|
return cuser
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
50
utils/sjwt/sjwt.go
Normal file
50
utils/sjwt/sjwt.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package sjwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyClaims struct {
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
TenantId string `json:"tenantId"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成 token
|
||||||
|
func GenerateToken(tenantId string, userId string, userName string, timeOut int, signedString string) (string, error) {
|
||||||
|
// 创建一个claims
|
||||||
|
claims := jwt.NewWithClaims(jwt.SigningMethodHS256, MyClaims{
|
||||||
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(timeOut))),
|
||||||
|
Issuer: "taotie",
|
||||||
|
},
|
||||||
|
TenantId: tenantId,
|
||||||
|
UserId: userId,
|
||||||
|
UserName: userName,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 生成 token
|
||||||
|
return claims.SignedString([]byte(signedString))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 token
|
||||||
|
func ParseToken(tokenString string, signedString string) (*MyClaims, error) {
|
||||||
|
// 解析token
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (any, error) {
|
||||||
|
return []byte(signedString), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对token进行断言
|
||||||
|
if claims, ok := token.Claims.(*MyClaims); ok {
|
||||||
|
return claims, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
29
wire.go
Normal file
29
wire.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//go:build wireinject
|
||||||
|
// +build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"taotie-api/api"
|
||||||
|
"taotie-api/core"
|
||||||
|
"taotie-api/repo"
|
||||||
|
"taotie-api/service"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitApp() (*gin.Engine, error) {
|
||||||
|
wire.Build(
|
||||||
|
core.NewConfiguration,
|
||||||
|
repo.NewMongoDb,
|
||||||
|
|
||||||
|
api.RouterProd,
|
||||||
|
|
||||||
|
service.ServiceProd,
|
||||||
|
|
||||||
|
repo.RepoProd,
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
35
wire_gen.go
Normal file
35
wire_gen.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"taotie-api/api"
|
||||||
|
"taotie-api/api/v1"
|
||||||
|
"taotie-api/core"
|
||||||
|
"taotie-api/repo"
|
||||||
|
"taotie-api/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func InitApp() (*gin.Engine, error) {
|
||||||
|
configuration, err := core.NewConfiguration()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mongoDb, err := repo.NewMongoDb(configuration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userRepo := repo.NewUserRepo(mongoDb)
|
||||||
|
tenantRepo := repo.NewTenantRepo(mongoDb)
|
||||||
|
userService := service.NewUserService(mongoDb, configuration, userRepo, tenantRepo)
|
||||||
|
userApi := v1.NewUserApi(userService)
|
||||||
|
engine := api.NewRouter(configuration, userApi)
|
||||||
|
return engine, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user