

首先,我们要分析业务需求,菜品名称,价格,分类,描述,图片,以及库存数量,口味选项,以及上架状态。 然后,我们分析功能需求,
操作 | 方法 | 说明 |
|---|---|---|
新增 | POST | 向服务器提交数据 |
查询 | GET | 获取数据 |
修改 | PUT/PATCH | 更新数据 |
删除 | DELETE | 删除数据 |
结论:新增菜品 → POST
// RESTful 风格 POST /api/dishes // 新增菜品 POST /api/category/{id}/dishes // 向指定分类添加菜品 // 实际选择 POST /api/admin/dishes // 后台管理接口// 定义请求对象
@Data
public class DishAddRequest {
@NotBlank(message = "菜品名称不能为空")
private String name;
@NotNull(message = "价格不能为空")
@DecimalMin(value = "0.01", message = "价格必须大于0")
private BigDecimal price;
@NotNull(message = "分类不能为空")
private Long categoryId;
private String description;
private List<String> imageUrls; // 图片URL列表
@NotNull(message = "库存不能为空")
@Min(value = 0, message = "库存不能小于0")
private Integer stock;
private Boolean status = true; // 默认上架
private List<DishFlavor> flavors; // 口味选项// Controller
@PostMapping("/dishes")
public Result<Long> addDish(@RequestBody @Valid DishAddRequest request) {
Long dishId = dishService.addDish(request);
return Result.success(dishId);
}适用:纯数据提交,图片可先上传返回URL
也就是定义一个Result类
维度 | 检查项 | 是否完成 |
|---|---|---|
功能 | 是否满足业务需求 | ☐ |
参数 | 参数是否完整、合理 | ☐ |
校验 | 是否有参数校验和业务校验 | ☐ |
安全 | 是否有权限控制 | ☐ |
幂等 | 重复提交是否有防护 | ☐ |
返回 | 返回格式是否统一 | ☐ |
文档 | 是否有API文档 | ☐ |
测试 | 是否有单元测试 | ☐ |
文件上传接口的开发: 前端通过浏览器进行图片上传,之后把图片传到后端,后端不是把图片上传到后端服务器,而是将图片上传到阿里云对象存储服务OSS,推荐使用阿里云OSS,核心是为了让专业的工具做专业的事:你的后端专注于业务逻辑,而图片的存储、分发、处理这些复杂且通用的工作,交给阿里云这样专业的云服务商来处理,最终实现更高效、更稳定、更省钱。
设计这个controller的目的是接收前端传过来的图片,从而为接下来的操作准备。不仅仅是菜品的,套餐服务需要上传图片时也能使用。
@RestController
@RequestMapping("/admin/common")
@Slf4j
@Api(tags = "通用接口")
public class CommonController {
/**
* 文件上传
* @param file
* @return
*/
@PostMapping("/upload")
@ApiOperation("文件上传")
public Result<String> upload(MultipartFile file){//注意,这里面的参数必须要和前端传过来的一样file,不然就接收不到前端的图片了,String是返回图片的路径,用于用户查看照片,可以直接调用这个路径
log.info("文件上传:{}",file);
//把文件上传到阿里云服务需要注意的是,我们在项目中还创建了一个AliOssProperties类,这个类加上了注解@ConfigrationProperties,这种类是读取配置文件的配置项,然后把这些封装成java对象。
这样就把配置信息和业务代码分离开,实现了更好的代码组织和维护性。
alioss:
endpoint: ${sky.alioss.endpoint}
access-key-id: ${sky.alioss. access-key-id}
access-key-secret: ${sky.alioss.access-key-secret}
bucket-name: ${sky.alioss.bucket-name}/**
* 配置类,用于创建AliUtil对象
*/
@Configuration
@Slf4j
public class OssConfiguration {
@Bean
@ConditionalOnBean
public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){
log.info("开始创建阿里云工作类对象,{}",aliOssProperties);
return new AliOssUtil(aliOssProperties.getEndpoint(),
aliOssProperties.getAccessKeyId(),
aliOssProperties.getAccessKeySecret(),
aliOssProperties.getBucketName());
对比项 | 客户端对象 (OSSClient) | Util类对象 (AliOssUtil) |
|---|---|---|
谁写的 | 阿里云官方 | 你自己 |
数量 | 通常只有一个(单例) | 通常只有一个(单例) |
复杂度 | 高(几十个方法) | 低(只有几个你需要的方法) |
依赖 | 无 | 内部包含客户端对象 |
配置 | 每次用都要配置 | 初始化时配置一次 |
异常处理 | 抛出原始异常 | 统一处理成业务异常 |
复用性 | 低(每个地方都要写重复代码) | 高(一个方法到处用) |
测试难度 | 难(依赖真实网络) | 易(可mock客户端) |
客户端对象是阿里云提供的"真工人",Util类对象是你雇来管理工人的"包工头"——包工头让工人干活更规范、更方便,你只需要跟包工头打交道就行。
对象 | 角色 | 比喻 |
|---|---|---|
OSSClient | 执行者 | 洗碗工 |
AliOssUtil | 封装者 | 洗碗机 |
你的Service | 使用者 | 餐厅老板 |
代码概念 | 现实比喻 |
|---|---|
@Configuration | "我是总店长"的胸牌 |
@Bean | "我负责采购这些设备"的采购清单 |
AliOssUtil | 奶茶机 |
OssProperties | 设备说明书(配置参数) |
application.yml | 总部发的统一采购标准 |
@Autowired | 各门店领用设备 |

注解 | 作用 | 比喻 |
|---|---|---|
@Configuration | 标记类是配置类 | 挂上"总店"招牌 |
@Bean | 标记方法是创建Bean的方法 | 写出"采购清单" |
类 | 角色 | 比喻 | 是否需要@Configuration |
|---|---|---|---|
OssConfiguration | 配置类 | 总店长(负责采购) | 需要 @Configuration |
AliOssUtil | 工具类 | 奶茶机(干活的) | 不需要 |
OssProperties | 属性类 | 设备说明书 | 不需要 |
@Configuration是挂牌子,@Bean是写采购清单——挂了总店的牌子(@Configuration)还不够,必须写明要采购什么设备(@Bean),仓库(Spring容器)才知道要准备什么!
我们只需要设置一个用于存储文件的Bucket

关于用量的问题,由于我们是用于个人的测试为主,因此我们不需要设置成公共的,设置成私人即可。
由于我们已经创建了AliOssUtil的对象,所以直接其中注入,因为是被Spring管理的
// Spring启动时: // 1. 看到@Configuration → 哦,这是配置类 // 2. 看到@Bean → 哦,这个方法返回的对象要管理 // 3. 执行方法 → 创建AliOssUtil对象 // 4. 放进容器 → 以后谁要就给谁,所以可以注入。
┌─────────────────┐
│ OssProperties │ ← 读取配置
└────────┬────────┘
↓
┌─────────────────┐
│OssConfiguration │ ← 配置类,创建Bean
└────────┬────────┘
↓
┌─────────────────┐
│ AliOssUtil │ ← 工具类,真正干活
└────────┬────────┘
↓
┌─────────────────┐
│ 业务Service │ ← 使用工具类
└─────────────────┘然后在controller中调用工具类中的上传文件的方法,里面有两个参数,一个是byte数组,另一个是文件名,用UUID生成的,避免文件名重复产生错误。
log.info("文件上传:{}",file); //把文件上传到阿里云服务 try { //原始文件名 String originalFilename = file.getOriginalFilename(); //截取原始文件名的后缀 String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); //构造新文件名名称 String objectName= UUID.randomUUID().toString()+extension; //文件的地址/网址 String filePath = aliOssUtil.upload(file.getBytes(), objectName); return Result.success(filePath); } catch (IOException e) { log.info("文件上传失败,{}",e); }结语:感谢看到最后的大家,如果对你有帮助,请点赞,关注,收藏,你的鼓励就是我最大的动力!!