feat(M02-A): 建立多小程序租户配置模型

This commit is contained in:
Codex
2026-06-18 10:30:31 +08:00
parent 9d13f75dc0
commit 8b8fb28c36
15 changed files with 459 additions and 15 deletions
+55
View File
@@ -0,0 +1,55 @@
import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import {
AmbiguousAppTenantError,
type PlatformBootstrap
} from '../tenancy/platform-config-repository.js';
const headerSchema = z.object({
'x-wechat-appid': z.string().trim().min(6).max(64),
'tenant-id': z.string().regex(/^[1-9]\d{0,19}$/).optional()
});
export interface PlatformConfigResolver {
resolveBootstrap(appId: string, tenantId?: string): Promise<PlatformBootstrap | null>;
}
export async function registerPlatformBootstrapRoutes(
app: FastifyInstance,
repository: PlatformConfigResolver
): Promise<void> {
app.get('/app-api/bootstrap', async (request, reply) => {
const parsed = headerSchema.safeParse(request.headers);
if (!parsed.success) {
return reply.status(400).send({
code: 'INVALID_APP_CONTEXT',
message: 'x-wechat-appid is required and tenant-id must be a positive integer.',
traceId: request.traceId
});
}
try {
const bootstrap = await repository.resolveBootstrap(
parsed.data['x-wechat-appid'],
parsed.data['tenant-id']
);
if (!bootstrap) {
return reply.status(404).send({
code: 'APP_TENANT_NOT_FOUND',
message: 'The application and tenant binding is not active.',
traceId: request.traceId
});
}
return { code: 0, data: bootstrap, traceId: request.traceId };
} catch (error) {
if (error instanceof AmbiguousAppTenantError) {
return reply.status(409).send({
code: 'TENANT_SELECTION_REQUIRED',
message: error.message,
traceId: request.traceId
});
}
throw error;
}
});
}