Files
workspace/code/app/.workbuddy/skills/allapp-conventions/SKILL.md
T
2026-06-23 21:29:18 +08:00

12 KiB
Raw Blame History

name, description, agent_created
name description agent_created
allapp-conventions Allapp 项目开发规范和数据库配置指南。该 skill 应在以下场景使用:(1) 在 Allapp 项目中新增功能、 创建数据库表、或调用后端 API 时;(2) 需要了解项目通用 API 接口用法、数据库连接信息、 表结构约定时;(3) 需要遵循项目的代码规范时。触发词包括:allapp、记账、app-go、app-rn、 数据库连接、API 接口、建表、收藏功能等。 true

Allapp 项目开发规范

项目概览

Allapp 是一个家庭/个人空间型 mini 程序平台:

  • 后端 app-go/:Go + Fiber v3 + PostgreSQL (pgx) + JWT
  • 前端 app-rn/:React Native + Expo 55 + Expo Router + Zustand + Alova
  • 数据库 PostgreSQL,库名 allapp,schema public

数据库连接信息见 references/db-config.md。

项目结构速查

app-go/                          # Go 后端
├── configs/                     # YAML 配置(config.yaml / config.dev.yaml)
├── internal/
│   ├── bootstrap/               # 启动入口
│   ├── config/                  # 配置加载
│   ├── handle/                  # 请求处理(auth / data / data_save / data_list / s3)
│   ├── httpx/                   # 请求/响应工具
│   ├── middleware/               # 中间件(auth / logger)
│   ├── router/                  # 路由注册
│   └── types/                   # 请求/响应类型
├── pkg/
│   ├── db/                      # 数据库操作(insert / update / meta_field)
│   └── jwtx/                    # JWT 工具
└── main.go

app-rn/                          # React Native 前端
├── app.json
├── package.json                 # ⚠️ 写页面必须先读这个
├── tailwind.config.ts
├── src/
│   ├── app/                     # Expo Router 页面
│   │   ├── _layout.tsx          # 根布局
│   │   ├── (main)/              # 主应用(route group,不影响 URL)
│   │   │   ├── (tabs)/              # 主 Tab(home / chat / my)
│   │   │   ├── auth/                # 登录页
│   │   │   ├── family/              # 家庭管理
│   │   │   ├── tools/               # 工具广场
│   │   │   └── activities/          # 动态列表
│   │   └── finance/                 # 记账子模块(独立于 main)
│   ├── components/              # 通用组件(layout/view / AZIndex 等)
│   ├── configs/                 # pages.ts(子应用列表配置)
│   ├── layouts/                 # 布局(ModuleLayout 等)
│   ├── request/                 # API 封装(index.ts / api.ts)
│   ├── store/                   # Zustand 状态管理
│   └── types/                   # TypeScript 类型定义

前端关键依赖库

写页面/组件时必须优先使用项目已安装的库,不要引入新包:

库 用途 导入方式
heroui-native UI 组件库(PressableFeedback、SearchField、Button 等) import { PressableFeedback, SearchField } from "heroui-native"
@legendapp/list 高性能长列表 import { LegendList } from "@legendapp/list"
lucide-react-native 图标库 import { Star, ... } from "lucide-react-native"
zustand 状态管理 import { create } from "zustand"
alova + @alova/adapter-axios 请求库 import { createAlova } from "alova"
dayjs 日期处理 import dayjs from "dayjs"
expo-router 路由 import { useRouter } from "expo-router"
react-native-reanimated 动画 import Animated from "react-native-reanimated"
react-native-gesture-handler 手势 与 reanimated 配合
react-native-safe-area-context 安全区域 import { useSafeAreaInsets } from "react-native-safe-area-context"
tailwindcss + tailwind-merge 样式 className 方式,import { twMerge } from "tailwind-merge"
sonner-native Toast 提示 import { toast } from "sonner-native"
@gorhom/bottom-sheet 底部弹出面板
expo-image 图片组件 import { Image } from "expo-image"

