Files
lingxi/prisma/seed.ts
T
Stev_Wang 04f49690f4
ci / 质量门禁 (push) Canceled after 0s
feat(a0): 完成工程骨架与契约基线
- 建立 pnpm workspace + Turborepo 单仓、ESLint 9/Prettier/commitlint/husky 门禁
- NestJS + Fastify API:统一错误体、trace_id、Zod 管道、/health 真实依赖探活、OpenAPI 3.1 契约
- Prisma 基线:outbox_events/idempotency_keys/system_configs 与首版迁移、幂等种子
- shared 契约包:错误码、权限点、ULID、游标分页、命名/时间工具
- ui 包与 Web 壳层:Semi + Remix 图标统一出口、Cloud Blue 视觉、登录/工作台/错误页
- worker 骨架、Playwright 冒烟、Gitea CI、OpenAPI 快照与 oasdiff 回退脚本
- 补充 A0 实施报告与本地开发说明
2026-11-02 17:01:50 +08:00

113 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 灵犀 · 种子数据(A0)
// 原则:
// 1) 只 seed 已建表的系统配置(system_configs),不提前创建 A1+ 业务表;
// 2) 幂等:按 config_key upsert,已存在的配置不覆盖(避免冲掉人工调整值);
// 3) 超管、角色、字典、电商行业包占位等种子依赖 A1/A3/A4/B0 的建表,见 prisma/SEED.md。
import { randomBytes } from 'node:crypto';
import { existsSync } from 'node:fs';
import { resolve } from 'node:path';
import { PrismaClient } from '@prisma/client';
import { config as loadDotenv } from 'dotenv';
for (const file of ['.env.local', '.env']) {
const path = resolve(process.cwd(), file);
if (existsSync(path)) {
loadDotenv({ path });
}
}
const prisma = new PrismaClient();
const ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
function ulid(): string {
let time = Date.now();
const timeChars = new Array<string>(10);
for (let i = 9; i >= 0; i -= 1) {
timeChars[i] = ENCODING[time % 32] ?? '0';
time = Math.floor(time / 32);
}
const random = randomBytes(16);
const randomChars = Array.from(random, (byte) => ENCODING[byte & 31] ?? '0');
return `${timeChars.join('')}${randomChars.join('')}`;
}
interface SeedConfig {
key: string;
value: string | number | boolean;
valueType: 'string' | 'number' | 'boolean';
description: string;
}
const systemConfigs: SeedConfig[] = [
{
key: 'platform.name',
value: '灵犀',
valueType: 'string',
description: '平台名称(展示用)',
},
{
key: 'platform.export.signed_url_ttl_seconds',
value: 900,
valueType: 'number',
description: '导出产物签名 URL 有效期(秒),短时效',
},
{
key: 'platform.export.archive_retention_days',
value: 3,
valueType: 'number',
description: '导出打包产物默认保留天数',
},
{
key: 'platform.upload.max_upload_mb',
value: 500,
valueType: 'number',
description: '单文件上传上限(MB)',
},
{
key: 'platform.page.default_limit',
value: 20,
valueType: 'number',
description: '列表默认每页数量',
},
{
key: 'platform.ai.cost_soft_limit_ratio',
value: 0.8,
valueType: 'number',
description: 'AI 成本软上限触发比例',
},
{
key: 'platform.ai.cost_hard_limit_ratio',
value: 1,
valueType: 'number',
description: 'AI 成本硬上限触发比例(达到即拒绝新任务)',
},
];
async function main(): Promise<void> {
for (const item of systemConfigs) {
await prisma.systemConfig.upsert({
where: { configKey: item.key },
update: {},
create: {
id: ulid(),
configKey: item.key,
configValue: item.value,
valueType: item.valueType,
description: item.description,
},
});
}
console.log(`[seed] system_configs 就绪:${String(systemConfigs.length)} 项(幂等)`);
}
main()
.catch((error: unknown) => {
console.error('[seed] 失败:', error);
process.exitCode = 1;
})
.finally(async () => {
await prisma.$disconnect();
});