【项目实践】公寓租赁项目(五):基于SpringBoot公寓信息管理接口开发

SpringBoot公寓管理接口开发

接口说明:ApartmentController

@Tag(name = "公寓信息管理")
@RestController
@RequestMapping("/admin/apartment")
public class ApartmentController {

    @Autowired
    private ApartmentInfoService service;
}


一、保存或更新公寓信息接口

接口名称:saveOrUpdate

请求方式:Post

请求路径:/admin/apartment/saveOrUpdate

请求参数:@RequestBody ApartmentSubmitVo apartmentSubmitVo

返回类型:Result

controller层:

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

请求的参数为ApartmentSubmitVo类型(VO:ViewObject,视图对象),为满足前端显示的要求所创建的VO对象,该对象继承了ApartmentInfo的对象。

我们需要判断apartmentSubmitVo对象的id值是否存在,如果存在则为更新,否则则为新增保存操作。无论是新增保存还是更新操作,我们都需要把基本的公寓信息赋值给VO对象。

更新:

举保存或更新图片的例子来说,更新无论新增还是删除,其实最后返回给我们的结果都是还剩多少张图片,所以为了方便,我们先把所有的与公寓相关的图片先全部删除,最后再全部重新插入

所以MybatisPlus提供的方法并不可以满足我们的需求,需要自己去定义service方法。

更新service实现层:

先判断并删除

@Override
    public void saveOrUpdateApartment(ApartmentSubmitVo apartmentSubmitVo) {
        boolean isupdate=apartmentSubmitVo.getId()!=null;  //判断id是否为空,若为空则是新增,反之为更新
        super.saveOrUpdate(apartmentSubmitVo);  //添加基本公寓信息
        if (isupdate){
            //1.删除图片列表
            LambdaQueryWrapper<GraphInfo> graphInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
            graphInfoLambdaQueryWrapper.eq(GraphInfo::getItemType, ItemType.APARTMENT);
            graphInfoLambdaQueryWrapper.eq(GraphInfo::getItemId,apartmentSubmitVo.getId());
            graphInfoService.remove(graphInfoLambdaQueryWrapper);
            //2.删除配置列表
            LambdaQueryWrapper<ApartmentFacility> apartmentFacilityLambdaQueryWrapper = new LambdaQueryWrapper<>();
            apartmentFacilityLambdaQueryWrapper.eq(ApartmentFacility::getApartmentId,apartmentSubmitVo.getId());
            apartmentFacilityService.remove(apartmentFacilityLambdaQueryWrapper);
            //3.删除标签列表
            LambdaQueryWrapper<ApartmentLabel> apartmentLabelLambdaQueryWrapper = new LambdaQueryWrapper<>();
            apartmentLabelLambdaQueryWrapper.eq(ApartmentLabel::getApartmentId,apartmentSubmitVo.getId());
            apartmentLabelService.remove(apartmentLabelLambdaQueryWrapper);
            //4.删除杂费列表
            LambdaQueryWrapper<ApartmentFeeValue> queryWrapper = new LambdaQueryWrapper<>();
            apartmentFeeValueService.remove(queryWrapper);
        }

再新增

//保存新增
        //1.插入图片列表
        List<GraphVo> graphVoList = apartmentSubmitVo.getGraphVoList();
        if (!CollectionUtils.isEmpty(graphVoList)){  //判断列表是否为空
            ArrayList<GraphInfo> graphInfoList = new ArrayList<>();
            for (GraphVo graphVo : graphVoList) {
                GraphInfo graphInfo = new GraphInfo();
                graphInfo.setItemType(ItemType.APARTMENT);
                graphInfo.setItemId(apartmentSubmitVo.getId());
                graphInfo.setName(graphVo.getName());
                graphInfo.setUrl(graphInfo.getUrl());
                graphInfoList.add(graphInfo);
            }
            graphInfoService.saveBatch(graphInfoList);
        }

        //2.插入配套列表
        List<Long> facilityInfoIdsList = apartmentSubmitVo.getFacilityInfoIds();
        if (!CollectionUtils.isEmpty(facilityInfoIdsList)){
            ArrayList<ApartmentFacility> facilitiesList = new ArrayList<>();
            for (Long facilityId : facilityInfoIdsList) {
                ApartmentFacility apartmentFacility = new ApartmentFacility();
                apartmentFacility.setApartmentId(apartmentSubmitVo.getId());
                apartmentFacility.setFacilityId(facilityId);
                facilitiesList.add(apartmentFacility);
            }
            apartmentFacilityService.saveBatch(facilitiesList);
        }

        //3.插入标签列表
        List<Long> labelIdsList = apartmentSubmitVo.getLabelIds();
        if (!CollectionUtils.isEmpty(labelIdsList)){
            ArrayList<ApartmentLabel> LabelsList = new ArrayList<>();
            for (Long lableId : labelIdsList) {
                ApartmentLabel apartmentLabel = new ApartmentLabel();
                apartmentLabel.setApartmentId(apartmentSubmitVo.getId());
                apartmentLabel.setLabelId(lableId);
                LabelsList.add(apartmentLabel);
            }
            apartmentLabelService.saveBatch(LabelsList);
        }
        //4.插入杂费列表
        List<Long> feeValueIdsList = apartmentSubmitVo.getFeeValueIds();
        if (!CollectionUtils.isEmpty(feeValueIdsList)){
            ArrayList<ApartmentFeeValue> feeValuesList = new ArrayList<>();
            for (Long feeValueId : feeValueIdsList) {
                ApartmentFeeValue feeValue = new ApartmentFeeValue();
                feeValue.setApartmentId(apartmentSubmitVo.getId());
                feeValue.setFeeValueId(feeValueId);
                feeValuesList.add(feeValue);
            }
            apartmentFeeValueService.saveBatch(feeValuesList);
        }

新增保存:

新增保存直接插入VO所需的属性即可。保存代码同上。

@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;

}

