Vue + SpringBoot 餐饮点餐小程序完整源码包解析:后台 RBAC 权限模型与小程序端通信
在数字化转型浪潮下,餐饮行业的线上化运营已从“可选项”变为“必选项”。本文将以一套成熟的“Vue + SpringBoot 餐饮点餐小程序完整源码包”为切入点,深度解析后台基于RBAC的权限模型设计,以及小程序端与后端服务的高效通信机制,为开发者提供从架构设计到代码落地的全链路参考。
项目整体架构与技术选型
架构概览
源码与演示:c.ymzan.top
该点餐小程序采用“前后端分离”的经典架构,整体分为三层:前端展示层(微信小程序)、后端服务层(SpringBoot微服务)、数据存储层(MySQL + Redis)。各层通过标准化接口通信,职责边界清晰,便于独立迭代与扩展。
- 前端:基于微信小程序原生框架开发,配合WeUI组件库实现统一UI风格,核心模块包括菜单浏览、购物车、订单提交、支付回调、个人中心等。
- 后端:采用SpringBoot 2.7.x构建RESTful API服务,集成MyBatis-Plus简化数据访问,Shiro/Spring Security实现权限控制,Redis缓存热点数据(如菜品信息、用户会话),RabbitMQ处理异步任务(如订单超时取消)。
- 数据库:MySQL 8.0存储结构化数据,核心表包括用户表(
t_user)、角色表(t_role)、权限表(t_permission)、菜品表(t_dish)、订单表(t_order)等;Redis用于缓存用户Token、临时订单数据,提升系统响应速度。
技术选型依据
- Vue生态:虽小程序前端未直接使用Vue.js,但其组件化思想与数据绑定机制与Vue高度相似,且配套的管理后台(商家端)基于Vue3 + Element Plus开发,实现“小程序-管理后台”技术栈统一,降低维护成本。
- SpringBoot:约定大于配置的特性大幅简化后端搭建流程,内置Tomcat容器、自动配置机制与Starter依赖管理,适合快速迭代餐饮业务场景。
- RBAC权限模型:餐饮场景涉及多角色协作(如顾客、商家管理员、后厨、服务员),RBAC通过“用户-角色-权限”三层映射,灵活支持权限动态分配,避免过度授权风险。
后台RBAC权限模型深度解析
RBAC核心设计思想
RBAC模型的核心是“权限与角色关联,用户通过角色间接获取权限”,相比直接给用户分配权限的ACL(Access Control List)模型,RBAC更适用于多角色、权限复杂的系统。本项目中,RBAC模型包含五个核心要素:用户(User)、角色(Role)、权限(Permission)、用户角色关联(User-Role)、角色权限关联(Role-Permission)。
数据库表结构设计
为实现RBAC,后台设计了5张核心表,结构如下:
(1)用户表(t_user)
存储系统用户信息,包括小程序端顾客与后台管理用户。
CREATE TABLE `t_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(50) NOT NULL COMMENT '用户名(小程序openid/后台登录名)',
`password` varchar(100) DEFAULT NULL COMMENT '密码(后台用户加密存储)',
`nick_name` varchar(50) DEFAULT NULL COMMENT '昵称',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB COMMENT='用户表';
(2)角色表(t_role)
定义系统角色,如“顾客”“商家管理员”“后厨人员”“服务员”。
CREATE TABLE `t_role` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(50) NOT NULL COMMENT '角色名称',
`role_code` varchar(50) NOT NULL COMMENT '角色编码(如ROLE_CUSTOMER、ROLE_ADMIN)',
`description` varchar(200) DEFAULT NULL COMMENT '角色描述',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_role_code` (`role_code`)
) ENGINE=InnoDB COMMENT='角色表';
(3)权限表(t_permission)
定义系统操作权限,粒度细化到“接口级”,如“查询菜品”“修改订单状态”“导出销售报表”。
CREATE TABLE `t_permission` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '权限ID',
`perm_name` varchar(100) NOT NULL COMMENT '权限名称',
`perm_code` varchar(100) NOT NULL COMMENT '权限编码(如dish:query、order:update)',
`url` varchar(200) DEFAULT NULL COMMENT '对应接口URL(如/api/dish/list)',
`method` varchar(10) DEFAULT NULL COMMENT '请求方法(GET/POST/PUT/DELETE)',
`description` varchar(200) DEFAULT NULL COMMENT '权限描述',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_perm_code` (`perm_code`)
) ENGINE=InnoDB COMMENT='权限表';
(4)用户角色关联表(t_user_role)
实现用户与角色的多对多关联(一个用户可拥有多个角色,一个角色可分配给多个用户)。
CREATE TABLE `t_user_role` (
`user_id` bigint NOT NULL COMMENT '用户ID',
`role_id` bigint NOT NULL COMMENT '角色ID',
PRIMARY KEY (`user_id`, `role_id`),
CONSTRAINT `fk_user_role_user` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_user_role_role` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='用户角色关联表';
(5)角色权限关联表(t_role_permission)
实现角色与权限的多对多关联(一个角色可拥有多个权限,一个权限可分配给多个角色)。
CREATE TABLE `t_role_permission` (
`role_id` bigint NOT NULL COMMENT '角色ID',
`permission_id` bigint NOT NULL COMMENT '权限ID',
PRIMARY KEY (`role_id`, `permission_id`),
CONSTRAINT `fk_role_perm_role` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_role_perm_perm` FOREIGN KEY (`permission_id`) REFERENCES `t_permission` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='角色权限关联表';
权限校验流程实现
后台基于Spring Security实现RBAC权限校验,核心流程如下:
(1)用户认证(登录)
小程序端用户通过微信授权登录,后端调用微信接口获取openid,作为用户唯一标识;管理后台用户通过账号密码登录。登录成功后,生成JWT(JSON Web Token)并返回给前端,Token中包含用户ID、角色信息。
// 登录接口示例(小程序端)
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserService userService;
@PostMapping("/wx-login")
public Result wxLogin(@RequestBody WxLoginDTO dto) {
// 1. 调用微信接口获取openid
String openid = WxApiUtil.getOpenid(dto.getCode());
// 2. 查询用户是否存在,不存在则创建
User user = userService.getByUsername(openid);
if (user == null) {
user = userService.registerWxUser(openid);
}
// 3. 生成Token(包含用户ID、角色列表)
List<String> roles = userService.getUserRoles(user.getId());
String token = jwtTokenUtil.generateToken(user.getId().toString(), roles);
return Result.success(new LoginVO(token, user.getNickName()));
}
}
(2)权限拦截(过滤器链)
Spring Security通过过滤器链实现请求拦截,核心过滤器JwtAuthenticationFilter负责解析Token、加载用户权限,并将权限信息存入SecurityContextHolder。
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
// 1. 从请求头获取Token
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
// 2. 验证Token有效性
if (jwtTokenUtil.validateToken(token)) {
// 3. 解析用户ID与角色
String userId = jwtTokenUtil.getUserIdFromToken(token);
List<String> roles = jwtTokenUtil.getRolesFromToken(token);
// 4. 构建认证对象
UserDetails userDetails = userDetailsService.loadUserByUsername(userId);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}
(3)方法级权限控制
通过@PreAuthorize注解实现接口级别的权限校验,例如“修改菜品”接口仅允许拥有dish:update权限的角色访问。
@RestController
@RequestMapping("/api/dish")
public class DishController {
@Autowired
private DishService dishService;
// 仅允许拥有dish:update权限的用户访问
@PutMapping("/{id}")
@PreAuthorize("hasAuthority('dish:update')")
public Result updateDish(@PathVariable Long id, @RequestBody DishUpdateDTO dto) {
dishService.updateById(id, dto);
return Result.success();
}
}
权限动态管理
后台管理端提供“角色管理”“权限分配”功能,支持管理员动态调整角色权限。例如,新增“服务员”角色时,可勾选“查看订单”“打印小票”等权限,系统自动更新t_role_permission表,无需重启服务即可生效。
小程序端与后端的通信机制
通信协议与数据格式
小程序端与后端采用HTTPS协议通信,确保数据传输安全。接口数据格式统一为JSON,请求与响应遵循以下规范:
- 请求头:
Content-Type: application/jsonAuthorization: Bearer {token}(携带JWT令牌,未登录接口除外)
- 响应格式:
{ "code": 200, // 状态码:200成功,400参数错误,401未授权,403无权限,500系统错误 "message": "success", "data": {} // 业务数据 }
核心通信流程
(1)登录认证流程
小程序登录采用“微信授权+后端Token验证”模式,流程如下:
- 小程序调用
wx.login()获取临时code; - 将
code发送至后端/api/auth/wx-login接口; - 后端调用微信服务器接口,用
code换取openid和session_key; - 后端根据
openid查询用户,不存在则创建新用户; - 生成JWT Token返回给小程序,小程序存储Token(本地缓存);
- 后续请求在Header中携带Token,后端验证通过后放行。
(2)菜品列表加载流程
用户进入点餐页面时,小程序请求菜品列表,流程如下:
- 小程序
onLoad生命周期调用getDishList()方法; - 检查本地是否有缓存的菜品数据(Redis缓存,有效期5分钟),有则直接使用;
- 无缓存则发起HTTP请求:
GET /api/dish/list?categoryId=1; - 后端Controller接收请求,调用Service层查询数据库(优先查Redis缓存);
- 返回菜品列表数据,小程序渲染页面并更新本地缓存。
// 小程序端获取菜品列表示例
async getDishList(categoryId) {
const cacheKey = `dish_list_${categoryId}`;
let dishList = wx.getStorageSync(cacheKey);
if (!dishList) {
try {
const res = await wx.request({
url: `${baseUrl}/api/dish/list`,
method: 'GET',
data: { categoryId },
header: { 'Authorization': `Bearer ${getToken()}` }
});
if (res.data.code === 200) {
dishList = res.data.data;
wx.setStorageSync(cacheKey, dishList); // 缓存数据
}
} catch (err) {
console.error('获取菜品失败', err);
}
}
this.setData({ dishList });
}
(3)订单提交流程
用户下单时,小程序需完成“购物车数据提交→库存校验→订单创建→支付唤起”全流程,核心步骤如下:
- 小程序收集购物车数据(菜品ID、数量、规格),组装为
OrderCreateDTO; - 发起
POST /api/order/create请求,携带Token; - 后端校验库存(Redis原子递减,防止超卖);
- 生成订单号,保存订单数据至MySQL,订单状态设为“待支付”;
- 调用微信支付统一下单接口,获取
prepay_id; - 后端返回支付参数(时间戳、随机串、签名)给小程序;
- 小程序调用
wx.requestPayment()唤起支付界面; - 支付完成后,微信服务器通过回调接口通知后端更新订单状态。
异常处理与重试机制
小程序端需处理网络异常、接口超时等问题,常见策略包括:
- 网络错误提示:请求失败时显示“网络连接失败,请重试”;
- 接口重试:对非幂等性接口(如查询)实现自动重试(最多2次),幂等性接口(如支付)需避免重复提交;
- Token过期处理:拦截401响应,自动刷新Token(通过
refresh_token),刷新失败则跳转登录页。
// 封装请求函数,统一处理异常
const request = (options) => {
return new Promise((resolve, reject) => {
wx.request({
...options,
success: (res) => {
if (res.statusCode === 200) {
if (res.data.code === 200) {
resolve(res.data);
} else if (res.data.code === 401) {
// Token过期,尝试刷新
refreshToken().then(() => {
options.header['Authorization'] = `Bearer ${getToken()}`;
request(options).then(resolve).catch(reject);
}).catch(() => {
wx.navigateTo({ url: '/pages/login/index' });
});
} else {
wx.showToast({ title: res.data.message, icon: 'none' });
reject(res.data);
}
} else {
wx.showToast({ title: '网络错误', icon: 'none' });
reject(new Error('Network Error'));
}
},
fail: (err) => {
wx.showToast({ title: '请求失败', icon: 'none' });
reject(err);
}
});
});
};
性能优化策略
为提升通信效率,项目采用以下优化手段:
- 接口合并:将“菜品分类”“热门菜品”“推荐菜品”合并为一个接口返回,减少请求次数;
- 数据压缩:后端开启GZIP压缩,减小传输体积;
- 缓存策略:小程序端缓存静态数据(如菜品分类),后端缓存热点数据(如销量Top10菜品);
- 分页加载:订单列表、评价列表采用分页查询,默认每页10条数据。
源码包结构与关键代码解析
后端源码结构
src/main/java/com/example/diancan/
├── config/ # 配置类(SecurityConfig、RedisConfig、SwaggerConfig等)
├── controller/ # 控制器(AuthController、DishController、OrderController等)
├── service/ # 业务逻辑层
│ ├── impl/ # 服务实现类
├── mapper/ # MyBatis Mapper接口
├── entity/ # 实体类(User、Role、Permission、Dish、Order等)
├── dto/ # 数据传输对象(LoginDTO、OrderCreateDTO等)
├── vo/ # 视图对象(LoginVO、DishVO等)
├── security/ # 安全相关(JwtTokenUtil、JwtAuthenticationFilter等)
├── exception/ # 自定义异常(BusinessException、GlobalExceptionHandler)
└── util/ # 工具类(DateUtil、RedisUtil、WxApiUtil等)
前端(小程序)源码结构
miniprogram/
├── pages/ # 页面目录
│ ├── index/ # 首页(菜品浏览)
│ ├── cart/ # 购物车
│ ├── order/ # 订单相关
│ ├── user/ # 个人中心
│ └── login/ # 登录页
├── components/ # 自定义组件(菜品卡片、购物车弹窗等)
├── utils/ # 工具类(request.js、auth.js、cache.js)
├── api/ # 接口封装(dishApi.js、orderApi.js、authApi.js)
├── app.js # 入口文件
├── app.json # 全局配置
└── app.wxss # 全局样式
关键代码片段解析
(1)RBAC权限校验核心工具类
// JwtTokenUtil.java(Token生成与解析)
@Component
public class JwtTokenUtil implements Serializable {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
// 生成Token
public String generateToken(String userId, List<String> roles) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", roles);
return Jwts.builder()
.setClaims(claims)
.setSubject(userId)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
// 从Token中获取角色
public List<String> getRolesFromToken(String token) {
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
return (List<String>) claims.get("roles");
}
}
(2)订单创建接口(含事务与库存校验)
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private DishMapper dishMapper;
@Autowired
private RedisTemplate<String, Integer> redisTemplate;
@Transactional(rollbackFor = Exception.class)
@Override
public Order createOrder(OrderCreateDTO dto, Long userId) {
// 1. 校验库存(Redis原子操作)
for (OrderItemDTO item : dto.getItems()) {
String stockKey = "dish:stock:" + item.getDishId();
Long remain = redisTemplate.opsForValue().decrement(stockKey, item.getQuantity());
if (remain < 0) {
// 库存不足,回滚Redis
redisTemplate.opsForValue().increment(stockKey, item.getQuantity());
throw new BusinessException("菜品【" + item.getDishName() + "】库存不足");
}
}
// 2. 创建订单
Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setUserId(userId);
order.setTotalAmount(calculateTotal(dto.getItems()));
order.setStatus(OrderStatus.PENDING_PAYMENT.getCode());
orderMapper.insert(order);
// 3. 保存订单项
saveOrderItems(order.getId(), dto.getItems());
// 4. 发送延迟消息(30分钟未支付自动取消)
rabbitTemplate.convertAndSend("order.delay.exchange", "order.cancel", order.getId(),
message -> {
message.getMessageProperties().setDelay(30 * 60 * 1000);
return message;
});
return order;
}
}

总结与建议
核心技术点回顾
本文解析的餐饮点餐小程序源码包,通过**Vue(管理后台)+ 微信小程序(用户端)+ SpringBoot(后端)**的技术组合,实现了高效的前后端分离架构。其中,后台RBAC权限模型通过“用户-角色-权限”三层映射,灵活支撑多角色协作场景;小程序端与后端的通信则通过HTTPS+JWT实现安全认证,结合缓存、接口合并等策略保障性能。
实践建议
- 权限粒度控制:避免过度设计权限,初期可按“模块+操作”划分(如
dish:query),后期根据业务需求细化; - Token安全:JWT密钥定期轮换,设置合理过期时间(小程序端Token建议2小时,
refresh_token建议7天); - 接口文档:使用Swagger/Knife4j生成在线接口文档,方便前后端联调。
通过本文的解析,开发者可深入理解餐饮点餐小程序的核心技术实现,快速复用源码包中的RBAC权限模型与通信机制,加速同类项目的落地。在实际开发中,还需结合业务场景灵活调整,持续优化系统性能与用户体验。
更多推荐
所有评论(0)