191 lines
9.3 KiB
Markdown
191 lines
9.3 KiB
Markdown
# CLAUDE.md
|
||
|
||
This file provides guidance to Claude Code (claude.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` 是否正确
|