二、根据条件分页查询公寓列表接口

接口名称:pageItem

请求方式:Get

请求路径:/admin/apartment/pageItem

请求参数:@RequestParam long current,

                  @RequestParam long size, ApartmentQueryVo queryVo

返回类型:Result<IPage<ApartmentItemVo>>

由于这个接口为分页查询,需要用到MybatisPlus的分页插件,所以我们先在MybatisPlus的配置类里添加分页插件

@Configuration
@MapperScan("com.atguigu.lease.web.*.mapper")
public class MybatisPlusConfiguration {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

controller层:

由于返回值为IPage<ApartmentItemVo>,所以不可以直接使用MybatisPlus提供的service.page()方法,因为service返回的是ApartmentInfo的属性信息,因此我们需要自己定义分页方法pageItem(page,queryVo)。

@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> result= service.pageItem(page,queryVo);
        return Result.ok(result);
    }

service实现层:

@Override
    public IPage<ApartmentItemVo> pageItem(IPage<ApartmentItemVo> page, ApartmentQueryVo queryVo) {
        return apartmentInfoMapper.pageItem(page,queryVo);
    }

Mapper层:

@Mapper
public interface ApartmentInfoMapper extends BaseMapper<ApartmentInfo> {

    IPage<ApartmentItemVo> pageItem(IPage<ApartmentItemVo> page, ApartmentQueryVo queryVo) ;
}

mapper层需要需要自定义Sql语句进行多表查询。公寓信息表、房间信息表和房间租期表多表联查。

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,
                     create_time,
                     update_time,
                     is_deleted
              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

难难难:这一部分是我目前项目实践以来最困难的一部分,难点在于处理表与表之间的联系,还有处理过滤条件过滤掉不需要的记录。还需要撰写动态sql语句来实现选择城市地区信息来查询公寓信息。

Sql解读:以apartment_info表作为主表,连接房间信息和租约信息表。

1.查询公寓的基本信息:

