263 lines
7.5 KiB
Go
263 lines
7.5 KiB
Go
package repository
|
|
|
|
import (
|
|
"backend/pkg/library/mongo"
|
|
"backend/pkg/permission/domain"
|
|
"backend/pkg/permission/domain/entity"
|
|
"backend/pkg/permission/domain/repository"
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"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 RolePermissionRepositoryParam struct {
|
|
Conf *mongo.Conf
|
|
CacheConf cache.CacheConf
|
|
DBOpts []mon.Option
|
|
CacheOpts []cache.Option
|
|
}
|
|
|
|
type RolePermissionRepository struct {
|
|
DB mongo.DocumentDBWithCacheUseCase
|
|
}
|
|
|
|
func NewRolePermissionRepository(param RolePermissionRepositoryParam) repository.RolePermissionRepository {
|
|
e := entity.RolePermission{}
|
|
documentDB, err := mongo.MustDocumentDBWithCache(
|
|
param.Conf,
|
|
e.CollectionName(),
|
|
param.CacheConf,
|
|
param.DBOpts,
|
|
param.CacheOpts,
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return &RolePermissionRepository{
|
|
DB: documentDB,
|
|
}
|
|
}
|
|
|
|
// Create 建立角色權限關聯
|
|
func (repo *RolePermissionRepository) Create(ctx context.Context, roleID int64, permissionIDs []int64) error {
|
|
if len(permissionIDs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
now := time.Now().Unix()
|
|
|
|
// 將 int64 轉換為 ObjectID
|
|
roleOID := bson.NewObjectIDFromTimestamp(time.Unix(roleID, 0))
|
|
|
|
// 批量建立角色權限關聯
|
|
documents := make([]interface{}, 0, len(permissionIDs))
|
|
for _, permissionID := range permissionIDs {
|
|
permOID := bson.NewObjectIDFromTimestamp(time.Unix(permissionID, 0))
|
|
rp := &entity.RolePermission{
|
|
ID: bson.NewObjectID(),
|
|
RoleID: roleOID,
|
|
PermissionID: permOID,
|
|
}
|
|
rp.CreateTime = now
|
|
rp.UpdateTime = now
|
|
|
|
documents = append(documents, rp)
|
|
}
|
|
|
|
_, err := repo.DB.GetClient().InsertMany(ctx, documents)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 清除快取
|
|
repo.clearRolePermissionCache(ctx, roleID)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Update 更新角色權限關聯 (先刪除再建立)
|
|
func (repo *RolePermissionRepository) Update(ctx context.Context, roleID int64, permissionIDs []int64) error {
|
|
// 先刪除該角色的所有權限關聯
|
|
err := repo.Delete(ctx, roleID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 再建立新的關聯
|
|
return repo.Create(ctx, roleID, permissionIDs)
|
|
}
|
|
|
|
// Delete 刪除角色的所有權限
|
|
func (repo *RolePermissionRepository) Delete(ctx context.Context, roleID int64) error {
|
|
// 將 int64 轉換為 ObjectID
|
|
roleOID := bson.NewObjectIDFromTimestamp(time.Unix(roleID, 0))
|
|
|
|
filter := bson.M{"role_id": roleOID}
|
|
|
|
_, err := repo.DB.GetClient().DeleteMany(ctx, filter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 清除快取
|
|
repo.clearRolePermissionCache(ctx, roleID)
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetByRoleID 取得角色的所有權限關聯
|
|
func (repo *RolePermissionRepository) GetByRoleID(ctx context.Context, roleID int64) ([]*entity.RolePermission, error) {
|
|
var data []*entity.RolePermission
|
|
// 將 int64 轉換為 ObjectID
|
|
roleOID := bson.NewObjectIDFromTimestamp(time.Unix(roleID, 0))
|
|
|
|
filter := bson.M{"role_id": roleOID}
|
|
|
|
err := repo.DB.GetClient().Find(ctx, &data, filter)
|
|
if err != nil {
|
|
if errors.Is(err, mon.ErrNotFound) {
|
|
return []*entity.RolePermission{}, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// GetByRoleIDs 批量取得多個角色的權限關聯 (優化 N+1 查詢)
|
|
func (repo *RolePermissionRepository) GetByRoleIDs(ctx context.Context, roleIDs []int64) (map[int64][]*entity.RolePermission, error) {
|
|
if len(roleIDs) == 0 {
|
|
return make(map[int64][]*entity.RolePermission), nil
|
|
}
|
|
|
|
var data []*entity.RolePermission
|
|
|
|
// 將 int64 轉換為 ObjectID
|
|
roleOIDs := make([]bson.ObjectID, 0, len(roleIDs))
|
|
oidToInt64 := make(map[string]int64) // 用於反向映射
|
|
for _, roleID := range roleIDs {
|
|
roleOID := bson.NewObjectIDFromTimestamp(time.Unix(roleID, 0))
|
|
roleOIDs = append(roleOIDs, roleOID)
|
|
oidToInt64[roleOID.Hex()] = roleID
|
|
}
|
|
|
|
filter := bson.M{
|
|
"role_id": bson.M{"$in": roleOIDs},
|
|
}
|
|
|
|
err := repo.DB.GetClient().Find(ctx, &data, filter)
|
|
if err != nil {
|
|
if errors.Is(err, mon.ErrNotFound) {
|
|
return make(map[int64][]*entity.RolePermission), nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// 將結果按 roleID (int64) 分組
|
|
result := make(map[int64][]*entity.RolePermission)
|
|
for _, rp := range data {
|
|
// 將 ObjectID 轉回 int64
|
|
roleIDInt64 := oidToInt64[rp.RoleID.Hex()]
|
|
result[roleIDInt64] = append(result[roleIDInt64], rp)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// GetByPermissionIDs 根據權限 ID 取得所有角色關聯
|
|
func (repo *RolePermissionRepository) GetByPermissionIDs(ctx context.Context, permissionIDs []int64) ([]*entity.RolePermission, error) {
|
|
if len(permissionIDs) == 0 {
|
|
return []*entity.RolePermission{}, nil
|
|
}
|
|
|
|
var data []*entity.RolePermission
|
|
|
|
// 將 int64 轉換為 ObjectID
|
|
permOIDs := make([]bson.ObjectID, 0, len(permissionIDs))
|
|
for _, permID := range permissionIDs {
|
|
permOID := bson.NewObjectIDFromTimestamp(time.Unix(permID, 0))
|
|
permOIDs = append(permOIDs, permOID)
|
|
}
|
|
|
|
filter := bson.M{
|
|
"permission_id": bson.M{"$in": permOIDs},
|
|
}
|
|
|
|
err := repo.DB.GetClient().Find(ctx, &data, filter)
|
|
if err != nil {
|
|
if errors.Is(err, mon.ErrNotFound) {
|
|
return []*entity.RolePermission{}, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// GetRolesByPermission 根據權限 ID 取得所有角色 ID
|
|
func (repo *RolePermissionRepository) GetRolesByPermission(ctx context.Context, permissionID int64) ([]int64, error) {
|
|
var data []*entity.RolePermission
|
|
|
|
// 將 int64 轉換為 ObjectID
|
|
permOID := bson.NewObjectIDFromTimestamp(time.Unix(permissionID, 0))
|
|
|
|
filter := bson.M{"permission_id": permOID}
|
|
|
|
err := repo.DB.GetClient().Find(ctx, &data, filter)
|
|
if err != nil {
|
|
if errors.Is(err, mon.ErrNotFound) {
|
|
return []int64{}, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// 提取所有 roleID 並轉換回 int64
|
|
roleIDs := make([]int64, 0, len(data))
|
|
for _, rp := range data {
|
|
// 將 ObjectID 轉換回 int64 (取 timestamp)
|
|
roleIDInt64 := rp.RoleID.Timestamp().Unix()
|
|
roleIDs = append(roleIDs, roleIDInt64)
|
|
}
|
|
|
|
return roleIDs, nil
|
|
}
|
|
|
|
// clearRolePermissionCache 清除角色權限關聯快取
|
|
func (repo *RolePermissionRepository) clearRolePermissionCache(ctx context.Context, roleID int64) {
|
|
rk := domain.GetRolePermissionRedisKey(roleID)
|
|
_ = repo.DB.DelCache(ctx, rk)
|
|
}
|
|
|
|
// Index20251009003UP 建立 RolePermission 集合的索引
|
|
// 這個函數應該在應用啟動時或數據庫遷移時執行一次
|
|
func (repo *RolePermissionRepository) Index20251009003UP(ctx context.Context) (*mongodriver.Cursor, error) {
|
|
// 1. 複合唯一索引:角色 ID + 權限 ID 的組合必須唯一(避免重複關聯)
|
|
// 等價於 db.role_permission.createIndex({"role_id": 1, "permission_id": 1}, {unique: true})
|
|
repo.DB.PopulateMultiIndex(ctx, []string{"role_id", "permission_id"}, []int32{1, 1}, true)
|
|
|
|
// 2. 查詢索引:按角色 ID 查詢(用於獲取某角色的所有權限)
|
|
// 等價於 db.role_permission.createIndex({"role_id": 1})
|
|
repo.DB.PopulateIndex(ctx, "role_id", 1, false)
|
|
|
|
// 3. 查詢索引:按權限 ID 查詢(用於獲取擁有某權限的所有角色)
|
|
// 等價於 db.role_permission.createIndex({"permission_id": 1})
|
|
repo.DB.PopulateIndex(ctx, "permission_id", 1, false)
|
|
|
|
// 4. 複合索引:按權限 ID 和狀態查詢
|
|
// 等價於 db.role_permission.createIndex({"permission_id": 1, "status": 1})
|
|
repo.DB.PopulateMultiIndex(ctx, []string{"permission_id", "status"}, []int32{1, 1}, false)
|
|
|
|
// 5. 時間戳索引:用於排序和時間範圍查詢
|
|
// 等價於 db.role_permission.createIndex({"create_time": 1})
|
|
repo.DB.PopulateIndex(ctx, "create_time", 1, false)
|
|
|
|
// 返回所有索引列表
|
|
return repo.DB.GetClient().Indexes().List(ctx)
|
|
}
|