【尚庭公寓SpringBoot + Vue 项目实战】公寓管理(十一)

【尚庭公寓SpringBoot + Vue 项目实战】公寓管理(十一)


1、业务介绍

公寓管理共有六个接口,分别是

  1. 保存或更新公寓信息
  2. 根据条件分页查询详细信息
  3. 根据ID获取公寓详情信息
  4. 根据ID删除公寓信息
  5. 根据ID修改公寓发布状态
  6. 根据区县ID查询公寓信息列表

image-20240615225107206

2、逻辑模型介绍

image-20240615225155083

3、接口开发
3.1、保存或更新公寓信息

查看接口

image-20240615230105118

image-20240615230119810

进行开发

查看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、根据条件分页查询详细信息

查看接口

image-20240615230601651

进行开发

查看请求和响应的数据结构

  • 请求数据结构

    • currentsize为分页相关参数,分别表示当前所处页面每个页面的记录数

    • 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做打平处理

image-20240615231518354

springdoc:
  default-flat-param-object: true

spring.default-flat-param-object参数设置为true后,效果如下。

image-20240615231529102

3.3、根据ID获取公寓详细信息

查看接口

image-20240615232418668

image-20240615232446248

查看响应数据结构

查看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删除公寓信息

查看接口

image-20240615232524882

编写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修改公寓发布状态

查看接口

image-20240615232925369

进行开发

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查询公寓信息列表

查看接口

image-20240615233103039

进行开发

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);
}

最近更新

  1. TCP协议是安全的吗?

    2024-06-17 03:52:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-17 03:52:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-17 03:52:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-17 03:52:01       18 阅读

热门阅读

  1. 2024前端面试准备6-TS基础

    2024-06-17 03:52:01       7 阅读
  2. vue3 如何给表单添加表单效验+正则表达式

    2024-06-17 03:52:01       5 阅读
  3. LeetCode热题1. 两数之和

    2024-06-17 03:52:01       6 阅读
  4. git diff

    2024-06-17 03:52:01       8 阅读
  5. windows用脚本编译qt的项目

    2024-06-17 03:52:01       6 阅读
  6. Window上ubuntu子系统编译Android

    2024-06-17 03:52:01       6 阅读
  7. react捡起来了

    2024-06-17 03:52:01       6 阅读
  8. python判断一个数是不是偶数

    2024-06-17 03:52:01       9 阅读
  9. 编程机器人的参数表怎么看

    2024-06-17 03:52:01       6 阅读
  10. AI芯片战场的迁徙:从训练到推理的深度剖析

    2024-06-17 03:52:01       6 阅读
  11. Linux部署FTP服务

    2024-06-17 03:52:01       5 阅读
  12. 大模型算法岗 100 道面试题(含答案)

    2024-06-17 03:52:01       6 阅读