mirror of
https://github.com/labring/FastGPT.git
synced 2026-05-08 01:08:43 +08:00
5ccdcc1cd4
* chore: bump pro submodule for hydration stability (#6808) * sandbox-sync-agent * refactor: host pro as submodule * chore: checkpoint host pro restructure * refactor workspace test layout and startup init * chore: update next turbopack setup * chore: snapshot current work before actions fix * chore: update pro submodule * chore: point pro submodule url to upstream https * fix: Dockerfile * chore: update pro submodule * ci: support private pro submodule token and skip fork jobs * fix(ci): build sdk workspace deps before code-sandbox bundle * fix(app): exclude vitest configs from production typecheck * fix(app-image): build sdk packages before next build * fix(ci): align dockerfiles with workspace sdk build flow * chore(docker): upgrade node20 docker images to node24 * fix(ci): read admin coverage output path in pro test workflow * fix(app-image): include next-i18next config and locale assets * chore: update pro submodule * chore: do not specify branch for submodule * chore: remove most ts-nocheck sign * chore: update pro submodule * chore: remove sandbox-agent-sync package * chore: do not modify "pushData" file logic * fix: health check * chore: restore dev axios proxy state * fix: test-fastgpt report workflow * fix: use valid vitest coverage action inputs * update shell (#6830) * .codex (#6832) * fix: home chat file uploads (#6838) * chore: update actions workflow yamls * chore: update turbo.json * fix: split admin preview image workflows * fix: allow home chat file uploads * chore: add skip file type check env (#6839) * chore: update actions workflow yamls (#6835) * chore: update actions workflow yamls * fix: allow pro workflows on fork pull requests * chore: update turbo.json * fix: split admin preview image workflows * chore: bump pro submodule for admin typecheck * chore: update pro submodule * chore: bump pro submodule for turbo ignore * chore: update pro submodule for file download api --------- Co-authored-by: Archer <545436317@qq.com>
7.8 KiB
7.8 KiB
代码规范
基础代码组织模式
采用 DDD 架构,按业务域 → 子功能 → 固定文件三层划分。
目录结构
packages/
├── global/core/ # 类型、常量(前后端共享)
│ ├── app/
│ │ ├── type.ts # 顶层聚合类型
│ │ ├── constants.ts
│ │ ├── workflow/
│ │ │ ├── type.ts
│ │ │ └── constants.ts
│ │ ├── version/
│ │ │ └── type.ts
│ │ └── evaluation/
│ │ └── type.ts
│ ├── chat/
│ ├── dataset/
│ └── plugin/
│
└── service/core/ # 后端业务逻辑(不可在前端引用)
├── app/
│ ├── schema.ts # App 主表 Mongoose Schema
│ ├── entity.ts # findById / create / updateById 等基础操作封装
│ ├── service.ts # 聚合业务逻辑(跨子功能协调),不允许互相引用,只允许单向依赖,跨 service 的协调需由上层通过 props 传入另一个 service 或者衍生方法
│ ├── auth.ts # 鉴权相关(如有)
│ ├── utils.ts # 纯函数工具,无副作用,可独立单测
│ ├── version/
│ │ ├── schema.ts
│ │ ├── entity.ts
│ │ ├── service.ts
│ │ └── utils.ts
│ ├── evaluation/
│ │ ├── schema.ts # 合并多个 schema 到单文件
│ │ ├── entity.ts
│ │ ├── service.ts
│ │ └── utils.ts
│ ├── logs/
│ └── tool/
│ ├── service.ts
│ └── utils.ts
├── chat/
├── dataset/
└── plugin/
叶子目录固定文件说明
| 文件 | 职责 |
|---|---|
schema.ts |
Mongoose Schema 定义,导出 Model 和 SchemaType |
entity.ts |
数据访问封装:findById、create、updateById 等基础操作 |
service.ts |
业务逻辑:调用 entity,跨模块协调,处理业务规则 |
utils.ts |
纯函数工具,无副作用,可独立单测 |
// entity.ts 示例 —— 只做数据访问,不含业务判断
export const findAppById = (id: string) =>
MongoApp.findById(id).lean();
export const createApp = (data: AppCreateParams, session?: ClientSession) =>
MongoApp.create([data], { session });
// service.ts 示例 —— 调用 entity,处理业务规则
export const createAppAndInitVersion = async (data: AppCreateParams, session?: ClientSession) => {
const app = await createApp(data, session);
await createVersion({ appId: app._id, ... }, session);
return app;
};
// service 需协同,通过 props 传入另一个 service 或者衍生方法。
const service1 = xxxx
const service2 = (props: {id:string; service1: typeof service1 }) => {
const data = findAppById(id)
return props.service1(data);
};
层级约束
global/core/只放类型和常量,禁止引入 mongoose、服务端 SDKservice/core/只在服务端使用,禁止被packages/web/或前端页面直接引用- 子功能目录不超过 3 层嵌套
- 一个目录内无需拆子功能时,直接放
schema.ts+entity.ts+service.ts+utils.ts - 多个 schema 文件(如
evalSchema.ts+evalItemSchema.ts)合并到单个schema.ts
代码风格
使用 type 进行类型声明,不使用 interface
// ❌ 不好的实践
interface User {
id: string;
name: string;
}
// ✅ 好的实践
type User = {
id: string;
name: string;
}
使用 IIFE 写法来取代 if/else 进行变量条件赋值。
// ❌ 不好的实践
if (condition) {
value = true;
} else {
value = false;
}
// ✅ 好的实践
const value = (() => {
if (condition) {
return true;
}
return false;
})();
类型推导:Zod schema 同时承担校验和类型
用 z.infer 从 schema 推导类型,不重复手写相同结构的 type。
// ❌ 不好的实践
type MessageParam = { role: 'user' | 'assistant'; content: string };
const MessageParamSchema = z.object({ role: z.enum(['user', 'assistant']), content: z.string() });
// ✅ 好的实践
export const MessageParamSchema = z.discriminatedUnion('role', [...]);
export type MessageParam = z.infer<typeof MessageParamSchema>;
可选链调用回调
用 ?.() 调用可选回调,取代 if (fn) fn() 的冗余写法。
// ❌ 不好的实践
if (onProgress) {
onProgress({ phase: 'creatingContainer' });
}
// ✅ 好的实践
onProgress?.({ phase: 'creatingContainer' });
空值合并取默认值
用 ?? 取代 || 处理默认值,避免 0、false、'' 被错误覆盖。
// ❌ 不好的实践
const version = lastVersion?.version || 0; // version 为 0 时被误覆盖
const text = item?.value || '';
// ✅ 好的实践
const version = (lastVersion?.version ?? -1) + 1;
const text = item?.value ?? '';
解构重命名
同名变量来自多个来源时,解构时重命名,避免命名冲突。
// ❌ 不好的实践
const r1 = await getSkillGuidance(...);
const r2 = await createLLMResponse(...);
const inputTokens = r1.usage.inputTokens + r2.usage.inputTokens;
// ✅ 好的实践
const { usage: guidanceUsage } = await getSkillGuidance(...);
const { usage: generateUsage } = await createLLMResponse(...);
const inputTokens = guidanceUsage.inputTokens + generateUsage.inputTokens;
类型守卫
用 is 关键字收窄 unknown / any 类型,替代强制断言。
// ❌ 不好的实践
function process(value: unknown) {
const n = value as number; // 不安全
}
// ✅ 好的实践
const isValidNumber = (value: unknown): value is number =>
typeof value === 'number' && Number.isFinite(value);
if (isValidNumber(value)) {
// 此处 value 安全收窄为 number
}
非关键清理用 .catch() 链
次要的清理操作(不影响主流程)用 .catch() 吞掉错误,不污染主 try/catch。
// ❌ 不好的实践
try {
await client.delete();
} catch {
// 清理失败,主流程中断
}
// ✅ 好的实践
await client.delete().catch(() => {});
函数参数不超过 2 个,多参数用对象传递
独立参数不超过 2 个,超过时改为对象参数,便于扩展且无需关心顺序。
// ❌ 不好的实践
function createVersion(skillId: string, teamId: string, tmbId: string, version: number) {}
// ✅ 好的实践
function createVersion(data: { skillId: string; teamId: string; tmbId: string; version: number }) {}
数据写操作函数支持可选 session 参数
涉及数据库写操作的函数统一支持可选的 session 参数,便于上层组合事务。事务统一通过 mongoSessionRun 发起,内部自动处理 startTransaction / commit / abort / retry。
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { type ClientSession } from '@fastgpt/service/common/mongo';
// entity.ts —— 基础操作透传 session
export const createVersion = (data: CreateVersionData, session?: ClientSession) =>
MongoAppVersion.create([data], { session });
// service.ts —— 需要事务时用 mongoSessionRun 包裹,外部已有 session 时直接传入
export const createAppAndInitVersion = async (
data: AppCreateParams,
session?: ClientSession
) => {
const create = async (session: ClientSession) => {
const app = await createApp(data, session);
await createVersion({ appId: app._id, version: 0 }, session);
return app;
};
if (session) {
return create(session);
} else {
return mongoSessionRun(create);
}
};