|
|
@@ -3,6 +3,7 @@ import {
|
|
|
ConflictException,
|
|
|
Injectable,
|
|
|
NotFoundException,
|
|
|
+ Logger,
|
|
|
} from '@nestjs/common';
|
|
|
import { isEmpty, pick } from 'lodash';
|
|
|
import { MongoPrismaService } from '@box/db/prisma/mongo-prisma.service';
|
|
|
@@ -39,6 +40,9 @@ const isGranted = (n: Can) => n === 1;
|
|
|
|
|
|
@Injectable()
|
|
|
export class RoleService {
|
|
|
+ private logger = new Logger(RoleService.name);
|
|
|
+ private isBackfilling = false;
|
|
|
+
|
|
|
constructor(private readonly mongoPrismaService: MongoPrismaService) {}
|
|
|
|
|
|
// async create(roleDto: RoleDto) {
|
|
|
@@ -54,6 +58,80 @@ export class RoleService {
|
|
|
// });
|
|
|
// }
|
|
|
|
|
|
+ // helper to generate next rid
|
|
|
+ private async generateNextRid(): Promise<number> {
|
|
|
+ const last = await this.mongoPrismaService.sysRole.findFirst({
|
|
|
+ where: { rid: { isSet: true } },
|
|
|
+ orderBy: { rid: 'desc' },
|
|
|
+ select: { rid: true },
|
|
|
+ });
|
|
|
+
|
|
|
+ return (last?.rid ?? 0) + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async backfillIds(): Promise<void> {
|
|
|
+ if (this.isBackfilling) {
|
|
|
+ this.logger.warn('backfill is already running, skipping.');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.isBackfilling = true;
|
|
|
+ this.logger.log('Starting backfill of id...');
|
|
|
+ try {
|
|
|
+ const withoutId = await this.mongoPrismaService.sysRole.findMany({
|
|
|
+ where: {
|
|
|
+ rid: null,
|
|
|
+ },
|
|
|
+ orderBy: { createTime: 'asc' },
|
|
|
+ select: { id: true },
|
|
|
+ });
|
|
|
+
|
|
|
+ if (withoutId.length === 0) {
|
|
|
+ this.logger.log('No sys roles need backfilling rid.');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.logger.log(
|
|
|
+ `Found ${withoutId.length} sys roles without rid. Starting backfill...`,
|
|
|
+ );
|
|
|
+
|
|
|
+ let nextRid = await this.generateNextRid();
|
|
|
+
|
|
|
+ for (const role of withoutId) {
|
|
|
+ let assigned = false;
|
|
|
+
|
|
|
+ while (!assigned) {
|
|
|
+ try {
|
|
|
+ await this.mongoPrismaService.sysRole.update({
|
|
|
+ where: { id: role.id },
|
|
|
+ data: { rid: nextRid },
|
|
|
+ });
|
|
|
+
|
|
|
+ this.logger.log(
|
|
|
+ `Backfilled rid ${nextRid} for sys role id ${role.id}`,
|
|
|
+ );
|
|
|
+
|
|
|
+ nextRid += 1;
|
|
|
+ assigned = true;
|
|
|
+ } catch (e: any) {
|
|
|
+ // Unique constraint violation → retry with a fresh number
|
|
|
+ if (e?.code === 'P2002') {
|
|
|
+ this.logger.warn(`Duplicate rid ${nextRid}, retrying...`);
|
|
|
+ nextRid = await this.generateNextRid();
|
|
|
+ } else {
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.logger.log(`Backfilled ${withoutId.length} rids successfully.`);
|
|
|
+ } finally {
|
|
|
+ this.isBackfilling = false;
|
|
|
+ this.logger.log('Finished backfilling process.');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
async create(dto: RoleDto) {
|
|
|
// (1) Unique name guard
|
|
|
const exists = await this.mongoPrismaService.sysRole.findUnique({
|
|
|
@@ -92,10 +170,12 @@ export class RoleService {
|
|
|
const menuIds = this.includeAncestors(selectedMenuIds, menuById);
|
|
|
|
|
|
// (5) Tx: create role and role_menu
|
|
|
+ const rId = await this.generateNextRid();
|
|
|
const created = await this.mongoPrismaService.$transaction(async (tx) => {
|
|
|
const role = await tx.sysRole.create({
|
|
|
data: {
|
|
|
name: dto.name,
|
|
|
+ rid: rId,
|
|
|
status: dto.status ?? true,
|
|
|
remark: dto.remark ?? null,
|
|
|
},
|
|
|
@@ -120,6 +200,7 @@ export class RoleService {
|
|
|
return {
|
|
|
data: {
|
|
|
id: created.id,
|
|
|
+ rid: created.rid,
|
|
|
name: created.name,
|
|
|
status: created.status,
|
|
|
remark: created.remark,
|
|
|
@@ -398,6 +479,9 @@ export class RoleService {
|
|
|
// }
|
|
|
|
|
|
async list(dto: RoleListDto): Promise<any> {
|
|
|
+ await this.backfillIds().catch((err) => {
|
|
|
+ this.logger.error('Error during backfillIds:', err);
|
|
|
+ });
|
|
|
const { page, size, name, roleIds } = dto;
|
|
|
const where: any = {};
|
|
|
if (name) {
|