重构 API 为 8 领域 + 应用层架构

将后端模块拆分为 domains、applications、shared 三层,结算计算器移入 domain 纯函数目录,API 路径与测试保持不变。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-02 14:48:41 +08:00
parent 14e49374ac
commit 4c92157299
47 changed files with 169 additions and 138 deletions

View File

@@ -0,0 +1,87 @@
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { IS_PUBLIC_KEY, PERMISSIONS_KEY } from '../../shared/common/decorators';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) return true;
return super.canActivate(context);
}
handleRequest<TUser = unknown>(err: Error | null, user: TUser): TUser {
if (err || !user) throw err || new UnauthorizedException();
return user;
}
}
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const required = this.reflector.getAllAndOverride<string[]>(PERMISSIONS_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!required?.length) return true;
const { user } = context.switchToHttp().getRequest();
const userPerms: string[] = user?.permissions ?? [];
if (user?.role === 'SUPER_ADMIN') return true;
const hasAll = required.every((p) => userPerms.includes(p));
if (!hasAll) throw new ForbiddenException('Insufficient permissions');
return true;
}
}
export function UserTypeGuard(...types: string[]) {
@Injectable()
class MixedUserTypeGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const { user } = context.switchToHttp().getRequest();
if (!types.includes(user?.userType)) {
throw new ForbiddenException('Access denied for this portal');
}
return true;
}
}
return MixedUserTypeGuard;
}
@Injectable()
export class PlayerGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const { user } = context.switchToHttp().getRequest();
if (user?.userType !== 'PLAYER') throw new ForbiddenException('Player access only');
return true;
}
}
@Injectable()
export class AdminGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const { user } = context.switchToHttp().getRequest();
if (user?.userType !== 'ADMIN') throw new ForbiddenException('Admin access only');
return true;
}
}
@Injectable()
export class AgentGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const { user } = context.switchToHttp().getRequest();
if (user?.userType !== 'AGENT') throw new ForbiddenException('Agent access only');
return true;
}
}