阅读时间 8 分钟最后更新: 2025-12-31 12:12
工程化编码规范与协作指南
本文档旨在统一项目的代码风格、架构设计与协作流程,确保代码库的可维护性、扩展性与一致性。所有贡献者在提交代码前,请务必仔细阅读并遵守本指南。
核心架构设计原则
分层架构
本项目采用清晰的分层架构,严格分离关注点:
- API Layer (
src/app/api): 处理 HTTP 请求,进行参数校验和权限检查,然后调用 Service Layer。不应包含核心业务逻辑。 - Service Layer (
src/lib/services): 核心业务逻辑的栖息地。负责处理具体的业务规则、数据组装、调用 ORM 和其他服务。client/: 面向终端用户的服务(如subscription-service.ts,credits-service.ts)admin/: 面向管理员的服务(如admin-service.ts)settings/: 系统配置相关服务
- Data Access Layer (
src/lib/database.ts, Drizzle ORM): 负责与数据库交互 - Lib/Utils (
src/lib): 通用工具函数、常量定义、第三方库封装(如src/lib/payment,src/lib/email)
依赖注入与单例模式
- 对于复杂的外部依赖(如支付网关、存储服务),应使用工厂模式或策略模式进行封装,对外提供统一接口
- 例如:
src/lib/payment/gateway-factory.ts根据配置动态返回StripeGateway或CreemGateway实例
- 例如:
- Service 类通常导出单例实例,方便直接调用,同时保持状态(如果需要)的一致性
- 例如:
export const subscriptionService = new SubscriptionService();
- 例如:
数据库适配
- 多数据库支持: 项目同时支持 SQL (PostgreSQL) 和 SQLite
- Schema 定义: 必须同时维护
src/drizzle-postgres-schema.ts和src/drizzle-sqlite-schema.ts,确保两者字段结构一致(类型定义可能略有差异) - ORM 使用: 使用 Drizzle ORM 进行数据库操作,尽量使用类型安全的构建器语法,避免手写 SQL 字符串,以确保跨数据库兼容性
编码规范
TypeScript 规范
- 严格类型: 启用
strict: true。禁止使用any,必须定义明确的接口(Interface)或类型(Type) - 类型定义位置:
- 组件 props 定义在组件文件内部
- 公共数据模型定义在
types.ts或相关模块的 types 文件中(如src/lib/payment/types.ts)
- 非空断言: 尽量避免使用
!非空断言,优先使用可选链?.和空值合并??,或进行显式的空值检查
组件开发规范 (React/Next.js)
- 组件结构:
- 优先使用函数式组件 + Hooks
- Server Components vs Client Components: 默认使用 Server Components。只有在需要交互(onClick, useState, useEffect)时才添加
'use client'指令 - 文件命名: 组件文件使用
PascalCase.tsx(如Button.tsx),页面文件使用page.tsx
- Props 命名:
- 使用
interface定义 Props,命名遵循PascalCase+Props后缀(如SubscriptionManagerProps) - 事件处理函数 Props 命名以
on开头(如onSave,onClick)
- 使用
- Hooks: 自定义 Hooks 应放在
src/lib/hooks目录下,命名以use开头 - 国际化: 所有 UI 文本必须使用
i18next进行国际化- Server Component: 使用
src/i18n中的useTranslation(async) - Client Component: 使用
src/i18n/client中的useTranslation - 文本 Key 命名:
namespace:category.key(如home:hero.title)
- Server Component: 使用
前端开发规范
样式与 UI 规范
本项目采用 Tailwind CSS 4 + Shadcn/ui 作为核心 UI 方案。
颜色语义化(强制)
严禁使用固定颜色值。必须使用设计系统中定义的语义化 CSS 变量,以确保主题切换功能正常。
- 背景:
bg-[var(--color-bg-main)],bg-[var(--color-bg-surface)],bg-[var(--color-bg-subtle)] - 文本:
text-[var(--color-text-primary)],text-[var(--color-text-secondary)],text-[var(--color-text-tertiary)] - 边框:
border-[var(--color-border-subtle)],border-[var(--color-border-strong)] - 品牌:
bg-brand-500,text-brand-500 - 功能:
text-[var(--color-text-error)],bg-[var(--color-bg-success)]
样式编写原则
- 原子化优先: 禁止编写传统的 CSS/SCSS 文件。所有样式应尽可能通过 Tailwind utility classes 实现
- 响应式设计: 遵循 Mobile First 原则。默认编写移动端样式,使用
sm:,md:,lg:前缀覆盖大屏样式 - 类名合并: 使用
src/lib/utils.ts中的cn函数处理条件类名- ✅
className={cn("p-4 rounded", isActive && "bg-brand-500", className)}
- ✅
常用前端工具类 (Utils)
本项目在 src/lib/utils.ts 和 src/lib/hooks 中封装了大量实用工具,开发时请优先使用,避免重复造轮子。
- 样式合并:
cn(...inputs: ClassValue[])- 合并 Tailwind 类名,解决冲突 - 日期格式化:
useDateFormatHook - 处理时区和本地化日期显示formatDate(date, formatStr?): 格式化日期formatTime(date): 格式化时间formatDateTime(date): 格式化日期时间
- 价格格式化:
formatPrice(amount, currency)- 根据币种格式化价格显示 - 网络请求:
src/lib/fetch.ts- 封装了带有语言参数和错误处理的 fetch 方法fetchGet(url, options)fetchPost(url, body, options)
- 认证状态:
useAuthContext- 获取当前用户信息 (user) 和加载状态 (isPending)
新页面开发清单
创建一个新页面时,必须包含以下要素:
- 文件位置:
- 路由页面放在
src/app/[lang]/.../page.tsx - 需要权限控制的页面(如后台)应放在
(auth)或admin路由组下
- 路由页面放在
- 国际化 (i18n):
- 页面组件必须接收
params: { lang: string } - 服务端获取翻译:
const { t } = await useTranslation(lang, 'namespace') - 客户端组件通过 props 接收
lang或使用useTranslationhook
- 页面组件必须接收
- 布局与容器:
- 使用语义化标签 (
main,section,article) - 页面内容通常包裹在
Layout或div容器中,使用标准内边距
- 使用语义化标签 (
- 错误边界:
- 复杂页面建议添加
error.tsx处理运行时错误 - 未找到资源时使用
notFound()函数
- 复杂页面建议添加
- 加载状态:
- 对于异步数据加载,应显示
loading.tsx或骨架屏 (Skeleton)
- 对于异步数据加载,应显示
示例页面结构:
import { useTranslation } from '@/i18n'
export default async function Page({ params: { lang } }) {
const { t } = await useTranslation(lang, 'home')
return (
<main className="bg-[var(--color-bg-main)] min-h-screen">
<div className="container mx-auto px-4 py-8">
<h1 className="text-[var(--color-text-primary)] text-2xl font-bold">
{t('title')}
</h1>
{/* Page Content */}
</div>
</main>
)
}
核心业务模块开发指南
认证 (Authentication)
- Better Auth: 所有的认证逻辑基于
src/lib/auth.ts - Hooks:
before: 用于登录前检查(如验证码、登录锁定、注册限制)after: 用于登录后处理(如记录日志、发送欢迎邮件)
- Session: 前端使用
useAuthContext获取当前用户信息
支付与订阅 (Payment & Subscription)
- 流程: 创建 Checkout Session -> 用户支付 -> Webhook 回调 -> 更新数据库状态
- Webhook: 所有状态变更必须依赖 Webhook 通知的处理结果,而不是前端回调
- 积分逻辑:
creditsService.addCredits: 增加积分,需区分Free(赠送) 和Paid(购买)creditsService.consumeCredits: 消费积分,优先扣除Free Credits
- 幂等性: Webhook 处理逻辑必须是幂等的,即多次接收同一事件不应导致数据错误(如重复发分)
管理后台 (Admin)
- 接口权限控制: 管理端 API 接口必须严格检查管理员权限。在 API Route 中应使用
checkPermissionWithResponse(request, Permission)进行校验;在 Server Action 中应验证 session 及其权限 - 页面访问控制: 管理后台的所有页面组件必须进行权限校验。使用
PermissionGuard组件包裹受限内容,确保无权用户无法看到特定功能或页面 - 日志记录: 所有敏感操作(如数据的增、删、改)必须记录日志。调用
adminOperationLogs服务或logSimpleOperation,记录操作人、模块、行为及目标 ID
性能、安全与部署
性能优化
- 图片优化: 必须使用
next/image组件,并正确配置sizes属性以实现响应式加载 - 组件懒加载: 对于非首屏关键组件(如模态框、大型图表),使用
next/dynamic进行懒加载 - 服务端组件: 默认使用 Server Components 减少客户端 Bundle 体积
- 字体优化: 使用
next/font加载字体,避免布局偏移 (CLS)
安全规范
- 输入验证: 所有 API 接口必须使用 Zod 进行参数校验,确保输入数据的合法性与安全性
- 身份验证 (Authentication): 客户端 API 接口在处理涉及用户隐私或个性化数据的请求时,必须首先调用
verifyUserInApiRoute(request)验证用户的 Session 状态 - CSRF 防护: 所有的 POST/PUT/DELETE 请求必须通过 Next.js 的 Server Actions 或 API Routes 自动处理 CSRF
- 敏感数据: 禁止在客户端代码中暴露 API Keys、Secrets 或数据库连接字符串。环境变量需区分
NEXT_PUBLIC_前缀 - 权限校验 (Authorization): 任何涉及数据修改的接口,必须在服务端再次校验当前用户是否具有执行该操作的具体权限
部署流程
- Vercel 部署: 推荐使用 Vercel 进行部署,利用其 Edge Network 和 Serverless Functions
- 环境变量: 生产环境部署前,务必在 Vercel Dashboard 配置好所有必要的环境变量
- 数据库迁移: 部署流程中应包含
drizzle-kit migrate步骤,确保数据库结构是最新的
提交与协作规范
Git 提交信息
遵循 Conventional Commits 规范:
feat: 新功能fix: 修复 bugdocs: 文档变更style: 代码格式调整(不影响逻辑)refactor: 代码重构(无新功能或 bug 修复)chore: 构建过程或辅助工具的变动
示例: feat(auth): add google login support
分支管理
main: 主分支,保持稳定,随时可部署develop: 开发分支,日常开发合并至此feature/*: 功能分支,从develop检出,完成后提 PR 合并回developfix/*: 修复分支
代码审查 (Code Review)
- 提交 PR 前请先自测,并确保通过 lint 检查
- PR 描述应清晰说明变更内容、目的及测试方法
- 核心逻辑变更(特别是涉及支付、权限部分)必须经过严格审查
常用命令速查
| 命令 | 说明 |
|---|---|
pnpm dev | 启动开发服务器 |
pnpm build | 构建项目 |
pnpm drizzle-kit generate | 数据库迁移生成 |
pnpm drizzle-kit push | 数据库迁移推送 |
pnpm prettier --write . | 代码格式化 |