在餐饮管理系统的开发过程中,菜品管理模块的删除功能是一个看似简单但实际需要考虑多种业务场景的核心功能。本文将详细拆解一个支持批量删除的菜品接口实现方案,包含完整的RESTful设计、参数处理、业务校验和数据库操作全流程。
采用DELETE方法作为HTTP请求类型,严格遵循RESTful架构风格的设计原则。与常见的POST方法携带删除参数不同,DELETE方法在语义上更准确地表达了删除操作意图,使API设计更加规范和专业。
接口路径设计为/dish,保持资源导向的命名方式。这种设计使得:
采用Query参数传递要删除的菜品ID列表,参数名为ids,多个ID之间用英文逗号分隔。例如:
code复制DELETE /dish?ids=1,2,3,4
这种设计相比JSON body更符合HTTP规范,具有以下优势:
Spring MVC通过@RequestParam注解自动处理URL参数。当参数定义为List<Long>类型时,框架会智能完成:
List<Long>对象这种处理方式极大简化了开发工作,避免了手动解析字符串的繁琐操作。在实际项目中,建议:
采用行业通用的三层响应结构:
json复制{
"code": 200,
"data": null,
"msg": "删除成功"
}
这种结构化响应具有以下优点:
提示:在实际项目中,建议使用枚举定义标准状态码,避免魔法数字的出现。
java复制@DeleteMapping
public Result<String> delete(@RequestParam List<Long> ids) {
dishService.deleteBatch(ids);
return Result.success();
}
关键设计要点:
@DeleteMapping明确HTTP方法映射List<Long>接收批量IDResult包装对象在实际开发中,Controller还应包含基本的异常捕获:
java复制try {
dishService.deleteBatch(ids);
return Result.success();
} catch (BusinessException e) {
return Result.error(e.getCode(), e.getMessage());
} catch (Exception e) {
log.error("删除菜品异常", e);
return Result.error("系统异常");
}
java复制public interface DishService {
void deleteBatch(List<Long> ids);
}
接口设计原则:
实现类中包含两个核心校验:
校验一:菜品是否在售
java复制LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(Dish::getId, ids).eq(Dish::getStatus, 1);
long count = dishMapper.selectCount(queryWrapper);
if (count > 0) {
throw new BusinessException("存在启售菜品,无法删除");
}
校验二:菜品是否被套餐关联
java复制LambdaQueryWrapper<SetmealDish> setmealDishQueryWrapper = new LambdaQueryWrapper<>();
setmealDishQueryWrapper.in(SetmealDish::getDishId, ids);
long setmealCount = setmealDishMapper.selectCount(setmealDishQueryWrapper);
if (setmealCount > 0) {
throw new BusinessException("菜品已被套餐关联,无法删除");
}
注意:这两个校验顺序不可颠倒,应先检查在售状态再检查关联关系,因为关联检查需要额外查询。
通过事务保证数据一致性:
java复制@Transactional
public void deleteBatch(List<Long> ids) {
// 校验逻辑...
// 删除菜品
dishMapper.deleteBatchIds(ids);
// 删除关联口味
LambdaQueryWrapper<DishFlavor> flavorQueryWrapper = new LambdaQueryWrapper<>();
flavorQueryWrapper.in(DishFlavor::getDishId, ids);
dishFlavorMapper.delete(flavorQueryWrapper);
}
事务处理要点:
@Transactional注解声明事务java复制dishMapper.deleteBatchIds(ids);
底层实际执行的SQL:
sql复制DELETE FROM dish WHERE id IN (?, ?, ?)
java复制LambdaQueryWrapper<DishFlavor> flavorQueryWrapper = new LambdaQueryWrapper<>();
flavorQueryWrapper.in(DishFlavor::getDishId, ids);
dishFlavorMapper.delete(flavorQueryWrapper);
生成的SQL:
sql复制DELETE FROM dish_flavor WHERE dish_id IN (?, ?, ?)
java复制@Select("SELECT COUNT(*) FROM dish WHERE id IN (#{ids}) AND status = 1")
Long countOnSaleByIds(@Param("ids") List<Long> ids);
使用注解SQL提高可读性,IN语句处理集合参数时需要注意:
java复制@Select("SELECT COUNT(*) FROM setmeal_dish WHERE dish_id IN (#{ids})")
Long countRelatedSetmeals(@Param("ids") List<Long> ids);
关联查询优化建议:
问题一:参数格式错误
ids=1,2,a导致400错误问题二:事务超时
问题三:并发删除冲突
软删除实现方案:
java复制@TableLogic
private Integer deleted;
使用@TableLogic注解实现逻辑删除优势:
接口安全增强:
@PreAuthorize)在实际开发中,我通常会为这类关键操作添加详细的日志记录,包括操作人、时间、影响数据量等信息。这既便于问题追踪,也符合审计要求。同时,对于批量操作的规模会做限制(如单次不超过100条),防止误操作导致大规模数据丢失。