backend/pkg/permission/repository/permission.go

228 lines
6.0 KiB
Go
Raw Permalink Normal View History

2025-10-07 09:29:47 +00:00
package repository
import (
"backend/pkg/library/mongo"
"backend/pkg/permission/domain"
"backend/pkg/permission/domain/entity"
"backend/pkg/permission/domain/repository"
"context"
"errors"
2025-10-10 15:25:36 +00:00
"strings"
2025-10-07 09:29:47 +00:00
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/v2/bson"
2025-10-10 15:25:36 +00:00
mongodriver "go.mongodb.org/mongo-driver/v2/mongo"
2025-10-07 09:29:47 +00:00
)
type PermissionRepositoryParam struct {
Conf *mongo.Conf
CacheConf cache.CacheConf
DBOpts []mon.Option
CacheOpts []cache.Option
}
type PermissionRepository struct {
DB mongo.DocumentDBWithCacheUseCase
}
2025-10-22 13:40:31 +00:00
func NewPermissionRepository(param PermissionRepositoryParam) repository.PermissionRepository {
2025-10-07 09:29:47 +00:00
e := entity.Permission{}
documentDB, err := mongo.MustDocumentDBWithCache(
param.Conf,
e.CollectionName(),
param.CacheConf,
param.DBOpts,
param.CacheOpts,
)
if err != nil {
panic(err)
}
return &PermissionRepository{
DB: documentDB,
}
}
func (repo *PermissionRepository) FindOne(ctx context.Context, id string) (*entity.Permission, error) {
var data entity.Permission
rk := domain.GetPermissionIDRedisKey(id)
oid, err := bson.ObjectIDFromHex(id)
if err != nil {
return nil, ErrInvalidObjectID
}
err = repo.DB.FindOne(ctx, rk, &data, bson.M{"_id": oid})
switch {
case err == nil:
return &data, nil
case errors.Is(err, mon.ErrNotFound):
return nil, ErrNotFound
default:
return nil, err
}
}
2025-10-07 09:39:54 +00:00
func (repo *PermissionRepository) FindByName(ctx context.Context, name string) (*entity.Permission, error) {
var data entity.Permission
rk := domain.GetPermissionNameRedisKey(name)
err := repo.DB.FindOne(ctx, rk, &data, bson.M{"name": name})
switch {
case err == nil:
return &data, nil
case errors.Is(err, mon.ErrNotFound):
return nil, ErrNotFound
default:
return nil, err
}
2025-10-07 09:29:47 +00:00
}
func (repo *PermissionRepository) GetByNames(ctx context.Context, names []string) ([]*entity.Permission, error) {
2025-10-07 09:39:54 +00:00
var data []*entity.Permission
filter := bson.M{
"name": bson.M{"$in": names},
}
err := repo.DB.GetClient().Find(ctx, &data, filter)
if err != nil {
if errors.Is(err, mon.ErrNotFound) {
return nil, ErrNotFound
}
return nil, err
}
return data, nil
2025-10-07 09:29:47 +00:00
}
2025-10-07 09:39:54 +00:00
func (repo *PermissionRepository) FindByHTTP(ctx context.Context, path, method string) (*entity.Permission, error) {
var perm entity.Permission
filter := bson.M{
2025-10-10 15:25:36 +00:00
"http_path": path,
"http_method": strings.ToUpper(method), // 確保大小寫一致
2025-10-07 09:39:54 +00:00
}
err := repo.DB.GetClient().FindOne(ctx, &perm, filter)
switch {
case err == nil:
return &perm, nil
case errors.Is(err, mon.ErrNotFound):
return nil, ErrNotFound
default:
return nil, err
}
2025-10-07 09:29:47 +00:00
}
func (repo *PermissionRepository) List(ctx context.Context, filter repository.PermissionFilter) ([]*entity.Permission, error) {
2025-10-10 15:25:36 +00:00
var data []*entity.Permission
// 建立查詢條件
bsonFilter := bson.M{}
// 如果有指定類型
if filter.Type != nil {
bsonFilter["type"] = *filter.Type
}
// 如果有指定狀態
if filter.Status != nil {
bsonFilter["status"] = *filter.Status
}
// 如果有指定父 ID
if filter.ParentID != nil {
if *filter.ParentID == 0 {
// 查詢根權限 (沒有父 ID 或父 ID 為空)
bsonFilter["$or"] = []bson.M{
{"parent_id": bson.M{"$exists": false}},
{"parent_id": bson.ObjectID{}},
}
} else {
// 查詢特定父 ID 的子權限
bsonFilter["parent_id"] = *filter.ParentID
}
}
err := repo.DB.GetClient().Find(ctx, &data, bsonFilter)
if err != nil {
if errors.Is(err, mon.ErrNotFound) {
return []*entity.Permission{}, nil
}
return nil, err
}
return data, nil
2025-10-07 09:29:47 +00:00
}
func (repo *PermissionRepository) ListActive(ctx context.Context) ([]*entity.Permission, error) {
2025-10-10 15:25:36 +00:00
var data []*entity.Permission
// 使用快取查詢啟用的權限
bsonFilter := bson.M{
"status": domain.RecordActive,
}
err := repo.DB.GetClient().Find(ctx, &data, bsonFilter)
if err != nil {
if errors.Is(err, mon.ErrNotFound) {
return []*entity.Permission{}, nil
}
return nil, err
}
return data, nil
2025-10-07 09:29:47 +00:00
}
func (repo *PermissionRepository) GetChildren(ctx context.Context, parentID int64) ([]*entity.Permission, error) {
2025-10-10 15:25:36 +00:00
var data []*entity.Permission
// 查詢指定父 ID 的子權限
bsonFilter := bson.M{
"parent_id": parentID,
}
err := repo.DB.GetClient().Find(ctx, &data, bsonFilter)
if err != nil {
if errors.Is(err, mon.ErrNotFound) {
return []*entity.Permission{}, nil
}
return nil, err
}
return data, nil
}
// Index20251009001UP 建立 Permission 集合的索引
// 這個函數應該在應用啟動時或數據庫遷移時執行一次
func (repo *PermissionRepository) Index20251009001UP(ctx context.Context) (*mongodriver.Cursor, error) {
// 1. 唯一索引:權限名稱必須唯一
// 等價於 db.permission.createIndex({"name": 1}, {unique: true})
repo.DB.PopulateIndex(ctx, "name", 1, true)
// 2. 複合唯一稀疏索引HTTP 路徑 + 方法的組合必須唯一(用於 API 權限)
// 等價於 db.permission.createIndex({"http_path": 1, "http_method": 1}, {unique: true, sparse: true})
// 注意sparse: true 表示只對存在這些欄位的文檔建立索引,避免 null 值衝突
repo.DB.PopulateSparseMultiIndex(ctx, []string{"http_path", "http_method"}, []int32{1, 1}, true)
// 3. 查詢索引:按狀態查詢(例如 ListActive
// 等價於 db.permission.createIndex({"status": 1})
repo.DB.PopulateIndex(ctx, "status", 1, false)
// 4. 查詢索引:按父 ID 查詢(用於獲取子權限)
// 等價於 db.permission.createIndex({"parent_id": 1})
repo.DB.PopulateIndex(ctx, "parent_id", 1, false)
// 5. 複合索引:按類型和狀態查詢(常用組合)
// 等價於 db.permission.createIndex({"type": 1, "status": 1})
repo.DB.PopulateMultiIndex(ctx, []string{"type", "status"}, []int32{1, 1}, false)
// 6. 時間戳索引:用於排序和時間範圍查詢
// 等價於 db.permission.createIndex({"create_time": 1})
repo.DB.PopulateIndex(ctx, "create_time", 1, false)
// 返回所有索引列表
return repo.DB.GetClient().Indexes().List(ctx)
2025-10-07 09:29:47 +00:00
}