样式约定:项目使用 NativeWind(Tailwind CSS),className 方式写样式,比如 className="bg-white rounded-xl shadow-lg"。不要写内联 style={{}} 除非必要。

核心开发规范

规则 0:写页面前必须先读 package.json

每次写新页面或新组件之前,必须先用 Read 工具读取 app-rn/package.json,了解项目已有的依赖库,确保:

  • 使用项目已有的组件库和工具库,不要引入未安装的包
  • 了解可用的第三方组件(如 heroui-native、@legendapp/list 等),优先复用

规则 1:禁止新增专用 API 接口

项目已有 4 个通用数据 API,所有 CRUD 操作必须复用它们,不得再创建专用 REST 端点:

接口 路径 用途
loadData POST /data/loadData 查询:传入 view_name(表/视图名)、search_condition(WHERE)、order_by、search_columns、args
saveData POST /data/saveData 增删改:传入 [{table_name, key_field, inserts, updates, deletes}] 数组
loadDataBySql POST /data/loadDataBySql 原生 SQL:传入 sql 和 args
getUniqueId POST /data/getUniqueId 获取雪花 ID:传入 count(1-100)

新增接口必须满足两个条件:

  1. 先向用户确认是否允许新增接口
  2. 新接口必须是通用接口(类似上面四种),不能专属于某个功能

前端调用示例:

// 查询
import { loadDataApi, saveDataApi, getUniqueIdApi } from "@/request/api";

const res = await loadDataApi({
  view_name: "b_user",
  search_condition: "id = ?",
  search_columns: [],
  order_by: "",
  args: [userId],
});

// 插入
const idRes = await getUniqueIdApi(1);
await saveDataApi([{
  table_name: "b_user",
  key_field: "id",
  inserts: [{ id: idRes.data[0], nickname: "test", ... }],
  updates: [],
  deletes: [],
}]);

规则 2:数据库表必须包含审计字段

所有业务表的必需列:

id           BIGINT PRIMARY KEY,     -- 雪花 ID
create_time  TIMESTAMPTZ NOT NULL,   -- 创建时间
update_time  TIMESTAMPTZ NOT NULL,   -- 更新时间
create_by    BIGINT,                 -- 创建者 user_id
update_by    BIGINT                  -- 更新者 user_id

后端 pkg/db/meta_field.go 中的 applyMetaFields 函数会为 insert/update 自动注入这些字段。 只有 b_user 和 b_user_oauth 两类系统表在 auditExcludeTables 中豁免,其他新表一律不要加豁免。

ID 获取方式:

  • 前端:调用 getUniqueIdApi(count) 获取
  • 后端:调用 uniqueid.NextId() 获取

规则 3:不要重复造轮子

  • 前端 API 请求:统一使用 @/request/api 中已有的方法,不要创建新的 API 封装
  • 前端状态管理:优先复用已有的 Zustand store(@/store/),新 Store 只用通用 API
  • 前端组件:
    • UI 组件优先用 heroui-native(PressableFeedback、SearchField、Button、Spinner 等)
    • 长列表用 @legendapp/list(LegendList),不要用 FlatList/ScrollView 处理大量数据
    • 图标统一用 lucide-react-native
    • Toast 用 sonner-native
    • 底部面板用 @gorhom/bottom-sheet
    • 样式用 NativeWind className,避免内联 style
  • 后端:不要创建新的 handler 和路由,除非是通用基础设施

规则 4:家庭/空间为中心的设计

系统以家庭(Family)为核心实体。所有业务数据(记账、收藏等)都跟 family_id 关联。 用户通过 family_member 表关联到家庭。

关键文件索引

