【尚庭公寓SpringBoot + Vue 项目实战】公寓管理(十一)
文章目录
- 【尚庭公寓SpringBoot + Vue 项目实战】公寓管理(十一)
- 1、业务介绍
- 2、逻辑模型介绍
- 3、接口开发
- 3.1、保存或更新公寓信息
- 3.2、根据条件分页查询详细信息
- 3.3、根据ID获取公寓详细信息
- 3.4、 根据ID删除公寓信息
- 3.5、根据ID修改公寓发布状态
- 3.6、根据区县ID查询公寓信息列表
1、业务介绍
公寓管理共有六个接口,分别是
- 保存或更新公寓信息
- 根据条件分页查询详细信息
- 根据ID获取公寓详情信息
- 根据ID删除公寓信息
- 根据ID修改公寓发布状态
- 根据区县ID查询公寓信息列表
2、逻辑模型介绍
3、接口开发
3.1、保存或更新公寓信息
查看接口
进行开发
查看web-admin模块中的com.atguigu.lease.web.admin.vo.apartment.ApartmentSubmitVo
类,内容如下:
@Schema(description = "公寓信息") @Data public class ApartmentSubmitVo extends ApartmentInfo { @Schema(description="公寓配套id") private List<Long> facilityInfoIds; @Schema(description="公寓标签id") private List<Long> labelIds; @Schema(description="公寓杂费值id") private List<Long> feeValueIds; @Schema(description="公寓图片id") private List<GraphVo> graphVoList; }
复制
编写Controller层逻辑
@Operation(summary = "保存或更新公寓信息") @PostMapping("saveOrUpdate") public Result saveOrUpdate(@RequestBody ApartmentSubmitVo apartmentSubmitVo) { service.saveOrUpdateApartment(apartmentSubmitVo); return Result.ok(); }
复制
编写Service层逻辑
-
在
ApartmentInfoService
中增加如下内容
复制void saveOrUpdateApartment(ApartmentSubmitVo apartmentSubmitVo); -
在
ApartmentInfoServiceImpl
中增加如下内容
复制/** * @author liubo * @description 针对表【apartment_info(公寓信息表)】的数据库操作Service实现 * @createDate 2023-07-24 15:48:00 */ @Service public class ApartmentInfoServiceImpl extends ServiceImpl<ApartmentInfoMapper, ApartmentInfo> implements ApartmentInfoService { @Autowired private GraphInfoService graphInfoService; @Autowired private ApartmentFacilityService apartmentFacilityService; @Autowired private ApartmentLabelService apartmentLabelService; @Autowired private ApartmentFeeValueService apartmentFeeValueService; /** * 保存或更新公寓信息 * * @param apartmentSubmitVo */ @Override @Transactional public void apartmentSaveOrUpdate(ApartmentSubmitVo apartmentSubmitVo) { //判断是新增方法还是修改方法 boolean isUpdate = apartmentSubmitVo.getId() != null; super.saveOrUpdate(apartmentSubmitVo); if (isUpdate){ //进行删除,先删除后再添加 //删除图片 LambdaQueryWrapper<GraphInfo> graphInfoQueryWrapper = new LambdaQueryWrapper<>(); graphInfoQueryWrapper.eq(GraphInfo::getItemType, ItemType.APARTMENT); graphInfoQueryWrapper.eq(GraphInfo::getItemId,apartmentSubmitVo.getId()); graphInfoService.remove(graphInfoQueryWrapper); //删除配套列表 LambdaQueryWrapper<ApartmentFacility> apartmentFacilityQueryWrapper = new LambdaQueryWrapper<>(); apartmentFacilityQueryWrapper.eq(ApartmentFacility::getApartmentId,apartmentSubmitVo.getId()); apartmentFacilityService.remove(apartmentFacilityQueryWrapper); //删除标签 LambdaQueryWrapper<ApartmentLabel> apartmentLabelQueryWrapper = new LambdaQueryWrapper<>(); apartmentLabelQueryWrapper.eq(ApartmentLabel::getApartmentId,apartmentSubmitVo.getId()); apartmentLabelService.remove(apartmentLabelQueryWrapper); //删除杂费 LambdaQueryWrapper<ApartmentFeeValue> apartmentFeeValueQueryWrapper = new LambdaQueryWrapper<>(); apartmentFeeValueQueryWrapper.eq(ApartmentFeeValue::getApartmentId,apartmentSubmitVo.getId()); apartmentFeeValueService.remove(apartmentFeeValueQueryWrapper); } //插入图片 List<GraphVo> graphVoList = apartmentSubmitVo.getGraphVoList(); if (!CollectionUtils.isEmpty(graphVoList)){ List<GraphInfo> graphInfoList = new ArrayList<>(); //循环添加 for (GraphVo graphVo : graphVoList) { GraphInfo graphInfo = GraphInfo.builder() .name(graphVo.getName()) .url(graphVo.getUrl()) .itemId(apartmentSubmitVo.getId()) .itemType(ItemType.APARTMENT) .build(); graphInfoList.add(graphInfo); } graphInfoService.saveBatch(graphInfoList); } } }
3.2、根据条件分页查询详细信息
查看接口
进行开发
查看请求和响应的数据结构
-
请求数据结构
-
current
和size
为分页相关参数,分别表示当前所处页面和每个页面的记录数。 -
ApartmentQueryVo
为公寓的查询条件,详细结构如下:
-
@Schema(description = "公寓信息") @Data public class ApartmentDetailVo extends ApartmentInfo { @Schema(description = "图片列表") private List<GraphVo> graphVoList; @Schema(description = "标签列表") private List<LabelInfo> labelInfoList; @Schema(description = "配套列表") private List<FacilityInfo> facilityInfoList; @Schema(description = "杂费列表") private List<FeeValueVo> feeValueVoList; }
复制
响应数据结构
单个公寓信息记录可查看com.atguigu.lease.web.admin.vo.apartment.ApartmentItemVo
,内容如下:
@Data @Schema(description = "后台管理系统公寓列表实体") public class ApartmentItemVo extends ApartmentInfo { @Schema(description = "房间总数") private Long totalRoomCount; @Schema(description = "空闲房间数") private Long freeRoomCount; }
复制
配置Mybatis-Plus分页插件
在common模块中的com.atguigu.lease.common.mybatisplus.MybatisPlusConfiguration
中增加如下内容:
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }
复制
接口实现
-
编写Controller层逻辑
在
ApartmentController
中增加如下内容:
@Operation(summary = "根据条件分页查询公寓列表") @GetMapping("pageItem") public Result<IPage<ApartmentItemVo>> pageItem(@RequestParam long current, @RequestParam long size, ApartmentQueryVo queryVo) { IPage<ApartmentItemVo> page = new Page<>(current, size); IPage<ApartmentItemVo> list = service.pageApartmentItemByQuery(page, queryVo); return Result.ok(list); }
复制
编写Service层逻辑
- 在
ApartmentInfoService
中增加如下内容
IPage<ApartmentItemVo> pageApartmentItemByQuery(IPage<ApartmentItemVo> page, ApartmentQueryVo queryVo);
复制
在ApartmentInfoServiceImpl
中增加如下内容
@Autowired private ApartmentInfoMapper apartmentInfoMapper; /** * 分页查询 * @param page * @param queryVo * @return */ @Override public IPage<ApartmentItemVo> pageItem(IPage<ApartmentItemVo> page, ApartmentQueryVo queryVo) { return apartmentInfoMapper.pageItem(page,queryVo); }
复制
编写Mapper层逻辑
在ApartmentInfoMapper
中增加如下内容
IPage<ApartmentItemVo> pageApartmentItemByQuery(IPage<ApartmentItemVo> page, ApartmentQueryVo queryVo);
复制
在ApartmentInfoMapper.xml
中增加如下内容
<select id="pageItem" resultType="com.atguigu.lease.web.admin.vo.apartment.ApartmentItemVo"> select ai.id, ai.name, ai.introduction, ai.district_id, ai.district_name, ai.city_id, ai.city_name, ai.province_id, ai.province_name, ai.address_detail, ai.latitude, ai.longitude, ai.phone, ai.is_release, ifnull(tc.cnt,0) total_room_count, ifnull(tc.cnt,0) - ifnull(cc.cnt,0) free_room_count from (select id, name, introduction, district_id, district_name, city_id, city_name, province_id, province_name, address_detail, latitude, longitude, phone, is_release from apartment_info <where> is_deleted=0 <if test="queryVo.provinceId != null"> and province_id=#{queryVo.provinceId} </if> <if test="queryVo.cityId != null"> and city_id=#{queryVo.cityId} </if> <if test="queryVo.districtId != null"> and district_id=#{queryVo.districtId} </if> </where> ) ai left join (select apartment_id, count(*) cnt from room_info where is_deleted = 0 and is_release = 1 group by apartment_id) tc on ai.id = tc.apartment_id left join (select apartment_id, count(*) cnt from lease_agreement where is_deleted = 0 and status in (2, 5) group by apartment_id) cc on ai.id = cc.apartment_id </select>
复制
注意:
默认情况下Knife4j为该接口生成的接口文档如下图所示,其中的queryVo参数不方便调试
可在application.yml文件中增加如下配置,将queryVo做打平处理
springdoc: default-flat-param-object: true
复制
将spring.default-flat-param-object
参数设置为true
后,效果如下。
3.3、根据ID获取公寓详细信息
查看接口
查看响应数据结构
查看web-admin下的com.atguigu.lease.web.admin.vo.apartment.ApartmentDetailVo
,内容如下
@Schema(description = "公寓信息") @Data public class ApartmentDetailVo extends ApartmentInfo { @Schema(description = "图片列表") private List<GraphVo> graphVoList; @Schema(description = "标签列表") private List<LabelInfo> labelInfoList; @Schema(description = "配套列表") private List<FacilityInfo> facilityInfoList; @Schema(description = "杂费列表") private List<FeeValueVo> feeValueVoList; }
复制
编写Controller层逻辑
在ApartmentController
中增加如下内容
@Operation(summary = "根据ID获取公寓详细信息") @GetMapping("getDetailById") public Result<ApartmentDetailVo> getDetailById(@RequestParam Long id) { ApartmentDetailVo apartmentDetailVo = apartmentInfoService.getDetailById(id); return Result.ok(apartmentDetailVo); }
复制
编写Service层逻辑
在ApartmentInfoService
中增加如下内容
ApartmentDetailVo getApartmentDetailById(Long id);
复制
在ApartmentInfoServiceImpl
中增加如下内容
@Autowired private GraphInfoMapper graphInfoMapper; @Autowired private LabelInfoMapper labelInfoMapper; @Autowired private FacilityInfoMapper facilityInfoMapper; @Autowired private FeeValueMapper feeValueMapper; /** * 根据id获取公寓详细信息 * * @param id * @return */ @Override public ApartmentDetailVo getDetailById(Long id) { //1、根据id或获取公寓信息 ApartmentInfo apartmentInfo = this.getById(id); if (apartmentInfo == null){ return null; } //2、获取图片列表 List<GraphVo> graphVoList = graphInfoMapper.getByIdGraphList(id,ItemType.APARTMENT); //3、获取标签列表 List<LabelInfo> labelInfoList = labelInfoMapper.selectListByApartmentId(id); //4、查询配套列表 List<FacilityInfo> facilityInfoList = facilityInfoMapper.selectListByApartmentId(id); //5、查询杂费信息列表 List<FeeValueVo> feeValueVoList = feeValueMapper.selectListByApartmentId(id); ApartmentDetailVo apartmentDetailVo = new ApartmentDetailVo(); //复制属性 BeanUtils.copyProperties(apartmentInfo,apartmentDetailVo); //增加属性 apartmentDetailVo.setGraphVoList(graphVoList); apartmentDetailVo.setLabelInfoList(labelInfoList); apartmentDetailVo.setFacilityInfoList(facilityInfoList); apartmentDetailVo.setFeeValueVoList(feeValueVoList); return apartmentDetailVo; }
复制
编写Mapper层逻辑
编写公寓图片查询逻辑,在GraphInfoMapper
中增加如下内容
/** * @author liubo * @description 针对表【graph_info(图片信息表)】的数据库操作Mapper * @createDate 2023-07-24 15:48:00 * @Entity com.atguigu.lease.model.GraphInfo */ public interface GraphInfoMapper extends BaseMapper<GraphInfo> { @Select("select name,url from graph_info where is_deleted=0 and item_id = #{id} and item_id = #{itemType}") List<GraphVo> getByIdGraphList(Long id,ItemType itemType); }
复制
编写公寓标签查询逻辑
在LabelInfoMapper
中增加如下内容
/** * @author liubo * @description 针对表【label_info(标签信息表)】的数据库操作Mapper * @createDate 2023-07-24 15:48:00 * @Entity com.atguigu.lease.model.LabelInfo */ public interface LabelInfoMapper extends BaseMapper<LabelInfo> { /** * 获取标签列表 * @param id * @return */ @Select("select id, type, name from label_info where label_info.is_deleted = 0 and id in " + "(select apartment_label.label_id from apartment_label where apartment_label.is_deleted = 0 and apartment_id = #{id})") List<LabelInfo> selectListByApartmentId(Long id); }
复制
编写公寓配套查询逻辑
在FacilityInfoMapper
中增加如下内容
/** * @author liubo * @description 针对表【facility_info(配套信息表)】的数据库操作Mapper * @createDate 2023-07-24 15:48:00 * @Entity com.atguigu.lease.model.FacilityInfo */ public interface FacilityInfoMapper extends BaseMapper<FacilityInfo> { /** * 根据id查询配套信息 * @param id * @return */ @Select("select id,type,name,icon from facility_info where " + "is_deleted=0 and id in " + "(select facility_id from apartment_facility where is_deleted = 0 and apartment_id = #{id})") List<FacilityInfo> selectListByApartmentId(Long id); }List<FacilityInfo> selectListByApartmentId(Long id);
复制
编写公寓杂费查询逻辑
在FeeValueMapper
中增加如下内容
/** * @author liubo * @description 针对表【fee_value(杂项费用值表)】的数据库操作Mapper * @createDate 2023-07-24 15:48:00 * @Entity com.atguigu.lease.model.FeeValue */ public interface FeeValueMapper extends BaseMapper<FeeValue> { List<FeeValueVo> selectListByApartmentId(Long id); }
复制
在FeeValueMapper.xml
中增加如下内容
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.atguigu.lease.web.admin.mapper.FeeValueMapper"> <select id="selectListByApartmentId" resultType="com.atguigu.lease.web.admin.vo.fee.FeeValueVo"> select fv.id, fv.name, fv.unit, fv.fee_key_id, fk.name as fee_key_name from fee_value fv join fee_key fk on fv.fee_key_id = fk.id where fv.is_deleted = 0 and fk.is_deleted = 0 and fv.id in (select fee_value_id from apartment_fee_value where is_deleted = 0 and apartment_id = #{id}) </select> </mapper>
复制
3.4、 根据ID删除公寓信息
查看接口
编写Controller层逻辑
在ApartmentController
中增加如下内容
@Operation(summary = "根据id删除公寓信息") @DeleteMapping("removeById") public Result removeById(@RequestParam Long id) { apartmentInfoService.removeApartmentById(id); return Result.ok(); }
复制
编写Service层逻辑
在ApartmentInfoService
中增加如下内容
/** * 根据id删除公寓信息 * @param id */ void removeApartmentById(Long id);
复制
在ApartmentInfoServiceImpl
中增加如下内容
@Autowired private RoomInfoMapper roomInfoMapper; /** * 根据id删除公寓信息 * * @param id */ @Override @Transactional public void removeApartmentById(Long id) { //查询公寓内是否有房间 LambdaQueryWrapper<RoomInfo> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(RoomInfo::getApartmentId,id); Long count = roomInfoMapper.selectCount(queryWrapper); if (count > 0){ throw new LeaseException(ResultCodeEnum.ADMIN_APARMIN_APARTMENT_DELETE_ERROR); } //删除公寓 super.removeById(id); //删除图片 LambdaQueryWrapper<GraphInfo> graphInfoQueryWrapper = new LambdaQueryWrapper<>(); graphInfoQueryWrapper.eq(GraphInfo::getItemType, ItemType.APARTMENT); graphInfoQueryWrapper.eq(GraphInfo::getItemId,id); graphInfoService.remove(graphInfoQueryWrapper); //删除配套列表 LambdaQueryWrapper<ApartmentFacility> apartmentFacilityQueryWrapper = new LambdaQueryWrapper<>(); apartmentFacilityQueryWrapper.eq(ApartmentFacility::getApartmentId,id); apartmentFacilityService.remove(apartmentFacilityQueryWrapper); //删除标签 LambdaQueryWrapper<ApartmentLabel> apartmentLabelQueryWrapper = new LambdaQueryWrapper<>(); apartmentLabelQueryWrapper.eq(ApartmentLabel::getApartmentId,id); apartmentLabelService.remove(apartmentLabelQueryWrapper); //删除杂费 LambdaQueryWrapper<ApartmentFeeValue> apartmentFeeValueQueryWrapper = new LambdaQueryWrapper<>(); apartmentFeeValueQueryWrapper.eq(ApartmentFeeValue::getApartmentId,id); apartmentFeeValueService.remove(apartmentFeeValueQueryWrapper); }
复制
知识点:
由于公寓下会包含房间信息,因此在删除公寓时最好先判断一下该公寓下是否存在房间信息,若存在,则提醒用户先删除房间信息后再删除公寓信息,判断逻辑如下
LambdaQueryWrapper<RoomInfo> roomQueryWrapper = new LambdaQueryWrapper<>(); roomQueryWrapper.eq(RoomInfo::getApartmentId, id); Long count = roomInfoMapper.selectCount(roomQueryWrapper); if (count > 0) { //直接为前端返回如下响应:先删除房间信息再删除公寓信息 }
复制
想要直接为前端返回响应,可利用前边配置的全局异常处理功能(此处直接抛出异常,全局异常处理器捕获到异常后,便会直接为前端返回响应结果)。
为灵活设置响应信息,可自定义异常类,如下
在common模块创建com.atguigu.lease.common.exception.LeaseException
类,内容如下:
@Data public class LeaseException extends RuntimeException { //异常状态码 private Integer code; /** * 通过状态码和错误消息创建异常对象 * @param message * @param code */ public LeaseException(String message, Integer code) { super(message); this.code = code; } /** * 根据响应结果枚举对象创建异常对象 * @param resultCodeEnum */ public LeaseException(ResultCodeEnum resultCodeEnum) { super(resultCodeEnum.getMessage()); this.code = resultCodeEnum.getCode(); } @Override public String toString() { return "LeaseException{" + "code=" + code + ", message=" + this.getMessage() + '}'; } }
复制
在common模块的com.atguigu.lease.common.exception.GlobalExceptionHandler
类中,增加自定义异常类的处理逻辑
@ExceptionHandler(LeaseException.class) @ResponseBody public Result error(LeaseException e){ e.printStackTrace(); return Result.fail(e.getCode(), e.getMessage()); }
复制
为Result新增一个构造方法,如下
public static <T> Result<T> fail(Integer code, String message) { Result<T> result = build(null); result.setCode(code); result.setMessage(message); return result; }
复制
3.5、根据ID修改公寓发布状态
查看接口
进行开发
在ApartmentController
中增加如下内容:
@Operation(summary = "根据id修改公寓发布状态") @PostMapping("updateReleaseStatusById") public Result updateReleaseStatusById(@RequestParam Long id, @RequestParam ReleaseStatus status) { LambdaQueryWrapper<ApartmentInfo> apartmentInfoQueryWrapper = new LambdaQueryWrapper<>(); apartmentInfoQueryWrapper.eq(ApartmentInfo::getId,id); apartmentInfoQueryWrapper.eq(ApartmentInfo::getIsRelease,status); apartmentInfoService.update(apartmentInfoQueryWrapper); return Result.ok(); }
复制
3.6、根据区县ID查询公寓信息列表
查看接口
进行开发
在ApartmentController
中增加如下内容:
@Operation(summary = "根据区县id查询公寓信息列表") @GetMapping("listInfoByDistrictId") public Result<List<ApartmentInfo>> listInfoByDistrictId(@RequestParam Long id) { LambdaQueryWrapper<ApartmentInfo> apartmentInfoQueryWrapper = new LambdaQueryWrapper<>(); apartmentInfoQueryWrapper.eq(ApartmentInfo::getDistrictId,id); List<ApartmentInfo> list = apartmentInfoService.list(apartmentInfoQueryWrapper); return Result.ok(list); }
复制