feat(p1): complete P1 foundation stage
Some checks failed
CI Go / test (push) Has been cancelled
CI Proto / lint (push) Has been cancelled
CI Python / test (push) Has been cancelled
CI TypeScript / test (push) Has been cancelled

- monorepo: pnpm workspace + go.work + pyproject.toml + commitlint/husky
- infra: docker-compose (minimal + full profiles) + init-sql + prometheus
- arch-scan: multi-language scanner skeleton (TS/Go/Python/Proto)
- shared-proto: buf v2 + classes.proto (ClassService CRUD contract)
- api-gateway: Go/Gin + JWT HS256 auth + reverse proxy + request ID
- classes: NestJS golden template (error system + observability + middleware + CRUD + tests)
- teacher-portal: Next.js + paper-feel UI design system
- CI/CD: 4 workflows (go/ts/py/proto)
- docs: migration guide + project_rules + coding-standards + git-workflow + ui-design-system + 004 + 9 module READMEs + known-issues + spec/plan migration + roadmap
This commit is contained in:
SpecialX
2026-07-07 23:39:37 +08:00
commit 2ba4250165
100 changed files with 15242 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
export type ErrorType =
| 'validation'
| 'not_found'
| 'permission_denied'
| 'conflict'
| 'business'
| 'database'
| 'internal';
export interface ErrorDetails {
[key: string]: unknown;
}
export abstract class ApplicationError extends Error {
abstract readonly type: ErrorType;
abstract readonly statusCode: number;
readonly code: string;
readonly details?: ErrorDetails;
// FIX #1: traceId 改为可写,以便 GlobalErrorFilter 注入请求级 traceId
traceId?: string;
constructor(message: string, code: string, details?: ErrorDetails) {
super(message);
this.name = this.constructor.name;
this.code = code;
this.details = details;
}
toJSON(): Record<string, unknown> {
return {
success: false,
error: {
code: this.code,
message: this.message,
details: this.details,
traceId: this.traceId,
},
};
}
}
export class ValidationError extends ApplicationError {
readonly type = 'validation' as const;
readonly statusCode = 400;
constructor(message: string, details?: ErrorDetails) {
super(message, 'CLASSES_VALIDATION_ERROR', details);
}
}
export class NotFoundError extends ApplicationError {
readonly type = 'not_found' as const;
readonly statusCode = 404;
constructor(resource: string, id: string) {
super(`${resource} not found: ${id}`, 'CLASSES_NOT_FOUND', { resource, id });
}
}
export class PermissionDeniedError extends ApplicationError {
readonly type = 'permission_denied' as const;
readonly statusCode = 403;
constructor(permission: string) {
super(`Permission denied: ${permission}`, 'CLASSES_PERMISSION_DENIED', { permission });
}
}
export class ConflictError extends ApplicationError {
readonly type = 'conflict' as const;
readonly statusCode = 409;
constructor(message: string, details?: ErrorDetails) {
super(message, 'CLASSES_CONFLICT', details);
}
}
export class BusinessError extends ApplicationError {
readonly type = 'business' as const;
readonly statusCode = 422;
constructor(message: string, details?: ErrorDetails) {
super(message, 'CLASSES_BUSINESS_ERROR', details);
}
}
export class DatabaseError extends ApplicationError {
readonly type = 'database' as const;
readonly statusCode = 500;
constructor(message: string, details?: ErrorDetails) {
super(message, 'CLASSES_DATABASE_ERROR', details);
}
}
export class InternalError extends ApplicationError {
readonly type = 'internal' as const;
readonly statusCode = 500;
constructor(message: string, details?: ErrorDetails) {
super(message, 'CLASSES_INTERNAL_ERROR', details);
}
}