M0: 工作区骨架 + 五道门禁 + 环境自举
- pnpm workspaces / tsconfig 双工程(node · web)/ 路径别名(§11.1) - eslint flat(§11.3 + §12.5 可机检项)/ dependency-cruiser 基线八条 + JCAI 增补(§11.4) - tools/module-tiers.json + gen-depcruise-tiers.ts 生成 tier-order 逐层规则 - tools/api-report.mjs 契约快照只留契约面(ADR-005) - docker-compose(PG16)+ db/init 四 schema;种子数据五岗位五员工冻结为常量 - docs/debt.md · docs/adr/ADR-005 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@app/api",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": { ".": "./src/app/main.ts" },
|
||||
"scripts": {
|
||||
"dev": "tsup --watch --onSuccess \"node dist/main.js\"",
|
||||
"build": "tsup",
|
||||
"seed": "node dist/seed.js"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 环境配置 —— 只在 app/ 层读环境变量;模块内一律经 port 注入(§5.3)。
|
||||
*/
|
||||
export type AppConfig = {
|
||||
readonly nodeEnv: 'development' | 'test' | 'production';
|
||||
readonly port: number;
|
||||
readonly databaseUrl: string;
|
||||
};
|
||||
|
||||
const DEFAULT_DATABASE_URL = 'postgres://jcai:jcai@localhost:54329/jcai';
|
||||
|
||||
export function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig {
|
||||
const nodeEnv = env['NODE_ENV'];
|
||||
return {
|
||||
nodeEnv: nodeEnv === 'production' || nodeEnv === 'test' ? nodeEnv : 'development',
|
||||
port: Number(env['PORT'] ?? 8787),
|
||||
databaseUrl: env['DATABASE_URL'] ?? DEFAULT_DATABASE_URL,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* apps/api 唯一组装层入口(编码规范 §5.2)。
|
||||
* 只有 app/ 能同时认识多个模块:new adapter、注入 port、注册路由 / worker / Mastra 实例。
|
||||
*
|
||||
* M0:骨架 —— 门禁跑通即可,业务装配自 M1 起逐层填入。
|
||||
*/
|
||||
import { loadConfig } from './config.ts';
|
||||
|
||||
export async function boot(): Promise<void> {
|
||||
const config = loadConfig();
|
||||
process.stdout.write(
|
||||
`JCAI api · M0 骨架 · env=${config.nodeEnv} · port=${config.port} · db=${config.databaseUrl.replace(/:[^:@]*@/, ':***@')}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
await boot();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 种子数据与播种入口(编码规范 §15 · M0)。
|
||||
*
|
||||
* 数据是常量、写入是端口 —— M0 立骨架:数据在此冻结,写入实现随 M1 的 `org` 模块落地。
|
||||
* 部署边界:单次部署 ≤50 人、单组织、不做多租户(OS 0.3)。
|
||||
*/
|
||||
|
||||
/** 岗位:编排层声明岗位,起单时按四策略解析出人(OS 03) */
|
||||
export const POSITION_SEED = [
|
||||
{ code: 'PM', name: '项目经理', note: '定价审批、策略拍板' },
|
||||
{ code: 'BD', name: '商务', note: 'RFQ 受理、成本定价、报价议价、情报归档' },
|
||||
{ code: 'FAE', name: '现场应用', note: '替代选型' },
|
||||
{ code: 'PRM', name: '采购/寻源', note: '渠道寻源' },
|
||||
{ code: 'OP', name: '运营', note: '运营支持' },
|
||||
] as const;
|
||||
|
||||
/** 员工五人;登录者 = 冯兵(BD) */
|
||||
export const STAFF_SEED = [
|
||||
{ account: 'dingziqiao', name: '丁子乔', position: 'PM' },
|
||||
{ account: 'fengbing', name: '冯兵', position: 'BD' },
|
||||
{ account: 'wangziming', name: '王梓名', position: 'FAE' },
|
||||
{ account: 'linpeixuan', name: '林佩萱', position: 'PRM' },
|
||||
{ account: 'liyuanchu', name: '李元初', position: 'OP' },
|
||||
] as const;
|
||||
|
||||
/** 默认登录者(MVP 单组织 cookie session,§4 鉴权定调) */
|
||||
export const DEFAULT_LOGIN_ACCOUNT = 'fengbing';
|
||||
|
||||
export type PositionSeed = (typeof POSITION_SEED)[number];
|
||||
export type StaffSeed = (typeof STAFF_SEED)[number];
|
||||
|
||||
/** 播种端口 —— 实现由 app/ 在 M1 注入(org 模块出口) */
|
||||
export type OrgSeeder = {
|
||||
upsertPosition(position: PositionSeed): Promise<void>;
|
||||
upsertStaff(staff: StaffSeed): Promise<void>;
|
||||
};
|
||||
|
||||
export async function runSeed(seeder: OrgSeeder): Promise<void> {
|
||||
for (const position of POSITION_SEED) await seeder.upsertPosition(position);
|
||||
for (const staff of STAFF_SEED) await seeder.upsertStaff(staff);
|
||||
}
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
process.stdout.write(
|
||||
`种子数据:${POSITION_SEED.length} 个岗位 / ${STAFF_SEED.length} 名员工;` +
|
||||
`登录者=${DEFAULT_LOGIN_ACCOUNT}\n` +
|
||||
'写入实现随 M1 `org` 模块落地(docs/debt.md · M0-1)。\n',
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: { main: 'src/app/main.ts', seed: 'src/app/seed.ts' },
|
||||
format: ['esm'],
|
||||
target: 'node22',
|
||||
platform: 'node',
|
||||
outDir: 'dist',
|
||||
clean: true,
|
||||
sourcemap: true,
|
||||
skipNodeModulesBundle: true,
|
||||
tsconfig: '../../tsconfig.node.json',
|
||||
});
|
||||
Reference in New Issue
Block a user