select id,
                     name,
                     introduction,
                     district_id,
                     district_name,
                     city_id,
                     city_name,
                     province_id,
                     province_name,
                     address_detail,
                     latitude,
                     longitude,
                     phone,
                     is_release,
                     create_time,
                     update_time,
                     is_deleted
              from apartment_info

2.这些查询的是房间信息里的公寓id和房间总数,通过以公寓id进行分组,没有被逻辑删除和租约状态为1(已出租)为过滤条件。

select apartment_id,
                     count(*) cnt
              from room_info
              where is_deleted = 0
                and is_release = 1
              group by apartment_id

3.查询租约信息表下,公寓id和已出租房间总数,以公寓id进行分组,没有被逻辑删除和状态为2和5作为过滤条件。

select apartment_id,
                     count(*) cnt
              from lease_agreement
              where is_deleted = 0
                and status in (2, 5)
              group by apartment_id
//租约状态枚举类
SIGNING(1, "签约待确认"),
    SIGNED(2, "已签约"),
    CANCELED(3, "已取消"),
    EXPIRED(4, "已到期"),
    WITHDRAWING(5, "退租待确认"),
    WITHDRAWN(6, "已退租"),
    RENEWING(7, "续约待确认");

4.最终查询字段。通过ai.*字段为公寓信息字段,最后两个字段为总房间数空闲房间数

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

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

接口名称:getDetailById

请求方式:Get

请求路径:/admin/apartment/getDetailById

请求参数:@RequestParam Long id

返回类型:ApartmentDetailVo

controller层:

这个接口仍然是返回VO类型,因此还是不可以直接使用MP的通用service方法,需要自定义service方法实现。

@Operation(summary = "根据ID获取公寓详细信息")
    @GetMapping("getDetailById")
    public Result<ApartmentDetailVo> getDetailById(@RequestParam Long id) {
        ApartmentDetailVo result=service.getDetailById(id);
        return Result.ok(result);
    }

service层:

通过条件查询得到公寓、图片、标签、配套、杂费信息,最后创建ApartmentDetailVo对象并且使用BeanUtils工具类把公寓信息类的基本信息赋值到VO对象上。最后再把查询到的信息赋值到VO对象上。

@Override
    public ApartmentDetailVo getDetailById(Long id) {
        //1.查询公寓信息
        ApartmentInfo apartmentInfo = apartmentInfoMapper.selectById(id);
        //2.查询图片信息
        List<GraphVo> graphVoList=graphInfoMapper.selectListByItemTypeAndId(ItemType.APARTMENT,id);

        //3.查询标签信息
        List<LabelInfo> labelInfoList=labelInfoMapper.selectByApartmentId(id);

        //4.查询配套列表
        List<FacilityInfo> facilityInfoList=facilityInfoMapper.selectByApartmentId(id);

        //5.查询杂费列表
        List<FeeValueVo> feeValueVoList=feeValueMapper.selectListByApartmentId(id);
        //6.组装结果
        ApartmentDetailVo apartmentDetailVo = new ApartmentDetailVo();
        BeanUtils.copyProperties(apartmentInfo,apartmentDetailVo);
        apartmentDetailVo.setGraphVoList(graphVoList);
        apartmentDetailVo.setLabelInfoList(labelInfoList);
        apartmentDetailVo.setFacilityInfoList(facilityInfoList);
        apartmentDetailVo.setFeeValueVoList(feeValueVoList);
        return apartmentDetailVo;
    }

四、根据id删除公寓信息接口

接口名称:removeById

请求方式:Delete

请求路径:/admin/apartment/removeById

请求参数:@RequestParam Long id

返回类型:Result

controller层:

删除公寓信息,需要先删除公寓基本信息以及公寓所属的图片、房间所属的租期信息、标签信息、支付方式信息、属性值、配套信息。

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

service层:

不过要先判断公寓下是否有房间,如果没有房间则终止删除。

