智慧食堂订餐管理平台

项目背景
暂无项目背景
STAR 过程
情境(Situation):暂无
任务(Task):暂无
行动(Action):暂无
结果(Result):暂无
智慧食堂 — 项目概要文档
1. 项目简介
sky-take-out 是一套基于 Spring Boot 2.7.3 的智慧食堂后端管理系统。系统采用 Maven 多模块聚合工程,提供管理端(Admin)和用户端(C端)两套完整 API,覆盖菜品/套餐管理、购物车与订单、报表统计,并扩展了智慧食堂功能:每日菜单计划、营养追踪、取餐时段预约、团队拼单、周订阅自动下单。
- 技术栈: Java 8+, Spring Boot 2.7.3, MyBatis, MySQL 8.x, Redis 6.x+, RabbitMQ 3.x+, WebSocket, Knife4j
- 端口: 8081
- API 文档:
http://localhost:8081/doc.html
2. 仓库结构
sky-take-out/
├── sky-common/ # 通用模块:常量、异常、工具类、统一响应、配置属性
├── sky-pojo/ # 数据模型:DTO(28)、Entity(18)、VO(25)
├── sky-server/ # 主服务模块:Controller、Service、Mapper、Config、Task、WebSocket
├── sql/ # 数据库脚本 (smart_canteen_ddl.sql)
├── k6_test/ # 压测脚本与结果
└── note/ # 技术文档
3. 技术架构图(文字描述)
┌──────────────────────────────────────────────────────────┐
│ 前端 (管理端 / 微信小程序) │
└──────────┬───────────────────────────────┬───────────────┘
│ /admin/** │ /user/**
▼ ▼
┌─────────────┐ ┌─────────────┐
│ JWT Admin │ │ JWT User │
│ Interceptor │ │ Interceptor │
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────────────────────────────────────┐
│ Controller Layer │
│ (12 Admin Controllers + 13 User Controllers) │
└────────────────────┬────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Service Layer │
│ 15 Interfaces + 15 Impls + Cache/MQ/Stock │
└────────────────────┬────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌────────────┐
│ MyBatis │ │ Redis │ │ RabbitMQ │
│ (MySQL) │ │ (缓存/库存)│ │ (异步下单) │
└──────────┘ └──────────┘ └────────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌────────────┐
│ MySQL │ │ Redis │ │ RabbitMQ │
│ 18 tables│ │ Server │ │ Server │
└──────────┘ └──────────┘ └────────────┘
关键技术组件:
- WebSocket: 新订单/催单实时推送
- Scheduled Tasks: 超时订单取消、每日菜单生成、配送自动完成
- AOP: 自动填充创建/更新时间与操作人
4. 核心业务能力
| 业务域 | 管理端 | 用户端 |
|---|---|---|
| 认证鉴权 | 员工登录/JWT | 微信登录/JWT |
| 分类管理 | CRUD + 起停 | 按类型查询 |
| 菜品管理 | CRUD + 口味 + 起停 | 按分类查询(缓存) |
| 套餐管理 | CRUD + 菜品关联 + 起停 | 按分类查询(缓存) |
| 购物车 | - | 加入/查看/清空 |
| 订单管理 | 确认/拒单/取消/派送/完成/统计 | 提交/支付/催单/历史/取消/再来一单 |
| 报表 | 营业额/用户/订单/Top10 | - |
| 工作台 | 今日数据/概览 | - |
| 文件上传 | 阿里云 OSS | - |
| 店铺状态 | 营业/休息 | 查看状态 |
| 智慧食堂 | ||
| 菜单计划 | CRUD | 今日菜单/按餐段查询 |
| 营养信息 | CRUD | 菜品营养/今日摄入 |
| 取餐时段 | 配置时间段 | 查看可用时段 |
| 拼单 | - | 创建/加入/退出/查邀请码 |
| 周订阅 | - | 订阅/暂停/恢复/取消(自动下单) |
5. 订单链路核心设计
提交 → 幂等(Redis SET NX) → 库存扣减(Redis Lua原子) → 雪花ID → MQ异步入MySQL
- 幂等保护: Redis 键 120 秒防重复
- 库存扣减: Lua 脚本保证原子性,失败自动回滚
- 异步写入: RabbitMQ 削峰,消费者负责写入 orders 和 order_detail
- 支付防重: Redis 分布式锁 + CAS SQL 双重保险
- 分表支持: 按月分表(可配置开关),支持海量订单
详见 详细技术文档。
6. 数据库概览
18 张表,分为两大类:
基础业务(11 张): employee, user, category, dish, dish_flavor, setmeal, setmeal_dish, shopping_cart, orders, order_detail, address_book
智慧食堂新增(7 张): meal_plan, nutrition_info, pickup_slot, group_order, group_order_member, meal_subscription, user_daily_nutrition
详见 数据库文档。
7. API 概览
| 类别 | 路径前缀 | Controller 数 | 接口数 |
|---|---|---|---|
| 管理端 | /admin/ | 12 | ~50 |
| 用户端 | /user/ | 13 | ~45 |
| WebSocket | /ws/{sid} | 1 | 1 |
详见 接口文档。
8. 本地运行
环境要求
- JDK 8+、Maven 3.6+
- MySQL 8.x(数据库
sky_take_out) - Redis 6.x+
- RabbitMQ 3.x+
启动
mvn clean package -DskipTests
mvn -pl sky-server spring-boot:run
访问
- API:
http://localhost:8081 - 文档:
http://localhost:8081/doc.html - WebSocket:
ws://localhost:8081/ws/{sid}
9. 外部依赖
| 服务 | 用途 |
|---|---|
| MySQL 8.x | 主数据库 |
| Redis 6.x+ | 缓存、库存计数、分布式锁、幂等 |
| RabbitMQ 3.x+ | 订单异步创建 |
| 阿里云 OSS | 图片/文件存储 |
| 微信小程序 API | C端用户登录 |
智慧食堂 — 详细技术文档
1. 项目概述
sky-take-out 是一个基于 Spring Boot 2.7.3 的外卖 / 智慧食堂后端系统。采用 Maven 多模块结构,覆盖管理端与用户端的完整业务链路,并在原外卖系统基础上扩展了智慧食堂功能(菜单计划、拼单、周订阅、营养追踪、取餐时段管理)。
2. 项目结构
sky-take-out/
├── pom.xml # 根 POM,聚合模块 + 依赖管理
├── sky-common/ # 通用模块:常量、异常、工具类、Result、配置属性
├── sky-pojo/ # 数据模型:Entity、DTO、VO
├── sky-server/ # 主服务:Controller、Service、Mapper、Config、Task、WebSocket
├── sql/ # 数据库脚本(智慧食堂 DDL)
├── k6_test/ # 压测脚本
└── note/ # 技术文档
2.1 sky-common(通用模块)
| 包路径 | 内容 | 说明 |
|---|---|---|
com.sky.constant | AutoFillConstant, JwtClaimsConstant, MessageConstant, PasswordConstant, StatusConstant | 常量定义 |
com.sky.context | BaseContext | ThreadLocal 存储当前用户 ID |
com.sky.enumeration | OperationType | INSERT / UPDATE |
com.sky.exception | BaseException 及 11 个子类异常 | 业务异常体系 |
com.sky.json | JacksonObjectMapper | 自定义 Jackson 序列化(Java 8 时间支持) |
com.sky.properties | AliOssProperties, JwtProperties, WeChatProperties | 外部配置绑定 |
com.sky.result | Result, PageResult | 统一响应格式 |
com.sky.utils | AliOssUtil, HttpClientUtil, JwtUtil, WeChatPayUtil | 通用工具 |
2.2 sky-pojo(数据模型)
| 包路径 | 数量 | 说明 |
|---|---|---|
com.sky.dto | 28 个 | 数据传输对象,接收前端参数 |
com.sky.entity | 18 个 | 数据库实体映射 |
com.sky.vo | 25 个 | 视图对象,返回给前端 |
2.3 sky-server(主服务)
| 层 | 文件数 | 说明 |
|---|---|---|
| Controller | 25 | 12 个 admin + 13 个 user |
| Service 接口 | 15 | 业务接口 |
| Service 实现 | 15 | 业务实现 |
| Service 组件 | 4 | CacheClient, CacheConsistencyService + 3 个 order 子服务 |
| Mapper | 18 | MyBatis 接口 + 18 个 XML |
| Config | 5 | WebMvc, Redis, OSS, WebSocket, RabbitMQ |
| Interceptor | 2 | JWT 拦截器(admin + user) |
| Aspect | 1 | AutoFillAspect |
| Task/Scheduler | 3 | OrderTask, WebSocketTask, MealPlanScheduler |
| WebSocket | 1 | WebSocketServer |
| Handler | 1 | GlobalExceptionHandler |
3. 认证鉴权机制
3.1 双通道 JWT 认证
系统有两套独立的认证通道:
| 通道 | 请求路径前缀 | Token 头名称 | JWT Claim | 放行接口 |
|---|---|---|---|---|
| Admin | /admin/** | token | EMP_ID | /admin/employee/login |
| User | /user/** | authentication | USER_ID | /user/user/login |
3.2 拦截器配置
WebMvcConfiguration 注册两个拦截器:
JwtTokenAdminInterceptor: 从请求头取token,用 admin 密钥解析 JWT,提取EMP_ID置入BaseContext.setCurrentId(empId)JwtTokenUserInterceptor: 从请求头取authentication,用 user 密钥解析 JWT,提取USER_ID置入BaseContext
解析失败返回 HTTP 401。
3.3 BaseContext — ThreadLocal 上下文
public class BaseContext {
private static final ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id);
public static Long getCurrentId();
public static void removeCurrentId();
}
用于 AOP 自动填充、Service 层获取当前操作用户 ID。
3.4 密码策略
- 管理端默认密码
123456,MD5 加密存储 - 登录时对明文密码 MD5 后与数据库比对
- 员工查询时密码字段脱敏为
"****"
3.5 微信登录流程
- C端传入微信小程序
code - 调用微信 API
https://api.weixin.qq.com/sns/jscode2session获取openid - 查数据库是否存在该
openid,不存在则自动注册 - 生成 JWT(claim:
USER_ID)返回
4. 订单核心链路
订单链路是系统最关键的业务链路,包含以下节点:
4.1 链路总览
用户提交订单 → 幂等检查 → 库存扣减 → 生成订单号 → RabbitMQ 异步入 MySQL
↓
OrderCreateConsumer
→ 写入 orders / order_detail
→ 失败回滚库存
4.2 详细步骤(OrderServiceImpl.submit)
步骤 1: 校验
- 校验购物车非空
- 校验地址存在
- 智慧食堂模式:若请求含
mealPeriod和pickupSlotId,尝试预订取餐时段
步骤 2: 幂等检查
- 生成幂等键:
order:submit:idem:{userId}:{MD5(DTO JSON)}或使用客户端提供的Idempotency-Key头 SET NX到 Redis,TTL 120 秒- 若 set 失败(键已存在),检查是否有缓存结果返回;若都无,抛
OrderBusinessException("请勿重复提交")
步骤 3: 库存扣减
- 遍历购物车,对每项构建 stock key:
stock:dish:{dishId}或stock:setmeal:{setmealId} - 调用
RedisLuaStockService.tryDeduct(key, quantity)原子扣减 - 任一扣减失败,回滚所有已扣项,抛异常
步骤 4: 生成订单号
SnowflakeIdWorker.nextId()生成全局唯一订单号- 参数: epoch 2023-01-01, machineId=1, 10 位机器 ID + 12 位序列号
步骤 5: 异步入 MySQL
- 构造
OrderCreateEvent(含订单号、DTO、地址、购物车数据、扣减记录、分表表名) - 通过
OrderEventProducer发送到 RabbitMQ 交换机order.event.exchange,routing keyorder.create
步骤 6: 返回结果
- 将
OrderSubmitVO缓存到 Redis(key=幂等键,TTL 10 分钟) - 返回给前端
4.3 异步消费者(OrderCreateConsumer)
@RabbitListener(queues = "order.create.queue"):
- 幂等检查: 查分表内是否已有该订单号(
getByNumberFromTable),有则跳过 - 写入 orders 表:
insertToTable到路由后的分表 - 写入 order_detail 表:
insertBatchToTable批量插入 - 失败处理: catch 异常后调用
StockService.rollback()回滚库存 - 自动确认: 默认
autoDelete模式
4.4 订单状态机
PENDING_PAYMENT (1)
│
├─ paySuccess() → TO_BE_CONFIRMED (2)
│ │
│ ├─ confirm() → CONFIRMED (3)
│ │ │
│ │ ├─ delivery() → DELIVERY_IN_PROGRESS (4)
│ │ │ │
│ │ │ ├─ complete() → COMPLETED (5)
│ │ │ │
│ │ │ └─ 1AM 定时任务: super(old>60min) → COMPLETED (5)
│ │ │
│ │ └─ pickup() (智慧食堂) → COMPLETED (5)
│ │
│ └─ rejection() → CANCELLED (6) [已支付则退款]
│
├─ userCancel() → CANCELLED (6)
├─ adminCancel() → CANCELLED (6)
│
└─ 定时任务: 超时 15 分钟未支付 → CANCELLED (6)
4.5 支付防重
支付成功回调 paySuccess(outTradeNo):
- Redis 分布式锁:
order:pay:success:{outTradeNo},TTL 24 小时 - CAS 更新 SQL:
UPDATE orders SET status=TO_BE_CONFIRMED, pay_status=PAID WHERE id=? AND status=PENDING_PAYMENT AND pay_status=UNPAID - 成功后推送 WebSocket 新订单通知(type=1)
5. 缓存架构
5.1 CacheClient — 缓存读取组件
实现 read-through 缓存模式,解决三大缓存问题:
防击穿(热点数据过期瞬间大量请求打 DB):
- Redis 互斥锁:
SET NX lock:key value EX 10s - 获得锁的线程查 DB 并回写缓存
- 未获得锁的线程定时间隔重试(最多 60 次,每次 50ms)
- 解锁使用 Lua 脚本
if get(key)==token then del(key)防止误删
防穿透(查询不存在的数据):
- 查询结果为 null 时,写
__NULL__标记到缓存,TTL 3 分钟
防雪崩(大量缓存同时过期):
- 每个缓存 TTL 加随机抖动:
defaultTtlMinutes + random(0, ttlJitterMinutes) - 默认 30 分钟 + 随机 0~10 分钟
配置项 (sky.cache.*):
| 配置 | 默认值 | 说明 |
|---|---|---|
default-ttl-minutes | 30 | 默认缓存 TTL |
ttl-jitter-minutes | 10 | TTL 随机抖动范围 |
null-ttl-minutes | 3 | 空值缓存 TTL |
lock-ttl-seconds | 10 | 互斥锁 TTL |
lock-sleep-millis | 50 | 重试间隔 |
lock-retry-times | 60 | 最大重试次数 |
5.2 CacheConsistencyService — 缓存一致性
采用 双删策略(double-delete):
- 立即删除: 更新数据库后立即删除缓存
- 延迟删除: 500ms 后再次删除(防止并发读导致脏数据被回写)
支持:
doubleDelete(String key): 精确删除单个缓存键doubleDeleteByPattern(String pattern): 模式匹配批量删除
使用 ThreadPoolTaskScheduler(pool size=2)执行延迟删除。
5.3 缓存应用位置
| 缓存键 | 使用位置 | 缓存方式 |
|---|---|---|
dish_{categoryId} | User DishController | CacheClient.queryWithMutex |
setmeal_{categoryId} | User SetmealController | CacheClient.queryWithMutex |
meal_plan_list:{date} | User MealPlanController | CacheClient + MealPlanScheduler 预热 |
meal_plan:{date}:{period} | User MealPlanController | CacheClient |
5.4 缓存失效触发
| 操作 | 触发的缓存清除 |
|---|---|
| 菜品新增/更新/删除/状态变更 | doubleDelete("dish_" + categoryId) + doubleDeleteByPattern("dish_*") |
| 套餐新增/更新/删除/状态变更 | doubleDeleteByPattern("setmeal_*") |
6. 消息队列设计
6.1 RabbitMQ 拓扑
order.event.exchange (Direct, durable)
│
▼
order.create.queue (durable)
│
▼
OrderCreateConsumer (@RabbitListener)
| 元素 | 名称 | 说明 |
|---|---|---|
| Exchange | order.event.exchange | 直连交换机,持久化 |
| Queue | order.create.queue | 持久化队列 |
| Routing Key | order.create | 绑定路由键 |
6.2 消息序列化
RabbitOrderConfig 配置 Jackson2JsonMessageConverter,集成 JavaTimeModule 以支持 LocalDateTime 等 Java 8 时间类型的序列化与反序列化。
6.3 OrderCreateEvent
事件对象在 com.sky.dto.OrderCreateEvent,包含:
- 订单号、DTO、地址
- 购物车数据
- 已扣减的库存清单
- 目标分表表名
6.4 消费策略
- 消费者确认模式: 自动确认(默认)
- 失败策略: 捕获异常后,主动调用
StockService.rollback()回滚 Redis 库存 - 消息顺序: 不保证严格顺序(订单号通过幂等检查去重)
7. 库存扣减
7.1 RedisLuaStockService
使用 Redis Lua 脚本实现原子性库存检查与扣减:
local stock = redis.call('get', KEYS[1]);
if (not stock) then return -1 end; -- 库存键不存在
stock = tonumber(stock);
local qty = tonumber(ARGV[1]);
if (stock < qty) then return 0 end; -- 库存不足
redis.call('decrby', KEYS[1], qty);
return 1; -- 扣减成功
返回值:
1: 扣减成功0: 库存不足-1: 库存键不存在
7.2 StockService 接口
| 方法 | 说明 |
|---|---|
tryDeduct(key, qty) | 原子扣减,返回是否成功 |
rollback(List<StockDeductItem>) | 批量回滚(INCRBY 返还已扣数量) |
increase(key, qty) | 增加库存 |
exists(key) | 检查库存键是否存在 |
buildStockKey(cart) | 从购物车项构建 stock key |
7.3 库存键设计
- 菜品:
stock:dish:{dishId} - 套餐:
stock:setmeal:{setmealId}
库存数值需由管理系统预先设定。
8. 订单分表策略
8.1 OrderShardRouter
| 配置 | 默认值 | 说明 |
|---|---|---|
sky.sharding.enabled | false | 是否启用分表 |
sky.sharding.scan-months | 2 | 定时任务扫描的月份数 |
分表命名规则: orders_YYYYMM / order_detail_YYYYMM
路由逻辑:
- 未启用分表: 所有操作指向
orders/order_detail - 启用分表: 按订单时间路由到对应月份的分表
定时任务扫描: candidateOrdersTables(LocalDateTime.now()) 返回当前及最近 N 个月的 orders 分表名列表,用于超时订单扫描。
8.2 动态表名
Mapper XML 使用 ${ordersTable} / ${orderDetailTable} 动态表名(非预编译),传入参数时以 Map 形式传递:
Map<String, Object> params = new HashMap<>();
params.put("ordersTable", router.ordersTable(time));
params.put("order", orderEntity);
9. 雪花 ID 生成
9.1 SnowflakeIdWorker
| 参数 | 值 |
|---|---|
| 起始时间戳 (epoch) | 2023-01-01 00:00:00 (1672502400000) |
| 机器 ID 位数 | 10 bit(最多 1024 台机器) |
| 序列号位数 | 12 bit(每毫秒 4096 个ID) |
| 当前机器 ID | 1 |
时钟回拨处理: spin-wait 直到时间追上上次时间戳。
10. 取餐时段预订
10.1 容量控制
PickupSlotServiceImpl.tryBook(slotId):
Redis 侧(核心,Lua 脚本):
-- 原子检查容量并 +1
local booked = redis.call('get', 'pickup:slot:'..slotId..':booked');
if booked and tonumber(booked) >= maxCapacity then return 0 end;
redis.call('incr', 'pickup:slot:'..slotId..':booked');
return 1;
MySQL 侧(备份):
同时更新 pickup_slot.booked_count 字段作为数据兜底。
10.2 释放
release(slotId): Redis DECR + MySQLbooked_count - 1
11. 拼单系统
11.1 状态流转
RECRUITING (1) ── timeout ──> members>=2: CLOSED_PENDING (2) ──> ORDERED (3)
│ members<2: CANCELLED (4)
│
└── leader 取消 ──> CANCELLED (4)
11.2 核心逻辑
创建: 生成 6 位邀请码(UUID 前 6 位),存储到 Redis group_order:lock:{inviteCode}
加入: 原子增加 current_members(incrementMembers mapper 用 SET current_members = current_members + 1),检查:
- 拼单状态为 RECRUITING
- 当前成员数 < 最大成员数
- 未超过截止时间
定时处理 (processDeadline()):
- 每 30 秒扫描已截止的拼单
- 成员 >= 2 → 状态变更为 CLOSED_PENDING → 自动为每个成员提交订单 → ORDERED
- 成员 < 2 → CANCELLED
12. 周订阅系统
12.1 SubscriptionServiceImpl
定时任务 (processDailyAutoOrder()): 每天早 7:00 执行
- 查询所有 ACTIVE 状态、当前日期在工作日范围内的订阅
- 对每个订阅,根据订阅的餐段,查找当天的 meal_plan
- 为每个餐段自动生成订单
- 使用幂等键
sub:{subscriptionId}:{date}:{period}防止重复下单
状态: ACTIVE → PAUSED(暂停) / ACTIVE → CANCELLED(取消) / PAUSED → ACTIVE(恢复)
13. 定时任务
13.1 OrderTask
| 任务 | Cron | 功能 |
|---|---|---|
deleteOrder() | 0 * * * * ? (每分钟) | 扫描所有候选分表,取消超过 15 分钟未支付订单 |
completeOrder() | 0 0 1 * * ? (每天 1:00 AM) | 自动完成超过 60 分钟的配送中订单 |
13.2 MealPlanScheduler
| 任务 | Cron | 功能 |
|---|---|---|
| 生成每日菜单 | 0 0 2 * * ? (每天 2:00 AM) | 为当天自动创建三餐菜单计划(早餐:4菜+2套餐, 午餐:8菜+4套餐, 晚餐:6菜+3套餐),缓存到 Redis TTL 26 小时 |
13.3 WebSocketTask
| 任务 | Cron | 功能 |
|---|---|---|
| 心跳推送 | 0/5 * * * * ? (每 5 秒) | 向所有连接客户端广播服务器时间 |
14. WebSocket 实时推送
14.1 服务端
WebSocketServer 使用 JSR 356 @ServerEndpoint("/ws/{sid}") 注解:
sessionMap:ConcurrentHashMap<String, Session>管理连接sendToAllClient(message): 遍历所有 session 发送消息
14.2 消息类型
| type | 事件 | 触发位置 |
|---|---|---|
| 1 | 新订单通知 | OrderServiceImpl.paySuccess() |
| 2 | 催单通知 | OrderServiceImpl.remind() |
14.3 配置
WebSocketConfiguration 注册 ServerEndpointExporter bean 以启用 @ServerEndpoint 注解。
15. AOP 自动填充
15.1 @AutoFill 注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
OperationType value();
}
15.2 AutoFillAspect
切点: execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)
逻辑:
- 获取被注解方法的第一个参数(实体对象)
- INSERT: 通过反射调用
setCreateTime(now),setUpdateTime(now),setCreateUser(currentId),setUpdateUser(currentId) - UPDATE: 通过反射调用
setUpdateTime(now),setUpdateUser(currentId)
currentId从BaseContext.getCurrentId()获取(ThreadLocal)。
16. 全局异常处理
16.1 GlobalExceptionHandler
@RestControllerAdvice:
BaseException(及其子类): 返回Result.error(ex.getMessage())SQLIntegrityConstraintViolationException: 解析重复键异常,提取字段名,返回友好提示"duplicateField" + USER_EXIST
17. 配置详解
17.1 application.yml 核心配置
server:
port: 8081
spring:
profiles:
active: dev
datasource:
druid: ... # 数据源配置(dev 中明文)
redis: ... # Redis 连接(dev 中明文)
rabbitmq: ... # RabbitMQ 连接(localhost:5672, guest/guest)
main:
allow-circular-references: true # 允许循环依赖
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.sky.entity
configuration:
map-underscore-to-camel-case: true
sky:
cache:
default-ttl-minutes: 30
ttl-jitter-minutes: 10
null-ttl-minutes: 3
lock-ttl-seconds: 10
lock-sleep-millis: 50
lock-retry-times: 60
jwt:
admin-secret-key: itcast
user-secret-key: itcast
admin-token-name: token
user-token-name: authentication
admin-ttl: 720000000 # 约 8.3 天
user-ttl: 720000000
sharding:
enabled: false
scan-months: 2
17.2 安全提醒
application-dev.yml 包含明文密码(MySQL: root/123456, Redis: 123456, WeChat appid, OSS access key),不建议直接用于生产环境。
18. 关键设计决策与权衡
| 决策 | 选择 | 原因 |
|---|---|---|
| 订单创建 | RabbitMQ 异步 + 立即返回 | 提升用户响应速度,削峰填谷 |
| 库存扣减 | Redis Lua 原子操作 | 保证高并发下库存一致性 |
| 缓存更新 | Cache Aside + 双删 | 保证最终一致性,避免缓存脏数据 |
| 幂等保护 | Redis SET NX + 业务键 | 简单可靠的防重方案 |
| 分表策略 | 按月分表(可开关) | 适应订单数据增长,开发/测试阶段可关闭 |
| 支付防重 | Redis 锁 + CAS SQL | 双保险,防止重复处理支付回调 |
| 取餐容量 | Redis Lua(主) + MySQL(备) | Redis 保障并发性能,MySQL 保证数据持久 |
| JWT 密钥 | itcast(固定明文) | 开发/学习环境,生产环境应使用配置中心 |
| 密码加密 | MD5 | 开发/学习环境,生产环境应使用 BCrypt/Argon2 |
| 雪花 ID epoch | 2023-01-01 | 基于项目创建时间,缩小 ID 长度 |
19. 外部依赖
| 服务 | 用途 | 配置位置 |
|---|---|---|
| MySQL 8.x | 主数据库 | application-dev.yml |
| Redis 6.x+ | 缓存/库存/锁/幂等 | application-dev.yml |
| RabbitMQ 3.x+ | 订单异步创建 | application.yml (localhost:5672) |
| 阿里云 OSS | 图片文件存储 | application-dev.yml |
| 微信小程序 API | 用户登录 | WeChatProperties |
20. 启动指南
环境要求
- JDK 8+
- Maven 3.6+
- MySQL 8.x(需预先创建
sky_take_out数据库并执行 DDL) - Redis 6.x+
- RabbitMQ 3.x+
启动步骤
# 1. 编译
mvn clean package -DskipTests
# 2. 启动 sky-server 模块
mvn -pl sky-server spring-boot:run
# 3. 或者直接运行 jar
java -jar sky-server/target/sky-server-1.0-SNAPSHOT.jar
可访问地址
- API 服务:
http://localhost:8081 - Knife4j 文档:
http://localhost:8081/doc.html - WebSocket:
ws://localhost:8081/ws/{sid}