Files
workspace/code/app/项目开发规范.md
2026-07-06 17:03:39 +08:00

8.0 KiB
Raw Permalink Blame History

项目开发规范(AI必读)

数据库

1. 表命名

格式:b_{模块}_{实体}

所有业务表以 b_ 前缀开头,使用 snake_case。例如:b_clothing_clothes、b_meal_dish、b_chat_file。

2. 关联表命名

M:N 关联表直接用「主实体 + 子实体」拼接:

格式:b_{模块}_{主实体}_{子实体}

例如:b_clothing_outfit_clothes、b_meal_dish_ingredient。

禁止:多态关联(target_type + target_id),应拆分为独立表。

3. 标签表

标签表统一用 _tag 后缀:

表名 用途
b_clothing_tag 衣物标签(父子结构)
b_meal_tag 餐食标签(父子结构)
b_clothing_clothes_tag 衣服 ↔ 标签关联
b_meal_dish_tag 菜品 ↔ 标签关联
  • 父子结构:parent_id BIGINT,NULL 表示顶层,不要用 0
  • 颜色字段:color varchar(7),存储 hex 色值如 #EF4444

4. 字段命名

审计字段(所有业务表必须包含):

id          BIGINT PRIMARY KEY,          -- 雪花 ID
create_time TIMESTAMPTZ DEFAULT now(),
update_time TIMESTAMPTZ DEFAULT now(),
create_by   BIGINT,
update_by   BIGINT

审计字段由 pkg/db/meta_field.go 自动注入,仅有 b_user 和 b_user_oauth 豁免。

通用业务字段:

family_id   BIGINT  NOT NULL,          -- 家庭 ID(多租户隔离)
status      SMALLINT NOT NULL DEFAULT 1, -- 0=停用, 1=启用
sort_number INT      NOT NULL DEFAULT 0, -- 排序序号

命名约定:

  • 全部 snake_case
  • 外键:{实体}_id,如 family_id、category_id、dish_id
  • 图片:image_file_key 或 {用途}_file_key,如 cover_file_key
  • 数值金额:amount numeric(12,2)
  • 金额:amount numeric(12,2)

4. 索引命名

格式:idx_{表名}_{用途}

  • 主键约束:{表名}_pkey
  • 唯一约束:{表名}_{字段}_key
-- 按业务查询
idx_b_clothing_clothes_family_status_category
-- 关联表双向索引
idx_b_meal_dish_tag_dish    -- 按主实体查
idx_b_meal_dish_tag_tag     -- 按子实体反查
-- 唯一约束
idx_b_meal_plan_family_date_slot

6. 不允许的坏味道

  • ❌ 时间戳用 timestamp without time zone → 必须用 TIMESTAMPTZ
  • ❌ parent_id 用 0 表示顶层 → 必须用 NULL
  • ❌ create_by / update_by 设 NOT NULL → 可为空,由应用层注入
  • ❌ 表名/索引名中包含过时模块名(如 b_space 残留)
  • ❌ 一张表用多态字段关联多种实体

前端

1. 项目结构

app-rn/src/
  app/           Expo Router 页面(文件即路由)
  components/    可复用组件
  configs/       配置定义
  helpers/       业务辅助函数
  hooks/         自定义 Hooks
  layouts/       布局组件
  request/       API 封装
  store/         Zustand 状态
  types/         TypeScript 类型
  utils/         通用工具

2. 命名规范

成员 规范 示例
组件 PascalCase PageLayout、Home
普通函数 camelCase formatMoney
Hooks use 开头 useLoading、useFamilyStore
API 函数 Api 结尾 loadDataApi、saveDataApi
类型/接口 PascalCase ClothingTag、FilterState
文件名 camelCase.ts(x) useLoading.ts、finance.tsx
  • 类型定义优先用 interface 而非 type
  • 领域类型按文件拆分:auth.ts、finance.ts、clothing.ts

3. 数据访问

所有数据操作通过 src/request/api.ts 的 5 个封装函数:

loadDataApi({ view_name, search_condition, args, ... })
loadDataPageApi({ view_name, page, page_size, ... })
loadDataBySqlApi({ sql, args })
saveDataApi([{ table_name, key_field, inserts, updates, deletes }])
getUniqueIdApi(count)
  • 必须传递 family_id:useFamilyStore((s) => s.getFamilyId())
  • 响应通过 res.isSuccess 判断结果

