重构 API 为 8 领域 + 应用层架构
将后端模块拆分为 domains、applications、shared 三层,结算计算器移入 domain 纯函数目录,API 路径与测试保持不变。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
48
apps/api/src/shared/common/decorators.ts
Normal file
48
apps/api/src/shared/common/decorators.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { createParamDecorator, ExecutionContext, SetMetadata } from '@nestjs/common';
|
||||
|
||||
export const IS_PUBLIC_KEY = 'isPublic';
|
||||
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
|
||||
|
||||
export const PERMISSIONS_KEY = 'permissions';
|
||||
export const RequirePermissions = (...permissions: string[]) =>
|
||||
SetMetadata(PERMISSIONS_KEY, permissions);
|
||||
|
||||
export const CurrentUser = createParamDecorator(
|
||||
(data: string | undefined, ctx: ExecutionContext) => {
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
const user = request.user;
|
||||
return data ? user?.[data] : user;
|
||||
},
|
||||
);
|
||||
|
||||
export function generateBetNo(): string {
|
||||
const ts = Date.now().toString(36).toUpperCase();
|
||||
const rand = Math.random().toString(36).substring(2, 8).toUpperCase();
|
||||
return `BET${ts}${rand}`;
|
||||
}
|
||||
|
||||
export function generateTransactionId(): string {
|
||||
const ts = Date.now().toString(36).toUpperCase();
|
||||
const rand = Math.random().toString(36).substring(2, 8).toUpperCase();
|
||||
return `TXN${ts}${rand}`;
|
||||
}
|
||||
|
||||
export function generateBatchNo(prefix: string): string {
|
||||
const ts = Date.now().toString(36).toUpperCase();
|
||||
return `${prefix}${ts}`;
|
||||
}
|
||||
|
||||
export function serializeBigInt(obj: unknown): unknown {
|
||||
if (obj === null || obj === undefined) return obj;
|
||||
if (typeof obj === 'bigint') return obj.toString();
|
||||
if (obj instanceof Date) return obj.toISOString();
|
||||
if (Array.isArray(obj)) return obj.map(serializeBigInt);
|
||||
if (typeof obj === 'object') {
|
||||
const result: Record<string, unknown> = {};
|
||||
for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
|
||||
result[key] = serializeBigInt(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
42
apps/api/src/shared/common/filters.ts
Normal file
42
apps/api/src/shared/common/filters.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
ExceptionFilter,
|
||||
Catch,
|
||||
ArgumentsHost,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
import { serializeBigInt } from './decorators';
|
||||
|
||||
@Catch()
|
||||
export class GlobalExceptionFilter implements ExceptionFilter {
|
||||
catch(exception: unknown, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
|
||||
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
let message = 'Internal server error';
|
||||
|
||||
if (exception instanceof HttpException) {
|
||||
status = exception.getStatus();
|
||||
const res = exception.getResponse();
|
||||
message = typeof res === 'string' ? res : (res as { message?: string }).message || message;
|
||||
} else if (exception instanceof Error) {
|
||||
message = exception.message;
|
||||
}
|
||||
|
||||
response.status(status).json({
|
||||
success: false,
|
||||
error: message,
|
||||
data: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function jsonResponse<T>(data: T, message?: string) {
|
||||
return {
|
||||
success: true,
|
||||
data: serializeBigInt(data),
|
||||
message,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user