# AGENTS.md

This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.

## 项目概览

零食商城（毕业设计）— 前后端分离的电商平台，**SpringBoot 4 + Vue 3** 单体多模块仓库：

| 目录 | 角色 | 技术栈 |
|------|------|--------|
| `server-snack/` | 后端 API | Spring Boot 4.0.6、Java 21、MyBatis-Plus 3.5、Sa-Token 1.45、Redis、MySQL 8、Knife4j 4.6 |
| `admin-snack/` | 管理后台前端 | Vue 3.5 + Vite 6 + TypeScript 5 + Element Plus 2.9 + Pinia 2 + ECharts 5 |
| `db/` | 数据库脚本与设计文档 | `schema.sql`（建库 + 21 张表 DDL）、`seed.sql`（演示数据）、`BUSINESS_DESIGN.md` |
| `功能设计文档.md` | 完整功能与接口设计（仓库根） | v1.2，覆盖用户端/管理端/客服/公告/优惠券/抢购 6 大模块 |

> 用户端 `web-snack` 模块当前未在本仓库中（设计文档中描述），当前实际前端仅有 `admin-snack` 管理端。

## 常用命令

### 后端（`server-snack/`）

```bash
# 构建（首次会下载依赖，可能需要较长时间）
mvn clean package

# 跳过测试运行
mvn spring-boot:run

# 单独跑测试
mvn test
mvn test -Dtest=类名#方法名   # 单个测试方法

# 开发工具热重启
mvn spring-boot:run -Dspring-boot.run.fork=true
```

- 启动类：`com.snack.server.ServerSnackApplication`
- 监听端口：**8080**
- 接口文档（Knife4j）：`http://localhost:8080/doc.html`
- MySQL 默认连接：`jdbc:mysql://localhost:3306/snack_mall`（账号 `root` / `123456`，见 `application.yml`）
- Redis 默认：`localhost:6379`
- **本地上传文件** 存储到 `server-snack/uploads/`，通过 `/uploads/**` 暴露为静态资源

### 数据库（`db/`）

```bash
mysql -u root -p < schema.sql   # 建库 + 21 张表
mysql -u root -p < seed.sql     # 演示数据（管理员 admin/123456，user001~user005/123456）
```

### 管理端（`admin-snack/`）

```bash
# 要求 Node ≥ 20
npm install
npm run dev                # 开发服务器（http://localhost:5173，已配置 /api 和 /uploads 代理到 8080）
npm run build              # 类型检查 + 生产构建（默认 mode）
npm run build:test         # 测试环境构建
npm run build:prod         # 生产环境构建
npm run type-check         # vue-tsc 类型检查
npm run lint               # ESLint --fix
npm run format             # Prettier 格式化
```

> 仓库内 `pnpm-lock.yaml` 已存在，但 `package.json` 脚本未区分包管理器；如本机用 pnpm，命令相同（`pnpm dev` 等）。
> 项目内默认账号 `admin` / `123456`（管理端登录页 + 数据库 seed 同步）。

## 架构与约定

### 后端分层

每个业务模块位于 `server-snack/src/main/java/com/snack/server/module/<name>/`，统一包结构：

```
module/<name>/
├── controller/   # @RestController，按"用户端 / 管理端"分文件（如 ProductPublicController / ProductAdminController）
├── service/      # 业务接口
│   └── impl/     # 业务实现
├── mapper/       # MyBatis-Plus BaseMapper
├── entity/       # 数据库表实体（含 createTime / updateTime，会被 MetaObjectHandler 自动填充）
├── dto/req       # 入参（带 jakarta.validation 注解）
├── vo/           # 出参（视图对象）
├── enums/        # 模块内枚举
└── constant/     # 模块内常量
```

跨模块共享放在 `com.snack.server.{common,config,constant,exception,handler,utils,enums,websocket}`。

### 关键技术点

1. **认证 — Sa-Token（注意不是 JWT）**
   - 用户端与管理端走**两套登录体系**（`LoginType.USER` / `LoginType.ADMIN`），通过 `SaRouter.match` 区分
   - Token 通过 `Authorization` 请求头传递（前端 `Bearer ${token}`）
   - 拦截器在 `config/SaTokenConfig.java` 集中维护，按 URL 模式做登录校验
   - 业务异常（401/403）由 `GlobalExceptionHandler` 统一转 `Result<Void>`

2. **统一响应 — `Result<T>`**
   - 结构：`{ code, message, data }`，封装在 `common/Result.java`
   - 业务码集中在 `common/ResultCode.java`（200/400/401/403/404/500 + 1xxx 业务码）
   - **前端 axios 拦截器在 `code === 200` 时直接返回 `data`**（`utils/request.ts`）— 这是已解包约定，调用方拿到的就是业务数据

3. **MyBatis-Plus 约定**
   - 逻辑删除字段：`deleted`（0 未删 / 1 已删），全局生效
   - 主键自增：`id-type: auto`
   - 实体类继承 `com.baomidou.mybatisplus.annotation.*`，自动填充 `createTime` / `updateTime`（`MybatisPlusMetaObjectHandler`）
   - 复杂 SQL 走 XML：`src/main/resources/com/snack/server/module/<name>/mapper/*.xml`
   - **建表 DDL 与 `entity` 字段必须保持一致**，新增字段要同步两边

