init: taotie-api 项目初始化

This commit is contained in:
2026-05-16 00:14:19 +08:00
commit eb15ef4b87
33 changed files with 1746 additions and 0 deletions

40
repo/mongodb.go Normal file
View 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
View 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
View 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
View 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
View 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
}