4. 操作记录

所有增/改/删操作完成后必须通过 useModuleChatNotifier 记录操作:

import { useModuleChatNotifier } from "@/hooks/useModuleChatNotifier";

const notifyOp = useModuleChatNotifier();

notifyOp({
  module: "餐食",          // 模块名
  action: "创建菜单",      // 操作描述
  target: planDate,        // 操作目标
  sourceTable: "b_meal_plan",
  sourceId: targetId,
});

5. 状态管理

  • 全局持久化(用户/家庭):Zustand + persist 中间件 + AsyncStorage
  • 模块内部:Zustand 内存 store,不持久化
  • Store 接口同时包含状态和方法

6. 样式与组件

  • 样式用 NativeWind className,禁止内联 style={}
  • 尽量使用主题色:颜色统一用 useThemeColor() 获取,不硬编码色值
  • 图标用 lucide-react-native,颜色通过 useThemeColor("accent") 获取(className="text-accent" 对图标不生效)
  • UI 组件优先用 HeroUI Native(heroui-native)
  • 页面用 PageLayout / ModuleLayout / AppLayout 包裹
  • 页面布局尽量使用 LayoutView + LayoutView.ScrollView
  • 页面搜索/筛选参考:app-rn/src/app/finance/(tabs)/home/index.tsx
  • 嵌套 BottomSheet 筛选参考:app-rn/src/app/clothes/outfit/edit/index.tsx

7. BottomSheet 规范

  • Title 必须居中:<Text className="text-center text-lg font-semibold">
  • 必须限制最大高度:内容区设置 maxHeight,防止弹出层过高
  • 搜索/筛选过多时加独立筛选 BottomSheet:如果选择器内筛选条件过多(如选择衣服时按分类、标签、归属者筛选),筛选按钮触发第二个 BottomSheet,避免第一个 BottomSheet 内容过载

8. 异步操作

所有保存/更新/删除操作必须使用 useLoading + <LoadingOverlay>:

const { isLoading, startLoading, stopLoading } = useLoading();

startLoading("保存中...");
const res = await saveDataApi([...]);
if (res.isSuccess) {
  stopLoading({ type: "success", message: "已保存" });
} else {
  stopLoading({ type: "error", message: "操作失败" });
}

禁止手动管理多个 loading 布尔值。

后端

1. 项目结构

app-go/
  internal/
    config/       配置加载
    handle/       HTTP handler
    httpx/        请求/响应工具
    middleware/   Fiber 中间件
    router/       路由注册
    types/        DTO
    ws/           WebSocket
  pkg/
    db/           PostgreSQL 客户端
    jwtx/         JWT
    logger/       日志
    s3store/      S3 存储
    uniqueid/     雪花 ID 生成

2. 命名规范

成员 规范 示例
导出函数/类型 PascalCase LoadData、LoginQqReq
非导出函数 camelCase handleThirdLogin
包名 单个小写单词 package handle
文件名 snake_case.go meta_field.go
JSON 标签 snake_case json:"family_id"

3. API 设计

不允许创建专用 REST 端点。 所有数据操作复用 5 个通用 API:

端点 用途
POST /app/data/loadData 条件查询
POST /app/data/loadDataPage 分页查询
POST /app/data/loadDataBySql 原生 SQL
POST /app/data/saveData 批量增/改/删
POST /app/data/getUniqueId 获取雪花 ID

非必要不新增接口。 新增任何接口前必须先询问并获得确认,且新增接口必须是通用型的,不能为某个业务功能单独定制。

4. 数据访问

  • 所有业务数据关联 family_id,查询时必须按当前用户的 family_id 过滤
  • 审计字段(create_time、update_time、create_by、update_by)由 pkg/db/meta_field.go 自动注入,仅 b_user 和 b_user_oauth 豁免
  • 事务使用闭包模式:dbClient.WithTx(ctx, func(tx *db.Client) error { ... })
  • 外部调用错误用 errors.WithStack(err) 包装,业务错误用 httpx.Fail(c, "消息") 返回

5. 导入顺序

内部包(allapp-go/)在前,第三方包在后,两组之间空一行:

import (
    "allapp-go/internal/httpx"
    "allapp-go/internal/types"

    "github.com/gofiber/fiber/v3"
)