文章目录
- 一,品牌查询接口的后台实现
- 二,编码经验总结
- 1,Controller层的作用
- 1.1 参数处理
- 1.2 调用Service
- 1.3 处理Service返回结果
- 实例
- 2,VO的封装时机
- 3,Service中最好注入Service,不要直接依赖Dao
- 问题记录
本节的主要内容是给发布商品-基本信息模块的品牌查询提供后台实现。
一,品牌查询接口的后台实现
选择品牌时,查询后台获取所选分类下的品牌,以列表的方式展示。
因为是根据分类查询品牌,所以在CategoryBrandRelationController
中开发这个接口。
@GetMapping("/brands/list")public R relationBrandsList(@RequestParam(value = "catId",required = true)Long catId){List<BrandEntity> vos = categoryBrandRelationService.getBrandsByCatId(catId);List<BrandVo> collect = vos.stream().map(item -> {BrandVo brandVo = new BrandVo();brandVo.setBrandId(item.getBrandId());brandVo.setBrandName(item.getName());return brandVo;}).collect(Collectors.toList());return R.ok().put("data",collect);}
后台的Service实现如下。
public List<BrandEntity> getBrandsByCatId(Long catId) {List<CategoryBrandRelationEntity> catelogId = relationDao.selectList(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", catId));List<BrandEntity> collect = catelogId.stream().map(item -> {Long brandId = item.getBrandId();BrandEntity byId = brandService.getById(brandId);return byId;}).collect(Collectors.toList());return collect;}
二,编码经验总结
1,Controller层的作用
Controller层不做具体的业务处理,业务处理放在Service层,Controller的主要逻辑包括三个部分:
- 参数处理,如各种参数校验
- 调用Service接口
- 封装返回结果
1.1 参数处理
- 获取请求参数:
- 使用
@RequestParam
注解获取请求参数,例如获取分类ID(categoryId
)。 - 参数设置为必须传递(
required=true
),并可以指定默认值。
- 使用
- 数据校验:
- 在Controller中进行数据校验,确保接收到的数据是合法的。
- 可以使用HTTP状态码(如403)来返回错误信息,如果参数不符合要求。
- 处理复杂参数:
- 对于复杂的参数,Controller可以进行额外的校验和处理,确保数据的正确性。
1.2 调用Service
- 调用Service方法:
- Controller调用Service的方法来处理业务逻辑。例如,调用
CategoryBrandRelationService
的getBrandsByCategoryId
方法来获取品牌信息。
- Controller调用Service的方法来处理业务逻辑。例如,调用
- 传递参数:
- 将从请求中获取的参数传递给Service方法。
- 业务逻辑分离:
- Controller应尽量只处理请求和响应,具体的业务逻辑应由Service处理,避免在Controller中编写复杂的业务逻辑代码。
1.3 处理Service返回结果
- 封装返回数据:
- Service返回的数据通常是一个完整的实体类或集合。Controller需要将这些数据封装成VO(Value Object),以便前端页面使用。例如,创建
BrandVO
来封装品牌ID和品牌名称。 - 使用
List<BrandVO>
返回品牌的ID和名称,而不是直接返回实体类。
- Service返回的数据通常是一个完整的实体类或集合。Controller需要将这些数据封装成VO(Value Object),以便前端页面使用。例如,创建
- 数据转换:
- 将Service返回的实体类转换为VO,使用
stream()
和map()
方法进行转换。
- 将Service返回的实体类转换为VO,使用
- 处理复杂数据:
- 如果Service返回的数据包含复杂的关联信息,Controller需要进行适当的处理和转换,确保返回给前端的数据是简洁和易于理解的。
- 错误处理:
- 如果Service处理过程中出现错误,Controller应捕获这些错误,并返回相应的错误信息或状态码,以便前端页面能够正确显示错误信息。
实例
- 获取分类关联品牌信息:
- 参数处理:使用
@RequestParam
获取categoryId
,并进行校验。 - 调用Service:调用
CategoryBrandRelationService
的getBrandsByCategoryId
方法,传递categoryId
。 - 处理返回结果:将Service返回的品牌实体类集合转换为
List<BrandVO>
,并返回给前端页面。
- 参数处理:使用
通过这种方式,Controller能够清晰地处理请求、调用Service、并正确地处理和返回数据,确保系统的稳定性和可维护性。
2,VO的封装时机
VO
的封装应该在Controller
层或者Controller
直接调用的Service
方法,不宜在DAO
或者靠近DAO
的方法中过早封装VO
,因为VO
和前端密切关联,具有一定的特殊性,可复用性不强。
底层方法应该尽量用Entity
。
先查出Entity
,然后封装为VO
,尽量在高层接口封装VO
,底层接口统一规范,便于复用。
如下方法,getBrandsByCatId
方法返回的是Entity
,而不是VO
,使得这个方法可以被其他方法复用。
@GetMapping("/brands/list")public R relationBrandsList(@RequestParam(value = "catId",required = true)Long catId){List<BrandEntity> vos = categoryBrandRelationService.getBrandsByCatId(catId);List<BrandVo> collect = vos.stream().map(item -> {BrandVo brandVo = new BrandVo();brandVo.setBrandId(item.getBrandId());brandVo.setBrandName(item.getName());return brandVo;}).collect(Collectors.toList());return R.ok().put("data",collect);}
3,Service中最好注入Service,不要直接依赖Dao
在软件设计中,通常推荐服务层(Service Layer)之间相互依赖而不是直接依赖数据访问层(DAO Layer)。这样做有助于解耦系统各部分,提高可测试性和维护性。
假设有一个简单的应用,该应用包含用户管理和订单管理两个功能。在这个应用中,有以下类:
- UserDao - 负责数据库操作,如查询、更新用户信息等。
- OrderDao - 负责数据库操作,如查询、更新订单信息等。
- UserService - 提供用户相关的业务逻辑。
- OrderService - 提供订单相关的业务逻辑。
在这个示例中,OrderService
依赖于 UserService
而不是直接依赖 UserDao
。这样的设计有几个好处:
- 解耦 - 如果将来需要更换不同的数据库实现或者改变用户数据的存储方式,只需要更改
UserDao
的实现,而不需要更改OrderService
或者其他任何依赖于UserService
的地方。 - 易于测试 - 可以通过模拟(Mocking)
UserService
来测试OrderService
,而不必关心底层的数据访问细节。 - 更好的灵活性 - 如果需要增加额外的业务逻辑,比如权限验证、缓存等,可以直接在
UserService
中实现,而不会影响到OrderService
。
这种分层的设计模式不仅有助于保持代码的整洁和可维护性,还可以更好地适应未来的需求变化。
问题记录
这一节遇到前端选择分类后,品牌查询未被触发的问题,记录在博客的3/4两个问题中https://blog.csdn.net/epitomizelu/article/details/140569196。