# AGENTS.md

## 目录


| 章节                        | 何时查                     |
| ------------------------- | ----------------------- |
| [运行环境与仓库结构](#运行环境与仓库结构)   | 不确定 monorepo 各 app 分工   |
| [本地启动](#本地启动)             | 起 dev、数据库、端口            |
| [常用命令](#常用命令)             | build / test / 打镜像      |
| [API 领域结构](#api-领域结构)     | 业务代码该放哪个 domain         |
| [API 约定](#api-约定)         | 鉴权、钱包、迁移、赔率快照           |
| [前端约定](#前端约定)             | 改 .vue/.ts、权限、dist      |
| [i18n 改文案流程](#i18n-改文案流程) | 三语文案改哪几个文件              |
| [主题分支](#主题分支player-多皮肤)   | theme-2/3/4 与 main 同步禁忌 |
| [演示账号](#演示账号开发-seed)      | 本地登录测试号                 |
| [文档索引](#文档索引)             | 详细说明在 `docs/` 哪篇        |
| [测试与冒烟](#测试与冒烟)           | Jest、UAT、smoke-tests    |
| [部署注意](#部署注意)             | 生产 compose、seed、打包      |
| [AI 改代码时请避免](#ai-改代码时请避免) | 常见踩坑清单                  |


> **AI 会读吗？** Cursor 等工具会在对话开始时把 `AGENTS.md` 注入上下文；当前约 200 行，一般能整篇读入。文件继续变长时，索引有助于人和 AI 快速定位章节；细节仍以 `docs/` 为准，此处只写约定与指针。

## 运行环境与仓库结构

- 使用 Node 22+、pnpm 11；`packageManager` 固定为 `pnpm@11.5.2`。
- pnpm workspace：`apps/api`（NestJS/Prisma）、`apps/player`（Vue H5，`:5173`）、`apps/admin`（平台管理 + 代理合一，`:5174`）、`packages/shared`（共享类型/常量与 `public` 静态资源）。
- 前端将 `@thebet365/shared` 别名到 `packages/shared/src/index.ts`（构建产物为 CommonJS）；改 shared 时须更新源码导出，不要只改 `dist`。
- `packages/shared` 构建前会执行 `scripts/generate-phone-countries.mjs`，再跑 `tsc`。
- 本地开发 compose：`docker-compose.yml`（仅 Postgres + Redis）；生产 compose：`docker-compose.prod.yml` + `.env.docker`。

## 本地启动

- 基础设施：`docker compose up -d`（PostgreSQL `5432`、Redis `6379`）。
- API 环境变量：`cp .env.example apps/api/.env`；通过 workspace filter 启动时，Nest 从 API 工作目录读 `.env`。
- 首次数据库（仓库根目录）：`pnpm db:generate` → `pnpm db:migrate` → `pnpm db:seed`。
- `pnpm dev:api` 会先执行 `scripts/ensure-port-free.mjs 3000`，释放 3000 端口再启动 watch。
- 分应用启动时，**先 API**，再 `pnpm dev:player` 或 `pnpm dev:admin`；两个 Vite 都把 `/api` 代理到 `http://localhost:3000`。
- Windows PowerShell 不支持 `&&` 链式命令，需分步执行或用 `;`。

## 常用命令

- `pnpm dev`：先构建 shared，再并行跑各 workspace 的 `dev`。
- `pnpm dev:admin` 与 `pnpm dev:manage` 是同一个管理端应用。
- 全量校验：`pnpm build`、`pnpm test`；根目录**没有**统一的 lint/format 脚本。
- API 测试：`pnpm --filter @thebet365/api test`。
- 单测文件：`pnpm --filter @thebet365/api exec jest settlement-calculator.spec.ts --runInBand`；不要用 `pnpm ... test -- ...` 传参，pnpm 会把字面量 `--` 传给 Jest。
- 前端构建：`pnpm --filter @thebet365/player build`、`pnpm --filter @thebet365/admin build`。
- 管理端体积分析：`pnpm --filter @thebet365/admin build:analyze`。
- 本地镜像打包（Windows）：`docs\docker\build-and-export-images.bat --tag latest`。
- 生产更新（服务器）：`./scripts/deploy-update.sh --images thebet365-images-<tag>.tar --tag <tag>`（详见 `docs/Docker部署指南.md` 第八节）。

## API 领域结构

业务规则放在 `apps/api/src/domains/`*，应用层只做编排：


| 领域                         | 路径                    | 职责            |
| -------------------------- | --------------------- | ------------- |
| identity                   | `domains/identity/`   | 登录、用户、员工、RBAC |
| agent                      | `domains/agent/`      | 代理网络、授信       |
| ledger                     | `domains/ledger/`     | 钱包、账变         |
| catalog                    | `domains/catalog/`    | 赛事            |
| odds                       | `domains/odds/`       | 盘口            |
| betting                    | `domains/betting/`    | 注单            |
| settlement                 | `domains/settlement/` | 结算            |
| operations                 | `domains/operations/` | 返水、内容、审计等     |
| player-messages / presence | 各子模块                  | 站内信、在线状态      |


门户控制器：`applications/{player,admin,agent}/`。

## API 约定

- 全局前缀 `/api`；非生产或 `ENABLE_SWAGGER=true` 时 Swagger 在 `/api/docs`。
- `AppModule` 注册了全局 `JwtAuthGuard`；公开接口须加 `@Public()`（`apps/api/src/shared/common/decorators.ts`）。
- 业务规则放在 `domains/*`；`applications/*` 只做门户/控制器编排，不要写领域规则。
- 钱包变动走 ledger/wallet 服务；UAT 明确禁止为修结算直接改余额。
- 下注赔率以提交时快照为准（`BetSelection.odds` + `oddsVersion`），结算用快照，不受后续盘口变动影响。
- 改 `apps/api/prisma/schema.prisma` 后执行 `pnpm db:generate`；开发迁移 `pnpm db:migrate`，部署由 `deploy-update.sh` 执行 `prisma migrate deploy`。
- 新增迁移后，theme 分支若只改 player 样式，也须同步 API/Admin 与迁移文件。

## 前端约定

- 改 `apps/player/src`、`apps/admin/src` 下的 `**.ts` / `.vue`**；`src` 下虽有 `.js` 旁文件，但 `index.html` 入口是 `/src/main.ts`，Vite 优先解析 TS。
- 管理端同一应用服务 `ADMIN` 与 `AGENT` 账号；路由/菜单权限在 `router/index.ts`、`stores/auth.ts`；员工可见菜单字段 `visibleMenus`。
- 管理端权限常量：`apps/admin/src/constants/permissions.ts`（`AdminPerm`）。
- 玩家端移动优先；性能验收见 `docs/player-mobile-performance.md`。
- 管理端切页慢的分析与优化任务见 `docs/admin-page-switch-performance.md`。
- **不要**把 `apps/player/dist/` 提交进 Git；生产由 Docker 构建生成静态资源。

## i18n 改文案流程

支持语言均为 **zh-CN / en-US / ms-MY**（马来语）。改文案须**三语同步**，不要只改中文。

### 玩家端（`apps/player`）


| 项      | 说明                                                               |
| ------ | ---------------------------------------------------------------- |
| 文案文件   | `apps/player/src/i18n/zh-CN.ts`、`en-US.ts`、`ms-MY.ts`            |
| 结构     | **嵌套对象**（如 `bet.place_bet`、`nav.home`）                           |
| 组件用法   | `useI18n()` → `t('bet.odds_changed')`                            |
| 语言切换   | `useAppLocale().setLocale()`；按需 `ensurePlayerLocale` 懒加载对应 ts 文件 |
| 存储 key | `localStorage.locale`；登录用户会 `POST /player/language` 同步后端         |


**改文案步骤：**

1. 在 Vue 里用 `t('模块.key')` 引用，**不要**在模板写死中文。
2. 在 **三个** locale 文件的**相同路径**下各加一条（保持 key 一致）。
3. 带占位符用 vue-i18n 约定，如 `'共 {n} 场': '共 {n} 场'` → `t('key', { n: 3 })`。
4. 本地 `pnpm dev:player` 切换语言各看一遍；热更新一般即时生效。
5. theme 分支合并 main 功能时：**只合并新增 key**，勿用 main 文件整文件覆盖（theme 可能有不同措辞）。

### 管理端（`apps/admin`）


| 项         | 说明                                                                                  |
| --------- | ----------------------------------------------------------------------------------- |
| 核心入口      | `apps/admin/src/i18n/admin-messages.ts`（三语扁平 `Record<string, string>`）              |
| 列表/弹窗大段文案 | `admin-pages.ts`（中/英）、`admin-pages-ms.ts`（马来）→ 通过 spread 并入 `admin-messages`        |
| 表单校验 key  | `form-validation.ts`（`err.`* 等，用 `resolveFormError` 展示）                             |
| 组件用法      | `useAdminLocale().t('nav.bets')` 或 `t('key', { n: 1 })` 占位符 `{n}`                   |
| 语言切换      | `useAdminLocale().setLocale()` → `preloadAdminLocale` + `localStorage.admin_locale` |
| 动态拆包      | `bundles/{zh-CN,en-US,ms-MY}.ts` + `locale-loader.ts`（当前主包仍含三语全文，拆包未完全落地）           |


**改文案步骤：**

1. 确定 key 命名：导航 `nav.`*、通用 `common.`*、页面 `user.*` / `match.*` / `deposit.*` 等，与现有前缀保持一致。
2. 在 `admin-messages.ts` 的 **zh / en / ms 三个对象**里各加同名 key（核心短文案）。
3. 若属于某列表页/弹窗长文案，优先加到 `admin-pages.ts`（中/英）与 `admin-pages-ms.ts`（马来）的对应 export，它们会 spread 进 `admin-messages`。
4. 表单校验错误：throw `FormValidationError('err.xxx')` 并在三语里定义 `err.xxx`。
5. 本地 `pnpm dev:admin`，右上角切换语言验证；改 `admin-pages`* 后若未刷新，重启 dev 一次。
6. Admin 与 Player **文案独立**，改玩家端不要只改 admin 文件（反之亦然）。

### 常见错误

- 只改 `zh-CN` 未改 `en-US`、`ms-MY` → 切换语言显示 key 或英文 fallback。
- 玩家端 key 层级不一致（如中文有 `bet.foo` 英文漏了 `bet` 层）→ `t()` 返回 key 字符串。
- 管理端在 `admin-pages.ts` 只加了 `adminPagesZh` 未加 `adminPagesEn` / `adminPagesMs`。
- theme 分支用 `git checkout main -- apps/player/src/i18n/` 覆盖 → 冲掉主题分支已有翻译。

### 与业务/后台内容的关系

- **i18n 文件**：前端固定 UI 标签、按钮、提示。
- **管理端「公共管理」/ 公告 / Banner**：运营可配正文，走 API 与数据库，**不要**硬编码进 i18n（玩家端公告详情等展示 API 返回内容）。

## 主题分支（player 多皮肤）


| 分支        | 风格          | 注意                                                                  |
| --------- | ----------- | ------------------------------------------------------------------- |
| `main`    | 暗金主题        | 生产默认发版分支                                                            |
| `theme-2` | Pinnacle 蓝白 | 玩家 UI 与 main 分叉，**禁止** `git checkout main -- apps/player/...` 整文件覆盖 |
| `theme-3` | 统一移动端视觉     | 同 theme-2                                                           |
| `theme-4` | 海军蓝暗色极简     | 含悬浮客服等独有组件                                                          |


同步功能到 theme 分支：API/Admin/迁移可整目录 checkout；Player 只做逻辑合并 + 保留各分支 `styles.css` 与主题资源。发版前在目标分支打包镜像。

## 演示账号（开发 seed）


| 角色    | 用户名     | 密码         |
| ----- | ------- | ---------- |
| 超级管理员 | admin   | Admin@123  |
| 一级代理  | agent1  | Agent@123  |
| 二级代理  | agent2  | Agent@123  |
| 玩家    | player1 | Player@123 |


生产 seed 仅创建 admin + WC2026 样例数据，不含代理/玩家演示号。详见 `docs/默认数据说明.md`。

## 文档索引


| 文档                                          | 用途                  |
| ------------------------------------------- | ------------------- |
| `docs/项目启动指南.md`                            | 本地开发、排错、端口          |
| `docs/Docker部署指南.md`                        | 生产部署、备份、回滚、发版流程     |
| `docs/docker/镜像构建与导出.md`                    | 本地打镜像 tar           |
| `docs/默认数据说明.md`                            | seed 数据、WC2026、48 强 |
| `docs/投注玩法说明.md`                            | 玩法与判赢规则             |
| `docs/结算与返水金额规则.md`                         | 派彩/返水金额公式           |
| `docs/settlement-and-fund-flow-analysis.md` | 结算操作流程              |
| `docs/手动充值功能说明.md`                          | 充值审核流程              |
| `docs/短信调试与日志说明.md`                         | 创蓝短信排错              |
| `docs/UAT_CHECKLIST.md`                     | 上线前回归清单             |
| `docs/player-mobile-performance.md`         | 玩家端性能验收             |
|                                             |                     |


## 部署注意

- 生产 compose 使用 `.env.docker`：`docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --build`，或配置好后 `pnpm docker:up`。
- **不要**用 example 覆盖线上 `.env.docker`；部署脚本通常只回写 `IMAGE_TAG`。
- 生产首次 seed 后保持 `SEED_DATABASE=false`；迁移由 `deploy-update.sh` 执行，勿在 API 容器设 `RUN_MIGRATIONS_ON_START=true`（除非应急）。
- `scripts/prod-init-db.sh` **会清空业务数据**：须 `CONFIRM=YES`，默认先备份，再 truncate 并灌生产数据。
- 打包部署 zip：`pnpm pack:deploy`；`pack.mjs` 会删除 `packages/shared/public` 下除 `flags`、`players` 外的遗留中文目录。
- 构建 player/admin 若报 `ENOENT ... public/球员`：清理 `packages/shared/public` 下错误中文目录后重试。

## AI 改代码时请避免

- 不要 `git merge main` 进 theme 分支做 player 同步（会把 main 暗金样式大量带入）。
- 不要直接改数据库余额修结算；走结算/钱包服务。
- 不要提交 `.env`、`.env.docker`、镜像 tar、`apps/*/dist/`。
- 改 player 主题相关文件时，先确认当前分支是 `main` 还是 `theme-`*，避免用错 CSS 变量（如 theme-2 无 `--gold`）。
- 管理端改列表页性能时，参考 `docs/admin-page-switch-performance.md` 任务清单，优先 KeepAlive + 缓存而非盲目减 API 字段。