4. **Redis 键命名**（`constant/RedisKey.java`）
   - 抢购库存：`seckill:stock:{activityId}:{productId}`
   - 用户抢购记录（限购）：`seckill:user:{userId}:{activityId}:{productId}`
   - 客服在线用户集合：`chat:online:users`
   - 会话未读数：`chat:unread:{sessionId}`
   - 验证码：`captcha:{uuid}`

5. **WebSocket 客服模块**（`websocket/` 当前为占位 `gitkeep`，`BUSINESS_DESIGN.md` 有详细方案）
   - Spring WebSocket + STOMP；握手时验证 JWT
   - 消息全部落库 `chat_message`，离线消息不丢
   - 用户端用 `stomp.js`；管理端会话页在 `views/chat/`

6. **防超卖**（抢购核心）
   - Redis `DECR` 原子扣减库存 → 失败直接返回"已抢完"
   - 写入异步队列 → 消费端落 MySQL（详见 `功能设计文档.md` 3.6.2）

7. **接口规范**
   - 用户端路径：`/api/<资源>`（例：`/api/cart`、`/api/orders/{id}/pay`）
   - 管理端路径：`/api/admin/**`（登录、验证码、文件上传除外）
   - RESTful：列表 `GET`、创建 `POST`、详情 `GET {id}`、删除 `DELETE {id}`

### 前端约定（`admin-snack/`）

1. **路径别名**：`@` → `src/`（Vite + tsconfig 双侧配置）

2. **自动导入**（`unplugin-auto-import` + `unplugin-vue-components`）
   - 自动导入 Vue / Vue Router / Pinia API、Element Plus 组件
   - 类型声明由插件生成到 `src/types/auto-imports.d.ts` 和 `src/types/components.d.ts`
   - 业务文件中**不要**手动 `import { ref, computed } from 'vue'` — 插件已处理

3. **状态管理 — Pinia + 持久化**
   - `stores/modules/user.ts` 持久化 key 为 `snack-admin-user`
   - 用 Composition API 写法（`defineStore('user', () => { ... })`），不是 Options API

4. **请求层**（`utils/request.ts`）
   - axios 实例，baseURL 取 `VITE_API_BASE_URL`（`.env.development` 默认 `http://localhost:8080`）
   - **响应拦截器已解包**：`code === 200` 时直接 `return data`，调用方拿到的就是业务数据
   - 401 自动清空登录态并跳 `/login`
   - 所有 API 文件（`api/*.ts`）用 `request<T>(config)` 二次封装

5. **路由**（`router/`）
   - `index.ts`：基础路由（login、404、仪表盘占位）
   - `async-routes.ts`：业务路由清单（与 `index.ts` 同步维护，权限动态挂载用）
   - 路由守卫在 `src/permission.ts`，白名单 `['/login', '/404']`
   - 默认页签布局：`layouts/BasicLayout.vue`（侧栏 + 顶栏 + TabsNav）

6. **样式**
   - SCSS 全局变量由 `vite.config.ts` 的 `additionalData` 自动注入 `@use "@/styles/variables.scss" as *;`
   - 业务组件中**直接使用变量**即可，不要重新 import
   - 设计令牌：主色 `#1E40AF` / `#3B82F6`，圆角 6/10px，背景 `#F8FAFC`（详见 `admin-snack/README.md`）

7. **Mock**
   - `src/mock/index.ts` 启动时拦截 API 返回本地假数据（基于 `mockjs` + `axios-mock-adapter`）
   - **联调后端时注释掉 `main.ts` 里的 `import './mock'` 即可关闭**

8. **设计系统**：Minimalism · Data-Dense Dashboard，状态色 success `#10B981` / warning `#F59E0B` / danger `#EF4444`，圆角与阴影三档（`admin-snack/README.md` 有完整表）

### 全局约定

- **不要 commit 真实密钥**：`.env.*.local`、`server-snack/application-local.yml`、`*.pem/*.key/*.crt` 已被 `.gitignore` 排除
- **Java 版本**：21（`<java.version>21</java.version>`）
- **Node 版本**：≥ 20
- **包管理器**：管理端 `pnpm-lock.yaml` 已存在
- **Maven 仓库**：内置了华为云、阿里云镜像加速
- **不需要写** README、通用开发规范、显而易见的"添加注释"等指令

## 关键文档

| 文档 | 路径 | 何时读 |
|------|------|--------|
| 功能设计总览 | `功能设计文档.md` | 任何新模块开发前 |
| 业务核心设计（WebSocket + 防超卖） | `db/BUSINESS_DESIGN.md` | 客服/抢购相关开发前 |
| 数据库表结构 | `db/schema.sql` + `db/README.md` | 新增/修改实体前 |
| 管理端技术栈与命令 | `admin-snack/README.md` | 前端环境问题排查 |

## 调试与排错

- **后端启动失败**：先检查 MySQL/Redis 是否启动、端口是否被占用、knife4j 文档能否打开
- **前端 401 风暴**：检查 `VITE_API_BASE_URL`、浏览器是否带了 `Authorization` 头、Sa-Token 拦截器路径配置
- **抢购超卖**：先看 Redis 中 `seckill:stock:*` 键值，再用 `BUSINESS_DESIGN.md` 对照流程
- **类型错误**：运行 `npm run type-check`（管理端），不要绕过 `vue-tsc` 直接 build
- **本地上传文件访问不到**：确认 `WebMvcConfig.addResourceHandlers` 中 `/uploads/**` → `file:./uploads/` 配置，并查看 `snack.upload.domain` 是否正确
