9.3 KiB
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/)
# 构建(首次会下载依赖,可能需要较长时间)
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/)
mysql -u root -p < schema.sql # 建库 + 21 张表
mysql -u root -p < seed.sql # 演示数据(管理员 admin/123456,user001~user005/123456)
管理端(admin-snack/)
# 要求 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}。
关键技术点
-
认证 — Sa-Token(注意不是 JWT)
- 用户端与管理端走两套登录体系(
LoginType.USER/LoginType.ADMIN),通过SaRouter.match区分 - Token 通过
Authorization请求头传递(前端Bearer ${token}) - 拦截器在
config/SaTokenConfig.java集中维护,按 URL 模式做登录校验 - 业务异常(401/403)由
GlobalExceptionHandler统一转Result<Void>
- 用户端与管理端走两套登录体系(
-
统一响应 —
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)— 这是已解包约定,调用方拿到的就是业务数据
- 结构:
-
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字段必须保持一致,新增字段要同步两边
- 逻辑删除字段:
-
Redis 键命名(
constant/RedisKey.java)- 抢购库存:
seckill:stock:{activityId}:{productId} - 用户抢购记录(限购):
seckill:user:{userId}:{activityId}:{productId} - 客服在线用户集合:
chat:online:users - 会话未读数:
chat:unread:{sessionId} - 验证码:
captcha:{uuid}
- 抢购库存:
-
WebSocket 客服模块(
websocket/当前为占位gitkeep,BUSINESS_DESIGN.md有详细方案)- Spring WebSocket + STOMP;握手时验证 JWT
- 消息全部落库
chat_message,离线消息不丢 - 用户端用
stomp.js;管理端会话页在views/chat/
-
防超卖(抢购核心)
- Redis
DECR原子扣减库存 → 失败直接返回"已抢完" - 写入异步队列 → 消费端落 MySQL(详见
功能设计文档.md3.6.2)
- Redis
-
接口规范
- 用户端路径:
/api/<资源>(例:/api/cart、/api/orders/{id}/pay) - 管理端路径:
/api/admin/**(登录、验证码、文件上传除外) - RESTful:列表
GET、创建POST、详情GET {id}、删除DELETE {id}
- 用户端路径:
前端约定(admin-snack/)
-
路径别名:
@→src/(Vite + tsconfig 双侧配置) -
自动导入(
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'— 插件已处理
-
状态管理 — Pinia + 持久化
stores/modules/user.ts持久化 key 为snack-admin-user- 用 Composition API 写法(
defineStore('user', () => { ... })),不是 Options API
-
请求层(
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)二次封装
- axios 实例,baseURL 取
-
路由(
router/)index.ts:基础路由(login、404、仪表盘占位)async-routes.ts:业务路由清单(与index.ts同步维护,权限动态挂载用)- 路由守卫在
src/permission.ts,白名单['/login', '/404'] - 默认页签布局:
layouts/BasicLayout.vue(侧栏 + 顶栏 + TabsNav)
-
样式
- SCSS 全局变量由
vite.config.ts的additionalData自动注入@use "@/styles/variables.scss" as *; - 业务组件中直接使用变量即可,不要重新 import
- 设计令牌:主色
#1E40AF/#3B82F6,圆角 6/10px,背景#F8FAFC(详见admin-snack/README.md)
- SCSS 全局变量由
-
Mock
src/mock/index.ts启动时拦截 API 返回本地假数据(基于mockjs+axios-mock-adapter)- 联调后端时注释掉
main.ts里的import './mock'即可关闭
-
设计系统: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是否正确