后端修改权限和折叠功能
CI / verify (pull_request) Has been cancelled

This commit is contained in:
2026-07-23 14:38:52 +08:00
parent 6479365d6a
commit 75765bf9d4
11 changed files with 375 additions and 54 deletions
@@ -24,6 +24,7 @@ import { verifyPassword } from '../../common/crypto/password.util';
import { AnalyticsService } from '../analytics/analytics.service';
import { UserAddressService } from './user-address.service';
import { ResourceService } from '../common/resource.service';
import { HqPermissionsResolver } from '../../common/guards/hq-permission.guard';
import type { User } from '@prisma/client';
@@ -65,6 +66,7 @@ export class AuthService {
private readonly smsCodeStore: SmsCodeStore,
private readonly userAddressService: UserAddressService,
@Inject(forwardRef(() => ResourceService)) private readonly resourceService: ResourceService,
private readonly hqPermissions: HqPermissionsResolver,
) {}
private assertMobilePhone(phone: string) {
@@ -1128,7 +1130,9 @@ export class AuthService {
}
if (actorType === 'HQ') {
const account = await this.prisma.hqAccount.findUnique({ where: { id: actorId } });
return serializeBigInt(account);
if (!account) return null;
const permissionKeys = await this.hqPermissions.resolveEffectiveKeys(actorId);
return serializeBigInt({ ...account, permissionKeys });
}
return null;
}
@@ -24,6 +24,10 @@ import { PartnerPrimaryGuard } from '../../common/guards/partner-primary.guard';
import { PartnerPermissionGuard } from '../../common/guards/partner-permission.guard';
import { ShopStoreGuard } from '../../common/guards/shop-store.guard';
import { StoreMembershipService } from '../../common/guards/store-membership.service';
import {
HqPermissionGuard,
HqPermissionsResolver,
} from '../../common/guards/hq-permission.guard';
import { CommonModule } from '../common/common.module';
@Module({
@@ -59,6 +63,8 @@ import { CommonModule } from '../common/common.module';
PartnerPrimaryGuard,
PartnerPermissionGuard,
ShopStoreGuard,
HqPermissionsResolver,
HqPermissionGuard,
],
exports: [
AuthService,
@@ -74,6 +80,8 @@ import { CommonModule } from '../common/common.module';
PartnerPrimaryGuard,
PartnerPermissionGuard,
ShopStoreGuard,
HqPermissionsResolver,
HqPermissionGuard,
],
})
export class IamModule {}
@@ -2,12 +2,17 @@ import { BadRequestException, Injectable, NotFoundException } from '@nestjs/comm
import {
HQ_PERMISSION_CATALOG,
HQ_ROLE_DEFAULT_PERMISSIONS,
LEGACY_SYSTEM_SETTINGS_KEY,
expandHqPermissionKeys,
type HqPermissionKey,
} from '@dukang/shared-types';
import { PrismaService } from '../../common/prisma/prisma.module';
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
const VALID_PERMISSION_KEYS = new Set<string>(HQ_PERMISSION_CATALOG.map((p) => p.key));
const VALID_PERMISSION_KEYS = new Set<string>([
...HQ_PERMISSION_CATALOG.map((p) => p.key),
LEGACY_SYSTEM_SETTINGS_KEY,
]);
function assertPermissionKeys(keys: string[]) {
const invalid = keys.filter((key) => !VALID_PERMISSION_KEYS.has(key));
@@ -37,7 +42,7 @@ export class AdminHqPermissionsService {
});
const permissionKeys =
rows.length > 0
? rows.map((r) => r.permissionKey)
? expandHqPermissionKeys(rows.map((r) => r.permissionKey))
: [...(HQ_ROLE_DEFAULT_PERMISSIONS[role] ?? [])];
return { role, permissionKeys };
}
@@ -47,13 +52,14 @@ export class AdminHqPermissionsService {
throw new BadRequestException('超级管理员拥有全部权限,无需配置');
}
assertPermissionKeys(permissionKeys);
const normalized = expandHqPermissionKeys(permissionKeys);
const adminRole = role as 'OPS' | 'FINANCE' | 'CUSTOMER_SERVICE';
await this.prisma.$transaction([
this.prisma.hqRolePermission.deleteMany({ where: { adminRole } }),
...(permissionKeys.length
...(normalized.length
? [
this.prisma.hqRolePermission.createMany({
data: permissionKeys.map((permissionKey) => ({ adminRole, permissionKey })),
data: normalized.map((permissionKey) => ({ adminRole, permissionKey })),
}),
]
: []),
@@ -84,7 +90,7 @@ export class AdminHqPermissionsService {
select: { permissionKey: true },
}),
]);
const userPermissionKeys = userPerms.map((p) => p.permissionKey);
const userPermissionKeys = expandHqPermissionKeys(userPerms.map((p) => p.permissionKey));
const effectivePermissionKeys = [
...new Set([...rolePerms.permissionKeys, ...userPermissionKeys]),
] as HqPermissionKey[];
@@ -105,12 +111,13 @@ export class AdminHqPermissionsService {
throw new BadRequestException('超级管理员拥有全部权限,无需配置');
}
assertPermissionKeys(permissionKeys);
const normalized = expandHqPermissionKeys(permissionKeys);
await this.prisma.$transaction([
this.prisma.hqAccountPermission.deleteMany({ where: { hqAccountId: accountId } }),
...(permissionKeys.length
...(normalized.length
? [
this.prisma.hqAccountPermission.createMany({
data: permissionKeys.map((permissionKey) => ({ hqAccountId: accountId, permissionKey })),
data: normalized.map((permissionKey) => ({ hqAccountId: accountId, permissionKey })),
}),
]
: []),
@@ -1,22 +1,41 @@
import { Body, Controller, Get, Post, Put, UseGuards } from '@nestjs/common';
import type { SystemConfigUpdateRequest } from '@dukang/shared-types';
import {
SYSTEM_CONFIG_GROUP_PERMISSION,
SYSTEM_SETTINGS_PERMISSION_KEYS,
type HqPermissionKey,
} from '@dukang/shared-types';
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
import { SuperAdminGuard } from '../../common/guards/super-admin.guard';
import {
HqPermissionGuard,
HqPermissionsResolver,
RequireAnySystemSettings,
} from '../../common/guards/hq-permission.guard';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import type { AuthUser } from '../../common/guards/jwt-auth.guard';
import { HqOperation } from '../../common/hq-operation/hq-operation.decorator';
import { HqOperationAction } from '../../common/hq-operation/hq-operation.constants';
import { SystemConfigService } from '../../common/system-config/system-config.service';
@Controller('admin/system-config')
@UseGuards(HqAuthGuard, SuperAdminGuard)
@UseGuards(HqAuthGuard, HqPermissionGuard)
export class AdminSystemConfigController {
constructor(private readonly systemConfig: SystemConfigService) {}
constructor(
private readonly systemConfig: SystemConfigService,
private readonly permissions: HqPermissionsResolver,
) {}
@Get()
getForm() {
return this.systemConfig.getForm();
@RequireAnySystemSettings()
async getForm(@CurrentUser() user: AuthUser) {
const keys = await this.permissions.resolveEffectiveKeys(user.actorId);
const allowedGroups = allowedConfigGroups(keys);
return this.systemConfig.getForm(allowedGroups);
}
@Put()
@RequireAnySystemSettings()
@HqOperation({
action: HqOperationAction.SYSTEM_CONFIG_UPDATE,
refType: 'SYSTEM_CONFIG',
@@ -24,11 +43,14 @@ export class AdminSystemConfigController {
batch: true,
includeBody: true,
})
update(@Body() dto: SystemConfigUpdateRequest) {
return this.systemConfig.update(dto);
async update(@CurrentUser() user: AuthUser, @Body() dto: SystemConfigUpdateRequest) {
const keys = await this.permissions.resolveEffectiveKeys(user.actorId);
const allowedGroups = allowedConfigGroups(keys);
return this.systemConfig.update(dto, allowedGroups);
}
@Post('sync-env')
@UseGuards(SuperAdminGuard)
@HqOperation({
action: HqOperationAction.SYSTEM_CONFIG_SYNC_ENV,
refType: 'SYSTEM_CONFIG',
@@ -39,6 +61,7 @@ export class AdminSystemConfigController {
}
@Post('import-env')
@UseGuards(SuperAdminGuard)
@HqOperation({
action: HqOperationAction.SYSTEM_CONFIG_IMPORT_ENV,
refType: 'SYSTEM_CONFIG',
@@ -48,3 +71,12 @@ export class AdminSystemConfigController {
return this.systemConfig.importFromProcessEnv();
}
}
function allowedConfigGroups(permissionKeys: HqPermissionKey[]): string[] | null {
if (SYSTEM_SETTINGS_PERMISSION_KEYS.every((k) => permissionKeys.includes(k))) {
return null; // 全部
}
return Object.entries(SYSTEM_CONFIG_GROUP_PERMISSION)
.filter(([, perm]) => permissionKeys.includes(perm))
.map(([group]) => group);
}