@Override
    public void removeApartmentById(Long id) {
        LambdaQueryWrapper<RoomInfo> roomQueryWrapper = new LambdaQueryWrapper<>();
        roomQueryWrapper.eq(RoomInfo::getApartmentId,id);
        Long count = roomInfoMapper.selectCount(roomQueryWrapper);
        if (count>0){
            //终止删除,并响应提示信息
            throw new LeaseException(ResultCodeEnum.DELETE_ERROR);
        }


        //1.删除基本的公寓信息
        super.removeById(id);
        //2.删除租期信息
        LambdaQueryWrapper<RoomLeaseTerm> roomLeaseTermQueryWrapper = new LambdaQueryWrapper<>();
        roomLeaseTermQueryWrapper.eq(RoomLeaseTerm::getRoomId,id);
        roomLeaseTermService.remove(roomLeaseTermQueryWrapper);

        //3.删除支付信息
        LambdaQueryWrapper<RoomPaymentType> roomPaymentTypeQueryWrapper = new LambdaQueryWrapper<>();
        roomPaymentTypeQueryWrapper.eq(RoomPaymentType::getRoomId,id);
        roomPaymentTypeService.remove(roomPaymentTypeQueryWrapper);

        //4.删除房间属性值
        LambdaQueryWrapper<RoomAttrValue> roomAttrValueQueryWrapper = new LambdaQueryWrapper<>();
        roomAttrValueQueryWrapper.eq(RoomAttrValue::getRoomId,id);
        roomAttrValueService.remove(roomAttrValueQueryWrapper);

        //5.删除房间标签
        LambdaQueryWrapper<RoomLabel> roomLabelQueryWrapper = new LambdaQueryWrapper<>();
        roomLabelQueryWrapper.eq(RoomLabel::getRoomId,id);
        roomLabelService.remove(roomLabelQueryWrapper);

        //6.删除房间配套
        LambdaQueryWrapper<RoomFacility> roomFacilityQueryWrapper = new LambdaQueryWrapper<>();
        roomFacilityQueryWrapper.eq(RoomFacility::getRoomId,id);
        roomFacilityService.remove(roomFacilityQueryWrapper);

        //7.删除图片
        LambdaQueryWrapper<GraphInfo> graphInfoQueryWrapper = new LambdaQueryWrapper<>();
        graphInfoQueryWrapper.eq(GraphInfo::getItemType,ItemType.ROOM);
        graphInfoQueryWrapper.eq(GraphInfo::getItemId,id);
        graphInfoService.remove(graphInfoQueryWrapper);

    }

五、根据id修改公寓发布状态接口

接口名称:updateReleaseStatusById

请求方式:Post

请求路径:/admin/apartment/updateReleaseStatusById

请求参数:@RequestParam Long id,

                  @RequestParam ReleaseStatus status

返回类型:Result

controller层:

通过条件查询,匹配参数id与公寓id的公寓记录修改其状态。

@Operation(summary = "根据id修改公寓发布状态")
    @PostMapping("updateReleaseStatusById")
    public Result updateReleaseStatusById(@RequestParam Long id, @RequestParam ReleaseStatus status) {
        LambdaUpdateWrapper<ApartmentInfo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ApartmentInfo::getId,id);
        updateWrapper.set(ApartmentInfo::getIsRelease,status);
        service.update(updateWrapper);
        return Result.ok();
    }

六、根据区县id查询公寓信息列表接口

接口名称:listInfoByDistrictId

请求方式:Get

请求路径:/admin/apartment/listInfoByDistrictId

请求参数:@RequestParam Long id

返回类型:List<ApartmentInfo>

controller层:

@Operation(summary = "根据区县id查询公寓信息列表")
    @GetMapping("listInfoByDistrictId")
    public Result<List<ApartmentInfo>> listInfoByDistrictId(@RequestParam Long id) {
        LambdaQueryWrapper<ApartmentInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ApartmentInfo::getDistrictId,id);
        List<ApartmentInfo> list = service.list(queryWrapper);
        return Result.ok(list);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值