feat(api): identity and audit schema with mysql semantics tests

packages/db-cn:@photo/db-cn(prisma 5.22,api/worker 共享 client)+
schema(users/sessions/audit_logs,DateTime(6)、cuid、账号唯一)。
迁移 SQL 手工改写表级 collation 为 utf8mb4_0900_bin——Prisma 生成的 MySQL
DDL 固定 utf8mb4_unicode_ci 会覆盖库级 bin collation,账号/token 大小写
敏感语义依赖它(后续迁移需同样复核)。services/api:@Global PrismaModule
(PrismaService 封装 $connect/$disconnect,池上限仍由
DATABASE_URL_CN ?connection_limit=10 承担);reset.ts 落地 FK 包裹式
TRUNCATE;e2e 语义复核 4 例(UTC 往返/大小写敏感/audit Json+actor/
session FK Restrict P2003)。pnpm 10 未拦截 prisma engines(平台包内置),
无需 onlyBuiltDependencies。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-17 19:51:56 +08:00
parent 7d250ec342
commit 2801e4f158
14 changed files with 417 additions and 14 deletions
+6 -2
View File
@@ -1,6 +1,7 @@
// services/api/src/app-cn.module.ts
// 根模块:日志(nestjs-pino / pino-http)、request-id 中间件、全局异常过滤器、
// /v1/health。功能模块(auth/albums/uploads/photos/public)自 Task 12+ 起逐个加入。
// 根模块:Prisma(@Global,封装 @photo/db-cn)、日志(nestjs-pino / pino-http)、
// request-id 中间件、全局异常过滤器、/v1/health。
// 功能模块(auth/albums/uploads/photos/public)自 Task 12+ 起逐个加入。
// 过滤器经 APP_FILTER、中间件经 NestModule 在模块层注册——e2e 直接 import 本模块
// 即可获得与生产一致的管线(全局前缀除外,由入口设置)。
import { type MiddlewareConsumer, Module, type NestModule } from '@nestjs/common';
@@ -9,10 +10,13 @@ import { LoggerModule } from 'nestjs-pino';
import type { IncomingMessage, ServerResponse } from 'node:http';
import { AllExceptionsFilter } from './all-exceptions.filter';
import { HealthController } from './health.controller';
import { PrismaModule } from './prisma/prisma.module';
import { RequestIdMiddleware, resolveRequestId } from './request-id.middleware';
@Module({
imports: [
// @Global:功能模块(Task 12+)直接注入 PrismaService,无需 re-import。
PrismaModule,
LoggerModule.forRoot({
pinoHttp: {
// 与 RequestIdMiddleware 共用同一解析函数:优先透传 x-request-id,
+12
View File
@@ -0,0 +1,12 @@
// services/api/src/prisma/prisma.module.ts —— 封装 @photo/db-cn 供注入。
// @Global:Task 12+ 的功能模块(auth/albums/uploads/photos/public)直接注入
// PrismaService,无需逐模块 re-import;本模块只在 AppCnModule 挂载一次。
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
+23
View File
@@ -0,0 +1,23 @@
// services/api/src/prisma/prisma.service.ts —— @photo/db-cn 的可注入封装。
// 连接池上限由 DATABASE_URL_CN 的 ?connection_limit=10 承担,这里不另行放大;
// 不使用 enableShutdownHooks(Nest 10 经 onModuleDestroy 生命周期即可优雅断开)。
import {
Injectable,
type OnModuleDestroy,
type OnModuleInit,
} from '@nestjs/common';
import { PrismaClient } from '@photo/db-cn';
@Injectable()
export class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
async onModuleInit(): Promise<void> {
await this.$connect();
}
async onModuleDestroy(): Promise<void> {
await this.$disconnect();
}
}