backend/pkg/permission/repository/permission.go

228 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"backend/pkg/library/mongo"
"backend/pkg/permission/domain"
"backend/pkg/permission/domain/entity"
"backend/pkg/permission/domain/repository"
"context"
"errors"
"strings"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/v2/bson"
mongodriver "go.mongodb.org/mongo-driver/v2/mongo"
)
type PermissionRepositoryParam struct {
Conf *mongo.Conf
CacheConf cache.CacheConf
DBOpts []mon.Option
CacheOpts []cache.Option
}
type PermissionRepository struct {
DB mongo.DocumentDBWithCacheUseCase
}
func NewAccountRepository(param PermissionRepositoryParam) repository.PermissionRepository {
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
}
}
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
}
}
func (repo *PermissionRepository) GetByNames(ctx context.Context, names []string) ([]*entity.Permission, error) {
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
}
func (repo *PermissionRepository) FindByHTTP(ctx context.Context, path, method string) (*entity.Permission, error) {
var perm entity.Permission
filter := bson.M{
"http_path": path,
"http_method": strings.ToUpper(method), // 確保大小寫一致
}
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
}
}
func (repo *PermissionRepository) List(ctx context.Context, filter repository.PermissionFilter) ([]*entity.Permission, error) {
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
}
func (repo *PermissionRepository) ListActive(ctx context.Context) ([]*entity.Permission, error) {
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
}
func (repo *PermissionRepository) GetChildren(ctx context.Context, parentID int64) ([]*entity.Permission, error) {
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)
}