Files
History_Api/internal/repositories/roleRepository.go
T
2026-06-05 14:18:55 +07:00

304 lines
8.1 KiB
Go

package repositories
import (
"context"
json "history-api/pkg/jsonx"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"history-api/internal/gen/sqlc"
"history-api/internal/models"
"history-api/pkg/cache"
"history-api/pkg/constants"
"history-api/pkg/convert"
)
type RoleRepository interface {
GetByID(ctx context.Context, id pgtype.UUID) (*models.RoleEntity, error)
GetByIDs(ctx context.Context, ids []string) ([]*models.RoleEntity, error)
GetByName(ctx context.Context, name string) (*models.RoleEntity, error)
All(ctx context.Context) ([]*models.RoleEntity, error)
Create(ctx context.Context, name string) (*models.RoleEntity, error)
Update(ctx context.Context, params sqlc.UpdateRoleParams) (*models.RoleEntity, error)
Delete(ctx context.Context, id pgtype.UUID) error
Restore(ctx context.Context, id pgtype.UUID) error
CreateUserRole(ctx context.Context, params sqlc.CreateUserRoleParams) error
DeleteUserRole(ctx context.Context, params sqlc.DeleteUserRoleParams) error
BulkDeleteRolesFromUser(ctx context.Context, userId pgtype.UUID) error
BulkDeleteUsersFromRole(ctx context.Context, roleId pgtype.UUID) error
WithTx(tx pgx.Tx) RoleRepository
}
type roleRepository struct {
q *sqlc.Queries
c cache.Cache
}
func NewRoleRepository(db sqlc.DBTX, c cache.Cache) RoleRepository {
return &roleRepository{
q: sqlc.New(db),
c: c,
}
}
func (r *roleRepository) WithTx(tx pgx.Tx) RoleRepository {
return &roleRepository{
q: r.q.WithTx(tx),
c: r.c,
}
}
func (r *roleRepository) generateQueryKey(prefix string, params any) string {
return cache.QueryKey(prefix, params)
}
func (r *roleRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.RoleEntity, error) {
if len(ids) == 0 {
return []*models.RoleEntity{}, nil
}
keys := make([]string, len(ids))
for i, id := range ids {
keys[i] = cache.Key("role:id", id)
}
raws := r.c.MGet(ctx, keys...)
roles := make([]*models.RoleEntity, 0, len(ids))
missingRolesToCache := make(map[string]any, len(ids))
missingPgIds := make([]pgtype.UUID, 0, len(ids))
for i, b := range raws {
if len(b) == 0 {
pgId := pgtype.UUID{}
err := pgId.Scan(ids[i])
if err == nil {
missingPgIds = append(missingPgIds, pgId)
}
}
}
dbMap := make(map[string]*models.RoleEntity, len(missingPgIds))
if len(missingPgIds) > 0 {
dbRows, err := r.q.GetRolesByIDs(ctx, missingPgIds)
if err == nil {
for _, row := range dbRows {
item := models.RoleEntity{
ID: convert.UUIDToString(row.ID),
Name: row.Name,
IsDeleted: row.IsDeleted,
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
}
dbMap[item.ID] = &item
}
}
}
for i, b := range raws {
if len(b) > 0 {
var u models.RoleEntity
if err := json.Unmarshal(b, &u); err == nil {
roles = append(roles, &u)
}
} else {
if item, ok := dbMap[ids[i]]; ok {
roles = append(roles, item)
missingRolesToCache[keys[i]] = item
}
}
}
if len(missingRolesToCache) > 0 {
_ = r.c.MSet(ctx, missingRolesToCache, constants.NormalCacheDuration)
}
return roles, nil
}
func (r *roleRepository) GetByIDs(ctx context.Context, ids []string) ([]*models.RoleEntity, error) {
return r.getByIDsWithFallback(ctx, ids)
}
func (r *roleRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.RoleEntity, error) {
cacheId := cache.Key("role:id", convert.UUIDToString(id))
var role models.RoleEntity
err := r.c.Get(ctx, cacheId, &role)
if err == nil {
_ = r.c.Set(ctx, cacheId, role, constants.NormalCacheDuration)
return &role, nil
}
row, err := r.q.GetRoleByID(ctx, id)
if err != nil {
return nil, err
}
role = models.RoleEntity{
ID: convert.UUIDToString(row.ID),
Name: row.Name,
IsDeleted: row.IsDeleted,
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
}
_ = r.c.Set(ctx, cacheId, role, constants.NormalCacheDuration)
return &role, nil
}
func (r *roleRepository) GetByName(ctx context.Context, name string) (*models.RoleEntity, error) {
cacheId := cache.Key("role:name", name)
var role models.RoleEntity
err := r.c.Get(ctx, cacheId, &role)
if err == nil {
_ = r.c.Set(ctx, cacheId, role, constants.NormalCacheDuration)
return &role, nil
}
row, err := r.q.GetRoleByName(ctx, name)
if err != nil {
return nil, err
}
role = models.RoleEntity{
ID: convert.UUIDToString(row.ID),
Name: row.Name,
IsDeleted: row.IsDeleted,
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
}
_ = r.c.Set(ctx, cacheId, role, constants.NormalCacheDuration)
return &role, nil
}
func (r *roleRepository) Create(ctx context.Context, name string) (*models.RoleEntity, error) {
row, err := r.q.CreateRole(ctx, name)
if err != nil {
return nil, err
}
go func() {
bgCtx := context.Background()
_ = r.c.DelByPattern(bgCtx, "role:all*")
}()
role := models.RoleEntity{
ID: convert.UUIDToString(row.ID),
Name: row.Name,
IsDeleted: row.IsDeleted,
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
}
return &role, nil
}
func (r *roleRepository) Update(ctx context.Context, params sqlc.UpdateRoleParams) (*models.RoleEntity, error) {
row, err := r.q.UpdateRole(ctx, params)
if err != nil {
return nil, err
}
role := models.RoleEntity{
ID: convert.UUIDToString(row.ID),
Name: row.Name,
IsDeleted: row.IsDeleted,
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
}
_ = r.c.Del(ctx, cache.Key("role:id", convert.UUIDToString(row.ID)), cache.Key("role:name", row.Name))
return &role, nil
}
func (r *roleRepository) All(ctx context.Context) ([]*models.RoleEntity, error) {
queryKey := "role:all"
var cachedIDs []string
err := r.c.Get(ctx, queryKey, &cachedIDs)
if err == nil {
if len(cachedIDs) == 0 {
return []*models.RoleEntity{}, nil
}
listItem, err := r.getByIDsWithFallback(ctx, cachedIDs)
if err != nil {
return nil, err
}
newCachedIDs := make([]string, len(listItem))
for i, media := range listItem {
newCachedIDs[i] = media.ID
}
_ = r.c.Set(ctx, queryKey, newCachedIDs, constants.ListCacheDuration)
return listItem, err
}
rows, err := r.q.GetRoles(ctx)
if err != nil {
return nil, err
}
roles := make([]*models.RoleEntity, 0, len(rows))
ids := make([]string, 0, len(rows))
roleToCache := make(map[string]any, len(rows))
for _, row := range rows {
role := &models.RoleEntity{
ID: convert.UUIDToString(row.ID),
Name: row.Name,
IsDeleted: row.IsDeleted,
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
}
ids = append(ids, role.ID)
roles = append(roles, role)
roleToCache[cache.Key("role:id", role.ID)] = role
}
if len(roleToCache) > 0 {
_ = r.c.MSet(ctx, roleToCache, constants.NormalCacheDuration)
}
_ = r.c.Set(ctx, queryKey, ids, constants.ListCacheDuration)
return roles, nil
}
func (r *roleRepository) Delete(ctx context.Context, id pgtype.UUID) error {
role, err := r.GetByID(ctx, id)
if err != nil {
return err
}
err = r.q.DeleteRole(ctx, id)
if err != nil {
return err
}
_ = r.c.Del(ctx, cache.Key("role:id", role.ID), cache.Key("role:name", role.Name))
return nil
}
func (r *roleRepository) Restore(ctx context.Context, id pgtype.UUID) error {
err := r.q.RestoreRole(ctx, id)
if err != nil {
return err
}
_ = r.c.Del(ctx, cache.Key("role:id", convert.UUIDToString(id)))
return nil
}
func (r *roleRepository) CreateUserRole(ctx context.Context, params sqlc.CreateUserRoleParams) error {
err := r.q.CreateUserRole(ctx, params)
return err
}
func (r *roleRepository) DeleteUserRole(ctx context.Context, params sqlc.DeleteUserRoleParams) error {
err := r.q.DeleteUserRole(ctx, params)
return err
}
func (r *roleRepository) BulkDeleteUsersFromRole(ctx context.Context, roleId pgtype.UUID) error {
err := r.q.BulkDeleteUsersFromRole(ctx, roleId)
return err
}
func (r *roleRepository) BulkDeleteRolesFromUser(ctx context.Context, roleId pgtype.UUID) error {
err := r.q.BulkDeleteRolesFromUser(ctx, roleId)
return err
}