【云岚到家】-day02-1-区域服务后续开发及完善
- 1 区域服务后续开发
- 1.1 添加区域服务
- 1.1.1 接口定义
- 1.1.1.1 接口设计
- 1.1.1.2 接口定义-json
- 1.1.2 接口开发
- 1.1.2.1 mapper
- 1.1.2.2 service
- 1.1.2.3 controller
- 1.1.3 测试
- 1.2 修改价格
- 1.2.1 接口定义
- 1.2.1.1 接口设计
- 1.2.1.2 接口定义-form表单
- 1.2.2 接口开发
- 1.2.2.1 mapper
- 1.2.2.2 service
- 1.2.2.3 controller
- 1.2.3 测试
- 1.3 服务上架
- 1.3.1 接口定义
- 1.3.1.1 接口设计
- 1.3.1.2 接口定义-form表单格式
- 1.3.2 接口开发
- 1.3.2.1 mapper
- 1.3.2.2 service
- 1.3.2.3 controller
- 1.3.3 测试
- 1.4 删除区域服务开发
- 1.4.1 接口定义
- 1.4.1.1 接口设计
- 1.4.1.2 接口定义-form表单
- 1.4.2 接口开发
- 1.4.2.1 mapper
- 1.4.2.2 service
- 1.4.2.3 controller
- 1.4.3 测试
- 1.5 区域服务下架开发
- 1.5.1 mapper
- 1.5.2 service
- 1.5.3 controller
- 1.5.4 测试
- 1.6 设置热门开发
- 1.6.1 mapper
- 1.6.2 service
- 1.6.3 controller
- 1.6.4 测试
- 1.7 取消热门开发
- 1.7.1 mapper
- 1.7.2 service
- 1.7.3 controller
- 1.7.4 测试
- 1.8 启用区域功能完善
- 1.8.1 创建按区域id和状态查询的mapper
- 1.8.2 增加校验逻辑
- 1.9 禁用区域功能完善
- 1.9.1 增加校验逻辑
- 1.10 禁用服务项功能完善
- 1.10.1 创建按服务项id和状态查询的mapper
- 1.10.2 增加校验逻辑
1 区域服务后续开发
1.1 添加区域服务
1.1.1 接口定义
1.1.1.1 接口设计
下边设计添加区域服务接口,重点设计:传入参数类型、参数内容、响应结果内容。
根据界面原型梳理操作流程:
首先进入某个区域的服务列表:
点击添加服务,如下图:
选择要添加到区域中的服务,点击“添加”按钮。
最终向serve表添加数据。
serve表存储了区域中开通的服务,根据字段信息思考数据来源:
字段名 | 含义 | 数据来源 |
---|---|---|
id | 服务id | 主键,自动生成 |
serve_item_id | 服务项id | 接口传入 |
region_id | 区域id | 接口传入 |
city_code | 城市编码 | 根据区域id查询region表得到 |
sale_status | 售卖状态 | 默认为草稿状态,数据库设置了默认值 |
price | 价格 | 默认为服务项的价格,用户可修改 |
is_hot | 是否为热门 | 默认为非热门,数据库设置了默认值 |
hot_time_stamp | 更新为热门的时间戳 | 默认空 |
create_time | 创建时间 | 数据库设置了默认值 |
update_time | 更新时间 | 数据库设置了默认值 |
create_by | 创建人 | 由framework的MyBatisAutoFillInterceptor自动处理 |
update_by | 更新人 | 由framework的MyBatisAutoFillInterceptor自动处理 |
根据上边的分析,通过接口传入服务项id、区域id、价格,支持多个服务传入,实现批量添加。
传入参数内容为数组,所以使用json格式。
传入参数内容包括:服务项id、区域id、价格。
响应结果内容可为空,前端根据状态码判断是否添加成功。
接口设计信息:
接口路径:POST/foundations/operation/serve/batch
请求数据类型 application/json
1.1.1.2 接口定义-json
controller
@PutMapping("/batch")
@ApiOperation("批量添加区域服务")
public void batch(@RequestBody List<ServeUpsertReqDTO> serveUpsertReqDTOList) {
return;
}
批量添加的请求参数用List来接收,@RequestBody
主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);而最常用的使用请求体传参的无疑是POST请求了,所以使用@RequestBody接收数据时,一般都用POST方式进行提交。而GET请求一般是key-value格式,则不用添加@RequestBody
。
查看接口文档:
重启服务,查看swagger文档如下:
1.1.2 接口开发
1.1.2.1 mapper
添加接口只向serve表添加数据所以使用mybatisplus提供的单表CRUD方法即可无需自定义mapper接口。
1.1.2.2 service
定义service接口:
void batchAdd(List<ServeUpsertReqDTO> serveUpsertReqDTOList);
定义service实现方法:
@Resource
private ServeItemMapper serveItemMapper;
@Resource
private RegionMapper regionMapper;
/**
* 批量添加区域服务
* @param serveUpsertReqDTOList
*/
@Override
@Transactional
public void batchAdd(List<ServeUpsertReqDTO> serveUpsertReqDTOList) {
for (ServeUpsertReqDTO serveUpsertReqDTO : serveUpsertReqDTOList) {
//1.校验服务项是否为启用状态,不是启用状态不能新增
ServeItem serveItem = serveItemMapper.selectById(serveUpsertReqDTO.getServeItemId());
//如果服务项信息不存在或未启用
if(ObjectUtil.isNull(serveItem) || serveItem.getActiveStatus()!= FoundationStatusEnum.ENABLE.getStatus()){
throw new ForbiddenOperationException("该服务未启用无法添加到区域下使用");
}
//2.校验是否重复新增
Integer count = lambdaQuery()
.eq(Serve::getRegionId, serveUpsertReqDTO.getRegionId())
.eq(Serve::getServeItemId, serveUpsertReqDTO.getServeItemId())
.count();
if(count>0){
throw new ForbiddenOperationException(serveItem.getName()+"服务已存在");
}
//3.新增服务
Serve serve = BeanUtil.toBean(serveUpsertReqDTO, Serve.class);
Region region = regionMapper.selectById(serveUpsertReqDTO.getRegionId());
serve.setCityCode(region.getCityCode());
baseMapper.insert(serve);
}
}
说明:
对于增、删、改类的接口通常会先进行入参校验,校验失败抛出异常,由异常处理器统一对异常进行处理。
我们抛出的异常是自定义异常类型,自定义的异常类型都继承了CommonException类型,在异常处理器中对此类型的异常进行处理。
异常处理器的源码在jzo2o-framework中,如下图:
通过@RestControllerAdvice注解加@ExceptionHandler注解实现,具体的原理是当controller抛出异常由DispatcherServlet统一拦截处理,再根据异常类型找到@ExceptionHandler标识方法去执行该方法进行异常处理。
1.1.2.3 controller
在controller方法中调用service接口添加区域服务。
@PostMapping("/batch")
@ApiOperation("批量添加区域服务")
public void batch(@RequestBody List<ServeUpsertReqDTO> serveUpsertReqDTOList) {
serveService.batchAdd(serveUpsertReqDTOList);
}
1.1.3 测试
我们通过前后端联调测试添加区域服务接口。
启动foundations服务和gateway服务。
通过cmd进入project-xzb-pc-admin-vue3-java目录启动前端项目
运行命令,启动前端工程
npm run dev
因为代码是我们写的,我们在前后端联调时除了测试成功结果还要测试失败结果。
下边我们先测试成功结果。
进入运营管理端,点击“区域管理”菜单,点击“设置服务”
区域服务列表如下图:
点击添加服务
点击添加服务,如下图:
添加成功查询区域服务列表是否存在已添加的服务。
如果列表没有显示已添加的服务则需要进行排查,可以进入数据库直接查询serve表查看是否添加成功,如果后台的错误日志需要根据错误日志进行排查。
下边测试失败结果。
1.2 修改价格
1.2.1 接口定义
1.2.1.1 接口设计
根据界面原型设计传入参数类型、参数内容、响应结果内容。
进入修改界面
输入价格进行修改
传入参数内容:价格、区域服务ID(serve表主键)
根据id更新serve表的价格字段。
传入参数类型:form表单格式
响应结果内容可为空,前端根据状态码判断是否添加成功。
接口设计信息如下:
接口路径:PUT/foundations/operation/serve/{id}
请求数据类型 application/x-www-form-urlencoded
1.2.1.2 接口定义-form表单
编写controller方法,HTTP方法使用PUT方式,代码如下:
PUT/foundations/operation/serve/{id}从url中接,用@PathVariable
,id是从url中获取,而price是前端传过来了的,所以用 @RequestParam
来接收。
@PutMapping("/{id}")
@ApiOperation("区域服务价格修改")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class),
@ApiImplicitParam(name = "price", value = "价格", required = true, dataTypeClass = BigDecimal.class)
})
public void update( @PathVariable("id") Long id,
@RequestParam("price") BigDecimal price) {
}
1.2.2 接口开发
1.2.2.1 mapper
修改接口只向serve表更新数据所以使用mybatisplus提供的单表CRUD方法即可无需自定义mapper接口。
1.2.2.2 service
定义service接口:
Serve update(Long id, BigDecimal price);
返回修改后的Serve类型有助于我们后期做缓存,就不用再查了。
定义service实现方法:
@Override
@Transactional
public Serve update(Long id, BigDecimal price) {
//1.更新服务价格
boolean update = lambdaUpdate()
.eq(Serve::getId, id)
.set(Serve::getPrice, price)
.update();
if(!update){
throw new CommonException("修改服务价格失败");
}
return baseMapper.selectById(id);
}
1.2.2.3 controller
在controller方法中调用service接口价格修改。
@PutMapping("/{id}")
@ApiOperation("区域服务价格修改")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class),
@ApiImplicitParam(name = "price", value = "价格", required = true, dataTypeClass = BigDecimal.class)
})
public void update(@PathVariable("id") Long id,
@RequestParam("price") BigDecimal price) {
serveService.update(id, price);
}
1.2.3 测试
查看控制台
测试成功
1.3 服务上架
1.3.1 接口定义
1.3.1.1 接口设计
根据业务流程、界面原型进行设计。
在区域服务列表中点击“上架”,此服务在该区域将生效,用户即可对该服务进行下单。
业务流程如下:
在serve表有一个售卖状态sale_status,服务上架后将状态更改为“上架”,代码为2。
下边分析接口的传入参数类型、参数内容、响应结果内容。
传入参数内容:服务Id(serve表的主键),
传入参数类型:form表单格式
响应结果内容: 内容可为空,前端根据状态码判断是否添加成功。
注意:区分 服务id和服务项id,服务id即serve表的主键,服务项id即serve_item表的主键。
接口信息如下:
接口路径:PUT/foundations/operation/serve/onSale/{id}
请求数据类型 application/x-www-form-urlencoded
1.3.1.2 接口定义-form表单格式
编写controller方法,HTTP方法使用PUT方式,代码如下:
@PutMapping("/onSale/{id}")
@ApiOperation("区域服务上架")
@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class)
public void onSale(@PathVariable("id") Long id) {
return;
}
1.3.2 接口开发
1.3.2.1 mapper
服务上架最终修改serve表的状态,所以使用mybatisplus提供的单表CRUD方法即可无需自定义mapper接口。
1.3.2.2 service
定义service接口:
Serve onSale(Long id);
实现:
@Override
@Transactional
public Serve onSale(Long id) {
//1.校验服务是否存在
Serve serve = baseMapper.selectById(id);
if(ObjectUtil.isNull(serve)){
throw new ForbiddenOperationException("区域服务不存在");
}
//2.校验服务状态是否为草稿或下架状态
if(serve.getSaleStatus()!= FoundationStatusEnum.INIT.getStatus() || serve.getSaleStatus()!= FoundationStatusEnum.DISABLE.getStatus()){
throw new ForbiddenOperationException("服务状态不为草稿或下架状态,无法上架");
}
//3.校验服务项是否为启用状态
ServeItem serveItem = serveItemMapper.selectById(serve.getServeItemId());
if(serveItem.getActiveStatus()!= FoundationStatusEnum.ENABLE.getStatus()){
throw new ForbiddenOperationException("本服务所属的服务项未启用,无法上架");
}
//4.更新服务状态为上架
boolean update = lambdaUpdate()
.eq(Serve::getId, id)
.set(Serve::getSaleStatus, FoundationStatusEnum.ENABLE.getStatus())
.update();
if(!update){
throw new CommonException("服务上架失败");
}
return baseMapper.selectById(id);
}
1.3.2.3 controller
@PutMapping("/onSale/{id}")
@ApiOperation("区域服务上架")
@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class)
public void onSale(@PathVariable("id") Long id) {
serveService.onSale(id);
}
1.3.3 测试
上架成功
1.4 删除区域服务开发
1.4.1 接口定义
1.4.1.1 接口设计
根据业务流程、界面原型进行设计。
在区域服务列表中点击“删除”,此服务在该区域将删除。
业务流程如下:
在serve表中删除该服务。
下边分析接口的传入参数类型、参数内容、响应结果内容。
传入参数内容:服务Id(serve表的主键),
传入参数类型:form表单格式
响应结果内容: 内容可为空,前端根据状态码判断是否添加成功。
注意:区分 服务id和服务项id,服务id即serve表的主键,服务项id即serve_item表的主键。
接口信息如下:
接口路径:DELETE/foundations/operation/serve/{id}
请求数据类型 application/x-www-form-urlencoded
1.4.1.2 接口定义-form表单
@DeleteMapping("/{id}")
@ApiOperation("区域服务删除")
@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class)
public void delete(@PathVariable("id") Long id) {
return;
}
1.4.2 接口开发
1.4.2.1 mapper
服务上架最终修改serve表的状态,所以使用mybatisplus提供的单表CRUD方法即可无需自定义mapper接口。
1.4.2.2 service
定义service接口:
void delete(Long id);
实现:
@Override
@Transactional
public void delete(Long id) {
//1.校验服务是否存在
Serve serve = baseMapper.selectById(id);
if(ObjectUtil.isNull(serve)){
throw new ForbiddenOperationException("区域服务不存在");
}
//2.校验服务状态是否为草稿状态
if(serve.getSaleStatus()!= FoundationStatusEnum.INIT.getStatus()){
throw new ForbiddenOperationException("服务状态不为草稿状态,无法删除");
}
//3.删除服务
baseMapper.deleteById(id);
}
1.4.2.3 controller
@DeleteMapping("/{id}")
@ApiOperation("区域服务删除")
@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class)
public void delete(@PathVariable("id") Long id) {
serveService.delete(id);
}
1.4.3 测试
1.5 区域服务下架开发
业务流程:
接口文档:
1.5.1 mapper
服务上架最终修改serve表的状态,所以使用mybatisplus提供的单表CRUD方法即可无需自定义mapper接口。
1.5.2 service
接口:
void offSale(Long id);
实现:
@Override
@Transactional
public void offSale(Long id) {
//1.校验服务是否存在
Serve serve = baseMapper.selectById(id);
if(ObjectUtil.isNull(serve)){
throw new ForbiddenOperationException("区域服务不存在");
}
//2.校验服务状态是否为上架状态
if(serve.getSaleStatus()!= FoundationStatusEnum.ENABLE.getStatus()){
throw new ForbiddenOperationException("服务状态不为上架状态,无法下架");
}
//3.更新服务状态为下架
boolean update = lambdaUpdate()
.eq(Serve::getId, id)
.set(Serve::getSaleStatus, FoundationStatusEnum.DISABLE.getStatus())
.update();
if(!update){
throw new CommonException("服务下架失败");
}
}
1.5.3 controller
@PutMapping("/offSale/{id}")
@ApiOperation("区域服务下架")
@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class)
public void offSale(@PathVariable("id") Long id) {
serveService.offSale(id);
}
1.5.4 测试
1.6 设置热门开发
要求:实现更新serve表的是否热门字段。
接口文档:
1.6.1 mapper
服务上架最终修改serve表的状态,所以使用mybatisplus提供的单表CRUD方法即可无需自定义mapper接口。
1.6.2 service
接口:
Serve onHot(Long id);
实现:
@Override
@Transactional
public Serve onHot(Long id) {
//1.校验服务是否存在
Serve serve = baseMapper.selectById(id);
if(ObjectUtil.isNull(serve)){
throw new ForbiddenOperationException("区域服务不存在");
}
//2.校验服务状态是否为上架状态
if(serve.getSaleStatus()!= FoundationStatusEnum.ENABLE.getStatus()){
throw new ForbiddenOperationException("服务状态不为上架状态,无法设置热门");
}
//3.更新服务状态为热门
boolean update = lambdaUpdate()
.eq(Serve::getId, id)
.set(Serve::getIsHot, FoundationStatusEnum.ONHOT.getStatus())
.update();
if(!update){
throw new CommonException("服务设置热门失败");
}
return baseMapper.selectById(id);
}
1.6.3 controller
@PutMapping("/onHot/{id}")
@ApiOperation("区域服务设置热门")
@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class)
public void onHot(@PathVariable("id") Long id) {
serveService.onHot(id);
}
1.6.4 测试
1.7 取消热门开发
要求:实现更新serve表的是否热门字段。
接口文档:
1.7.1 mapper
服务上架最终修改serve表的状态,所以使用mybatisplus提供的单表CRUD方法即可无需自定义mapper接口。
1.7.2 service
接口:
Serve offHot(Long id);
实现:
@Override
@Transactional
public Serve offHot(Long id) {
//1.校验服务是否存在
Serve serve = baseMapper.selectById(id);
if(ObjectUtil.isNull(serve)){
throw new ForbiddenOperationException("区域服务不存在");
}
//2.校验服务状态是否为上架状态
if(serve.getSaleStatus()!= FoundationStatusEnum.ENABLE.getStatus()){
throw new ForbiddenOperationException("服务状态不为上架状态,无法取消热门");
}
//3.更新服务状态为非热门
boolean update = lambdaUpdate()
.eq(Serve::getId, id)
.set(Serve::getIsHot, FoundationStatusEnum.OFFHOT.getStatus())
.update();
if(!update){
throw new CommonException("服务取消热门失败");
}
return baseMapper.selectById(id);
}
1.7.3 controller
@PutMapping("/offHot/{id}")
@ApiOperation("区域服务取消热门")
@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class)
public void offHot(@PathVariable("id") Long id) {
serveService.offHot(id);
}
1.7.4 测试
同1.6.4
1.8 启用区域功能完善
业务流程:
增加校验:区域下存在上架的服务方可启用。
1.8.1 创建按区域id和状态查询的mapper
在com.jzo2o.foundations.mapper.ServeMapper中创建
List<ServeResDTO> queryServeListByRegionIdAndStatus(@Param("regionId") Long regionId, @Param("status") Integer status);
xml
<select id="queryServeListByRegionIdAndStatus"
resultType="com.jzo2o.foundations.model.dto.response.ServeResDTO">
SELECT
st.id serve_type_id,
si.NAME serve_item_name,
s.update_time update_time,
s.sale_status sale_status,
si.id serve_item_id,
si.reference_price reference_price,
s.create_time create_time,
s.region_id region_id,
s.price price,
s.id id,
s.is_hot is_hot,
st.NAME serve_type_name
FROM
serve s
INNER JOIN serve_item si ON s.serve_item_id = si.id
INNER JOIN serve_type st ON si.serve_type_id = st.id
WHERE
region_id = #{regionId} AND sale_status = #{status}
</select>
1.8.2 增加校验逻辑
在com.jzo2o.foundations.service.impl.RegionServiceImpl#active方法中
//如果需要启用区域,需要校验该区域下是否有上架的服务
List<ServeResDTO> serveResDTOS = serveMapper.queryServeListByRegionIdAndStatus(region.getId(), FoundationStatusEnum.ENABLE.getStatus());
if (serveResDTOS.size() == 0) {
throw new ForbiddenOperationException("区域内无可用的上架服务,无法启用");
}
1.9 禁用区域功能完善
业务流程:
增加校验:区域下不存在上架的服务方可禁用。
1.9.1 增加校验逻辑
在com.jzo2o.foundations.service.impl.RegionServiceImpl#deactivate方法中
List<ServeResDTO> serveResDTOS = serveMapper.queryServeListByRegionIdAndStatus(region.getId(), FoundationStatusEnum.ENABLE.getStatus());
if (serveResDTOS.size() > 0) {
throw new ForbiddenOperationException("区域内有上架服务,无法禁用");
}
1.10 禁用服务项功能完善
业务流程
1.10.1 创建按服务项id和状态查询的mapper
在com.jzo2o.foundations.mapper.ServeMapper中创建
List<ServeResDTO> queryServeListByServeItemIdAndStatus(@Param("serveItemId") Long serveItemId, @Param("status") Integer status);
xml
<select id="queryServeListByServeItemIdAndStatus"
resultType="com.jzo2o.foundations.model.dto.response.ServeResDTO">
SELECT
st.id serve_type_id,
si.NAME serve_item_name,
s.update_time update_time,
s.sale_status sale_status,
si.id serve_item_id,
si.reference_price reference_price,
s.create_time create_time,
s.region_id region_id,
s.price price,
s.id id,
s.is_hot is_hot,
st.NAME serve_type_name
FROM
serve s
INNER JOIN serve_item si ON s.serve_item_id = si.id
INNER JOIN serve_type st ON si.serve_type_id = st.id
WHERE
serve_item_id = #{serveItemId} AND sale_status = #{status}
</select>
1.10.2 增加校验逻辑
在com.jzo2o.foundations.service.impl.ServeItemServiceImpl#deactivate中
//有区域在使用该服务将无法禁用(存在关联的区域服务且状态为上架表示有区域在使用该服务项)
List<ServeResDTO> serveResDTOS = serveMapper.queryServeListByServeItemIdAndStatus(id, FoundationStatusEnum.ENABLE.getStatus());
if (serveResDTOS.size() > 0) {
throw new ForbiddenOperationException("存在关联的区域服务且状态为上架表示有区域在使用该服务项");
}