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

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


文章目录

1、业务介绍

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

  1. 保存或更新公寓信息
  2. 根据条件分页查询详细信息
  3. 根据ID获取公寓详情信息
  4. 根据ID删除公寓信息
  5. 根据ID修改公寓发布状态
  6. 根据区县ID查询公寓信息列表
2、逻辑模型介绍
3、接口开发
3.1、保存或更新公寓信息

查看接口

进行开发

查看web-admin模块 中的com.atguigu.lease.web.admin.vo.apartment.ApartmentSubmitVo类,内容如下:

java 复制代码
@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层逻辑

java 复制代码
@Operation(summary = "保存或更新公寓信息")
@PostMapping("saveOrUpdate")
public Result saveOrUpdate(@RequestBody ApartmentSubmitVo apartmentSubmitVo) {
    service.saveOrUpdateApartment(apartmentSubmitVo);
    return Result.ok();
}

编写Service层逻辑

  • ApartmentInfoService中增加如下内容

    java 复制代码
    void saveOrUpdateApartment(ApartmentSubmitVo apartmentSubmitVo);
  • ApartmentInfoServiceImpl中增加如下内容

    java 复制代码
    /**
     * @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、根据条件分页查询详细信息

查看接口

进行开发

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

  • 请求数据结构

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

    • ApartmentQueryVo为公寓的查询条件,详细结构如下:

java 复制代码
@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,内容如下:

java 复制代码
@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中增加如下内容:

java 复制代码
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}

接口实现

  • 编写Controller层逻辑

    ApartmentController中增加如下内容:

java 复制代码
@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中增加如下内容
java 复制代码
IPage<ApartmentItemVo> pageApartmentItemByQuery(IPage<ApartmentItemVo> page, ApartmentQueryVo queryVo);

ApartmentInfoServiceImpl中增加如下内容

java 复制代码
@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中增加如下内容

java 复制代码
IPage<ApartmentItemVo> pageApartmentItemByQuery(IPage<ApartmentItemVo> page, ApartmentQueryVo queryVo);

ApartmentInfoMapper.xml中增加如下内容

java 复制代码
<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做打平处理

yml 复制代码
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,内容如下

java 复制代码
@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中增加如下内容

java 复制代码
@Operation(summary = "根据ID获取公寓详细信息")
@GetMapping("getDetailById")
public Result<ApartmentDetailVo> getDetailById(@RequestParam Long id) {
    ApartmentDetailVo apartmentDetailVo = apartmentInfoService.getDetailById(id);
    return Result.ok(apartmentDetailVo);
}

编写Service层逻辑

ApartmentInfoService中增加如下内容

java 复制代码
ApartmentDetailVo getApartmentDetailById(Long id);

ApartmentInfoServiceImpl中增加如下内容

java 复制代码
@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中增加如下内容

java 复制代码
/**
* @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中增加如下内容

java 复制代码
/**
* @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中增加如下内容

java 复制代码
/**
* @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中增加如下内容

java 复制代码
/**
* @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中增加如下内容

java 复制代码
<?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中增加如下内容

java 复制代码
@Operation(summary = "根据id删除公寓信息")
@DeleteMapping("removeById")
public Result removeById(@RequestParam Long id) {
    apartmentInfoService.removeApartmentById(id);
    return Result.ok();
}

编写Service层逻辑

ApartmentInfoService中增加如下内容

java 复制代码
/**
     * 根据id删除公寓信息
     * @param id
     */
void removeApartmentById(Long id);

ApartmentInfoServiceImpl中增加如下内容

java 复制代码
@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);


}

知识点

由于公寓下会包含房间信息,因此在删除公寓时最好先判断一下该公寓下是否存在房间信息,若存在,则提醒用户先删除房间信息后再删除公寓信息,判断逻辑如下

java 复制代码
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类,内容如下:

java 复制代码
@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类中,增加自定义异常类的处理逻辑

java 复制代码
@ExceptionHandler(LeaseException.class)
@ResponseBody
public Result error(LeaseException e){
    e.printStackTrace();
    return Result.fail(e.getCode(), e.getMessage());
}

为Result新增一个构造方法,如下

java 复制代码
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中增加如下内容:

java 复制代码
@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中增加如下内容:

java 复制代码
@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);
}
相关推荐
gnip32 分钟前
企业级配置式表单组件封装
前端·javascript·vue.js
一只叫煤球的猫1 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9652 小时前
tcp/ip 中的多路复用
后端
bobz9652 小时前
tls ingress 简单记录
后端
皮皮林5513 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友3 小时前
什么是OpenSSL
后端·安全·程序员
bobz9653 小时前
mcp 直接操作浏览器
后端
前端小张同学6 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook6 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康7 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark