摘要:
主要记录了web-admin模块下与公寓有关的几个较难,且知识点较多的几个接口的实现,对这些接口的知识点进行了简单的介绍,巩固有关的知识体系。
一,公寓信息管理所有接口
1,保存或更新公寓信息
众嗦粥滋,mybatisplus提供了非常多的通用mapper,其中就有saveOrUpdate()用于保存或更新。但是就业务场景来说要保存的数据往往不止操作一张表,例如这个接口保存的是ApartmentSubmitVo(继承了ApartmentInfo类)而不是单纯的ApartmentInfo类,所以我们必须自定义方法来实现保存或更新公寓信息。
(1)实现类中调用saveOrUpdate()保存ApartmentSubmitVo中的ApartmentInfo数据到表
boolean isUpdate = apartmentSubmitVo.getId() != null;
super.saveOrUpdate(apartmentSubmitVo);
(2)这里采用的更新策略是删除原有的信息,重新插入新的信息,是否更新的判断是isUpdate
LambdaQueryWrapper<GraphInfo> graphQueryWrapper = new LambdaQueryWrapper<>();
//设置查询条件,itemType为APARTMENT,itemId为apartmentSubmitVo.getId()
graphQueryWrapper.eq(GraphInfo::getItemType, ItemType.APARTMENT);
graphQueryWrapper.eq(GraphInfo::getItemId, apartmentSubmitVo.getId());
//调用graphInfoService的remove方法,删除符合条件的记录
graphInfoService.remove(graphQueryWrapper);
List<GraphVo> graphVoList = apartmentSubmitVo.getGraphVoList();
if (!CollectionUtils.isEmpty(graphVoList)) {
ArrayList<GraphInfo> graphInfoList = new ArrayList<>();
//遍历图片VO集合
for (GraphVo graphVo : graphVoList) {
//创建一个新的GraphInfo对象
GraphInfo graphInfo = new GraphInfo();
graphInfo.setItemType(ItemType.APARTMENT);
graphInfo.setItemId(apartmentSubmitVo.getId());
graphInfo.setName(graphVo.getName());
graphInfo.setUrl(graphVo.getUrl());
graphInfoList.add(graphInfo);
}
graphInfoService.saveBatch(graphInfoList);
}
(3)实现时多思考:为什么更新判断在通用保存前?得到数据集合时是不是空?
2, 根据条件分页查询公寓列表
分页查询三步走:构造page对象,自定义查询方法,返回数据
Page<ApartmentItemVo> page = new Page<>(current,size);
//调用service.pageItem方法,查询分页数据,传入构造的page和查询条件queryVo
IPage<ApartmentItemVo> result = service.pageItem(page, queryVo);
return Result.ok(result);
如果查询涉及到多表,一般采用手写sql来完成查询,观察查询返回类的字段:公寓基本信息(条件查询) + 房间总数 + 空闲房间数(房间总数-已签约房间数)
(1)根据条件查询公寓基本信息作为子表ai
(2)根据公寓id在room_info表中查询出对应的房间总数cnt
(3)根据公寓id在lease_agreement表中查询出已签约房间数cnt
(4)主表公寓信息来自子表ai,左连接上ifnull(tc.cnt,0)和ifnull(tc.cnt,0) - ifnull(cc.cnt,0)
这里采用ifnull是为了避免出现,如果房间总数为null,不仅会使得返回数据为null还会因为运算导致其他字段为null。
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
3,根据ID获取公寓详细信息
同样,观察要求返回的类ApartmentDetailVo(继承ApartmentInfo),也是需要获取多张表的数据,所以自定义实现方法在Java代码中编写逻辑:
public ApartmentDetailVo queryApartmentDetail(Long id) {
//1,查询公寓信息
ApartmentInfo apartmentInfo = apartmentInfoMapper.selectById(id);
//2,查询图片列表
List<GraphVo> GraphVoList = graphInfoMapper.selectListByItemTypeAndId(id, ItemType.APARTMENT);
//3,查询标签列表
List<LabelInfo> labelInfoList = labelInfoMapper.selectAllLabelsById(id);
//4,查询配套列表
List<FacilityInfo> facilityInfoList = facilityInfoMapper.selectFacilityById(id);
//5,查询杂费列表
List<FeeValueVo> feeValueVoList = feeValueMapper.seelctListById(id);
ApartmentDetailVo apartmentDetailVo = new ApartmentDetailVo();
BeanUtils.copyProperties(apartmentInfo, apartmentDetailVo);
apartmentDetailVo.setGraphVoList(GraphVoList);
apartmentDetailVo.setLabelInfoList(labelInfoList);
apartmentDetailVo.setFacilityInfoList(facilityInfoList);
apartmentDetailVo.setFeeValueVoList(feeValueVoList);
return apartmentDetailVo;
}
4,根据公寓id删除公寓所有信息
这里需要我们思考,如果公寓下有房间存在,我们该怎么处理?所以在删除之前还需要判断该公寓下的房间是否大于0,如果大于0就抛出异常,同时中止删除操作
LambdaQueryWrapper<RoomInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RoomInfo::getApartmentId, id);
Long count = roomInfoMapper.selectCount(wrapper);
if(count > 0){
throw new LeaseException(ResultCodeEnum.ADMIN_APARTMENT_DELETE_ERROR);
}
5,自定义异常和全局异常处理
(1)通常在实际开发场景中,都会通过全局异常处理器来处理各类异常,同时可以自定义你自己需要的异常,且在自定义异常中我们一般会接收一个状态枚举(包含code状态码和message错误信息)
@Data
//自定义异常类LeaseException
public class LeaseException extends RuntimeException {
private Integer code;
//ResultCodeEnum返回结果状态枚举
public LeaseException(ResultCodeEnum resultCodeEnum) {
super(resultCodeEnum.getMessage());
this.code=resultCodeEnum.getCode();
}
}
(2)定义全局处理LeaseException异常的方法
小tips:这里两个处理器,一个默认处理所有异常,一个是leaseException,那么出现异常会怎么执行呢?答案是匹配度高的优先执行
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handler(Exception e){
e.printStackTrace();
return Result.fail();
}
//定义全局处理LeaseException异常的方法
@ExceptionHandler(LeaseException.class)
//对响应体中的异常进行处理
@ResponseBody
public Result handler(LeaseException e){
e.printStackTrace();
//返回前端异常信息
return Result.fail(e.getCode(),e.getMessage());
}
}
二,房间属性管理
1,查询全部属性名称和属性值列表接口
(1)观察返回值为List<AttrKeyVo>包含AttrKeyVo(继承AttrKey)的List集合,一个key可以对应多个value,所以AttrKeyVo中包含List<AttrValue> 。
(2)因为需要集合映射,这里引入一个新的知识点<resultMap>和<collection>标签,简单来说<resultMap>就是为了处理多张表的同名字段映射到一个类中混淆的情况,<collection>则是对类中集合的单独处理,可以开启autoMapping=true开启自动映射而无需手动映射
<resultMap id="AttrKeyVoMap" type="com.atguigu.lease.web.admin.vo.attr.AttrKeyVo">
<id property="id" column="id"/>
<result property="name" column="name"/>
<collection property="attrValueList" ofType="com.atguigu.lease.model.entity.AttrValue">
<id property="id" column="attr_value_id"/>
<result property="name" column="attr_value_name"/>
<result property="attrKeyId" column="attr_key_id"/>
</collection>
</resultMap>
<select id="listAttrInfo" resultMap="AttrKeyVoMap" >
select k.id,
k.name,
v.id as attr_value_id,
v.name as attr_value_name,
v.attr_key_id
from attr_key k
left join attr_value v
on k.id = v.attr_key_id and v.is_deleted = 0
where k.is_deleted = 0
</select>
三,配套设施管理
1,[根据类型]查询配套信息列表接口
接口通过@RequestParam(required = false)标明ItemType为非必需的值,所以在查询条件wrapper构造上引入条件判断type!=null,表示不为空才执行该条件,否则不执行这个条件
@GetMapping("list")
public Result<List<FacilityInfo>> listFacility(@RequestParam(required = false) ItemType type) {
LambdaQueryWrapper<FacilityInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(type!=null,FacilityInfo::getType, type);
List<FacilityInfo> list = facilityInfoService.list(wrapper);
return Result.ok(list);
}