文件 说明 写页面必读
app-rn/package.json 前端依赖清单,了解可用库 ⭐
PROJECT_CONTEXT.md 完整项目文档
db.local.yml 本地数据库连接备忘
app-go/internal/router/router.go 后端路由定义
app-go/pkg/db/meta_field.go 审计字段自动注入逻辑
app-rn/src/request/api.ts 前端 API 方法
app-rn/src/store/ Zustand 状态管理
app-rn/src/configs/pages.ts 子应用列表配置
app-rn/src/components/ 通用组件

当前数据库表

b_user                   - 用户
b_user_oauth             - 第三方登录绑定
b_family                 - 家庭/空间
b_family_member          - 家庭成员
b_finance                - 记账记录
b_finance_category       - 记账分类
b_finance_category_default - 默认分类模板
b_app_favorite           - 子应用收藏
b_family_feed            - 家庭动态

视图:v_family_member(join b_family_member + b_user)

家庭动态(b_family_feed)记录规范

以下场景必须调用 recordFeed() 写入动态。

type 简码对照:join(加入/创建)、rename(改名)、bill(记账)、fav(收藏)

触发场景 type content 模板 触发位置
创建家庭 join {nickname} 创建了家庭「{名称}」 family/entry.tsx handleCreate
加入家庭 join {nickname} 加入了家庭 family/entry.tsx handleJoin
修改昵称 rename {nickname} 修改了昵称为「{新昵称}」 family/user.tsx handleRename
记账 bill {nickname} 记了一笔「{备注}」¥{金额} finance/add/ 保存成功
收藏应用 fav {nickname} 收藏了「{应用名}」 tools/index.tsx 收藏操作

⚠️ content 自包含原则:所有展示信息直接写在 content 字符串中,不依赖 meta 字段。recordFeed() 不接收 meta 参数,loadFeeds() 只做 {nickname} 替换。meta 列保留但始终为 NULL。

content 占位符规则:content 存储模板字符串,用户昵称用 {nickname} 占位,不在写入时拼接。loadFeeds() 会自动将 {nickname} 替换为 b_family_member.nickname 的最新值,确保改名后历史动态也显示最新昵称。

import { recordFeed } from "@/helpers/feed";

// 正确写法 — 用 {nickname} 占位符,不拼接昵称
recordFeed(familyId, userId, "join", "{nickname} 加入了家庭");

// 错误写法 — 禁止拼接昵称写死
recordFeed(familyId, userId, "join", `${nick} 加入了家庭`); // ❌

修改昵称时同步更新:family/user.tsx 的 handleRename 除了更新 b_user.name,还必须同步更新 b_family_member.nickname:

saveDataApi([
  { table_name: "b_user", updates: [{ id: uid, name: newName }] },
  { table_name: "b_family_member", updates: [{ id: memberId, nickname: newName }] },
]);

前端:拉取数据

import { loadDataApi } from "@/request/api";

// 查当前家庭的收藏
const res = await loadDataApi({
  view_name: "b_app_favorite",
  search_condition: "family_id = ?",
  order_by: "id ASC",
  search_columns: [],
  args: [familyId],
});
if (res.isSuccess) {
  const list = res.data; // any[]
}

前端:保存数据(新增)

import { saveDataApi, getUniqueIdApi } from "@/request/api";

const idRes = await getUniqueIdApi(1);
await saveDataApi([{
  table_name: "b_app_favorite",
  key_field: "id",
  inserts: [{
    id: idRes.data[0],
    family_id: familyId,
    user_id: userId,
    app_id: "finance",
  }],
  updates: [],
  deletes: [],
}]);

前端:保存数据(删除)

await saveDataApi([{
  table_name: "b_app_favorite",
  key_field: "id",
  inserts: [],
  updates: [],
  deletes: [{ id: recordId }],
}]);

Python:连接数据库

import psycopg2
conn = psycopg2.connect(
  'postgres://postgres:<password>@117.72.182.135:5432/allapp?sslmode=disable'
)
# 具体密码见 references/db-config.md