尚庭公寓租赁项目day04

server/2024/10/24 10:21:02/

7.2.2 公寓信息管理

7.2.2.1 房间支付方式管理

房间支付方式管理共有三个接口,分别是查询全部支付方式列表保存或更新支付方式根据ID删除支付方式,下面逐一实现。

首先在PaymentTypeController中注入PaymentTypeService依赖,如下

@Tag(name = "支付方式管理")
@RequestMapping("/admin/payment")
@RestController
public class PaymentTypeController {@Autowiredprivate PaymentTypeService service;
}
1. 查询全部支付方式列表

PaymentTypeController中增加如下内容

@Operation(summary = "查询全部支付方式列表")
@GetMapping("list")
public Result<List<PaymentType>> listPaymentType() {List<PaymentType> list = service.list();return Result.ok(list);
}

知识点

  • 逻辑删除功能

    由于数据库中所有表均采用逻辑删除策略,所以查询数据时均需要增加过滤条件is_deleted=0

    上述操作虽不难实现,但是每个查询接口都要考虑到,也显得有些繁琐。为简化上述操作,可以使用Mybatis-Plus提供的逻辑删除功能,它可以自动为查询操作增加is_deleted=0过滤条件,并将删除操作转为更新语句。具体配置如下,详细信息可参考官方文档。

    • 步骤一:在application.yml中增加如下内容

      mybatis-plus:global-config:db-config:logic-delete-field: flag # 全局逻辑删除的实体字段名(配置后可以忽略不配置步骤二)logic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

    • 步骤二:在实体类中的删除标识字段上增加@TableLogic注解(代码同下)

    • 忽略特定字段

      通常情况下接口响应的Json对象中并不需要create_timeupdate_timeis_deleted等字段,这时只需在实体类中的相应字段添加@JsonIgnore注解,该字段就会在序列化时被忽略。

      具体配置如下,详细信息可参考Jackson官方文档。

      @Data
      public class BaseEntity implements Serializable {@Schema(description = "主键")@TableId(value = "id", type = IdType.AUTO)private Long id;@JsonIgnore@Schema(description = "创建时间")@TableField(value = "create_time", fill = FieldFill.INSERT)private Date createTime;@JsonIgnore@Schema(description = "更新时间")@TableField(value = "update_time", fill = FieldFill.UPDATE)private Date updateTime;@TableLogic@JsonIgnore@Schema(description = "逻辑删除")@TableField("is_deleted")private Byte isDeleted;}

      注意

      逻辑删除功能只对Mybatis-Plus自动注入的sql起效,也就是说,对于手动在Mapper.xml文件配置的sql不会生效,需要单独考虑。

2. 保存或更新支付方式

PaymentTypeController中增加如下内容

    @Operation(summary = "保存或更新支付方式")@PostMapping("saveOrUpdate")public Result saveOrUpdatePaymentType(@RequestBody PaymentType paymentType) {//mp提供了更新或修改的方法service.saveOrUpdate(paymentType);return Result.ok();}

知识点

保存或更新数据时,前端通常不会传入isDeletedcreateTimeupdateTime这三个字段,因此我们需要手动赋值。但是数据库中几乎每张表都有上述字段,所以手动去赋值就显得有些繁琐。为简化上述操作,我们可采取以下措施。

  • is_deleted字段:可将数据库中该字段的默认值设置为0。

  • create_timeupdate_time:可使用mybatis-plus的自动填充功能,所谓自动填充,就是通过统一配置,在插入或更新数据时,自动为某些字段赋值,具体配置如下,详细信息可参考官方文档。

    • 为相关字段配置触发填充的时机,例如create_time需要在插入数据时填充,而update_time需要在更新数据时填充。具体配置如下,观察@TableField注解中的fill属性。

      @Data
      public class BaseEntity implements Serializable {@Schema(description = "主键")@TableId(value = "id", type = IdType.AUTO)private Long id;@JsonIgnore@Schema(description = "创建时间")@TableField(value = "create_time", fill = FieldFill.INSERT)private Date createTime;@JsonIgnore@Schema(description = "更新时间")@TableField(value = "update_time", fill = FieldFill.UPDATE)private Date updateTime;@TableLogic@JsonIgnore@Schema(description = "逻辑删除")@TableField("is_deleted")private Byte isDeleted;}
    • 配置自动填充的内容,具体配置如下

      common模块下创建com.atguigu.lease.common.mybatisplus.MybatisMetaObjectHandler类,内容如下:

      @Component
      public class MybatisMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", Date.class, new Date());}
      ​@Overridepublic void updateFill(MetaObject metaObject) {this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());}
      }

    在做完上述配置后,当写入数据时,Mybatis-Plus会自动将实体对象的create_time字段填充为当前时间,当更新数据时,则会自动将实体对象的update_time字段填充为当前时间。

3. 根据ID删除支付方式

PaymentTypeController中增加如下内容

    @Operation(summary = "根据ID删除支付方式")@DeleteMapping("deleteById")public Result deletePaymentById(@RequestParam Long id) {service.removeById(id);return Result.ok();}

知识点

MybatisPlus逻辑删除功能的使用。

7.2.2.2 房间租期管理

房间租期管理共有三个接口,分别是查询全部租期列表保存或更新租期信息根据ID删除租期,具体实现如下。

LeaseTermController中增加如下内容

package com.atguigu.lease.web.admin.controller.apartment;import com.atguigu.lease.common.result.Result;
import com.atguigu.lease.model.entity.LeaseTerm;
import com.atguigu.lease.web.admin.service.LeaseTermService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;@Tag(name = "租期管理")
@RequestMapping("/admin/term")
@RestController
public class LeaseTermController {@Autowiredprivate LeaseTermService Service;@GetMapping("list")@Operation(summary = "查询全部租期列表")public Result<List<LeaseTerm>> listLeaseTerm() {List<LeaseTerm> list = Service.list();return Result.ok(list);}@PostMapping("saveOrUpdate")@Operation(summary = "保存或更新租期信息")public Result saveOrUpdate(@RequestBody LeaseTerm leaseTerm) {Service.saveOrUpdate(leaseTerm);return Result.ok();}@DeleteMapping("deleteById")@Operation(summary = "根据ID删除租期")public Result deleteLeaseTermById(@RequestParam Long id) {Service.removeById(id);return Result.ok();}
}
7.2.2.3 标签管理

标签管理共有三个接口,分别是[根据类型]查询标签列表保存或更新标签信息根据ID删除标签,下面逐一实现。

首先在LabelController中注入LabelInfoService依赖,如下

@Tag(name = "标签管理")
@RestController
@RequestMapping("/admin/label")
public class LabelController {
​@Autowiredprivate LabelInfoService service;
}
1. [根据类型]查询标签列表

LabelController中增加如下内容

    @Operation(summary = "(根据类型)查询标签列表")@GetMapping("list")public Result<List<LabelInfo>> labelList(@RequestParam(required = false) ItemType type) {LambdaQueryWrapper<LabelInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(type!=null, LabelInfo::getType, type);List<LabelInfo> list = Service.list(queryWrapper);return Result.ok(list);}

知识点

上述接口的功能是根据type(公寓/房间),查询标签列表。由于这个type字段在数据库、实体类、前后端交互的过程中有多种不同的形式,因此在请求和响应的过程中,type字段会涉及到多次类型转换。

首先明确一下type字段的各种形式:

  • 数据库中

    数据库中的type字段为tinyint类型

    +-------------+--------------+
    | Field       | Type         |
    +-------------+--------------+
    | id          | bigint       |
    | type        | tinyint      |
    | name        | varchar(255) |
    | create_time | timestamp    |
    | update_time | timestamp    |
    | is_deleted  | tinyint      |
    +-------------+--------------+
  • 实体类

    实体类中的type字段为ItemType枚举类型

    LabelInfo实体类如下

    @Schema(description = "标签信息表")
    @TableName(value = "label_info")
    @Data
    public class LabelInfo extends BaseEntity {
    ​private static final long serialVersionUID = 1L;
    ​@Schema(description = "类型")@TableField(value = "type")private ItemType type;
    ​@Schema(description = "标签名称")@TableField(value = "name")private String name;
    }

    ItemType枚举类如下

    public enum ItemType {APARTMENT(1, "公寓"),ROOM(2, "房间");private Integer code;private String name;ItemType(Integer code, String name) {this.code = code;this.name = name;}
    }

  • 前后端交互中

    前后端交互所传递的数据中type字段为数字(1/2)。

具体转换过程如下图所示:

  • 请求流程

    说明

    • SpringMVC中的WebDataBinder组件负责将HTTP的请求参数绑定到Controller方法的参数,并实现参数类型的转换。

    • Mybatis中的TypeHandler用于处理Java中的实体对象与数据库之间的数据类型转换。

  • 响应流程

    说明

    • SpringMVC中的HTTPMessageConverter组件负责将Controller方法的返回值(Java对象)转换为HTTP响应体中的JSON字符串,或者将请求体中的JSON字符串转换为Controller方法中的参数(Java对象),例如下一个接口保存或更新标签信息

下面介绍一下每个环节的类型转换原理

  • WebDataBinder枚举类型转换

    WebDataBinder依赖于Converter实现类型转换,若Controller方法声明的@RequestParam参数的类型不是StringWebDataBinder就会自动进行数据类型转换。SpringMVC提供了常用类型的转换器,例如StringIntegerStringDateStringBoolean等等,其中也包括String到枚举类型,但是String到枚举类型的默认转换规则是根据实例名称("APARTMENT")转换为枚举对象实例(ItemType.APARTMENT)。若想实现code属性到枚举对象实例的转换,需要自定义Converter,代码如下,具体内容可参考官方文档。

    • web-admin模块自定义com.atguigu.lease.web.admin.custom.converter.StringToItemTypeConverter

      //将类交给Spring容器管理,之后进行注册交给mvc进行管理
      @Component
      public class StringToItemTypeConverter implements Converter<String, ItemType> {@Overridepublic ItemType convert(String code) {//获取当前枚举类的所有值ItemType[] values = ItemType.values();//使用循环遍历来寻找传入的数据是否有对应的值for (ItemType itemType : values) {if (itemType.getCode().equals(Integer.valueOf(code))){return itemType;}}//如果未找到则返回错误throw new RuntimeException("code:"+code+"错误");}
      }

    • 注册上述的StringToItemTypeConverter,在web-admin模块创建com.atguigu.lease.web.admin.custom.config.WebMvcConfiguration,内容如下:

      @Configuration
      public class WebMvcConfiguration implements WebMvcConfigurer {//将自定义的类型转换器添加到spring容器中@Autowiredprivate StringToItemTypeConverter stringToItemTypeConverter;//添加类型转换器@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(this.stringToItemTypeConverter);}
      }

    但是我们有很多的枚举类型都需要考虑类型转换这个问题,按照上述思路,我们需要为每个枚举类型都定义一个Converter,并且每个Converter的转换逻辑都完全相同,针对这种情况,我们使用ConverterFactory接口更为合适,这个接口可以将同一个转换逻辑应用到一个接口的所有实现类,因此我们可以定义一个BaseEnum接口,然后另所有的枚举类都实现该接口,然后就可以自定义ConverterFactory,集中编写各枚举类的转换逻辑了。具体实现如下:

    • model模块定义com.atguigu.lease.model.enums.BaseEnum接口

      public interface BaseEnum {Integer getCode();String getName();
      }

    • 令所有com.atguigu.lease.model.enums包下的枚举类都实现BaseEnun接口

    • web-admin模块自定义com.atguigu.lease.web.admin.custom.converter.StringToBaseEnumConverterFactory

      package com.atguigu.lease.web.admin.custom.converter;import com.atguigu.lease.model.enums.BaseEnum;
      import org.springframework.core.convert.converter.Converter;
      import org.springframework.core.convert.converter.ConverterFactory;
      import org.springframework.stereotype.Component;@Component
      public class StringToBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {@Overridepublic <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {//使用匿名内部类return new Converter<String, T>() {@Overridepublic T convert(String code) {//获取当前调用的枚举的枚举值T[] enumConstants = targetType.getEnumConstants();for (T enumConstant : enumConstants) {if (enumConstant.getCode().equals(Integer.valueOf(code))){return enumConstant;}}throw new IllegalArgumentException("非法的枚举值:" + code);}};}
      }
      

    • 注册上述的ConverterFactory,在web-admin模块创建com.atguigu.lease.web.admin.custom.config.WebMvcConfiguration,内容如下:

      package com.atguigu.lease.web.admin.custom.config;import com.atguigu.lease.web.admin.custom.converter.StringToBaseEnumConverterFactory;
      import com.atguigu.lease.web.admin.custom.converter.StringToItemTypeConverter;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.format.FormatterRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
      public class WebMvcConfiguration implements WebMvcConfigurer {//将自定义的类型转换器添加到spring容器中
      //    @Autowired
      //    private StringToItemTypeConverter stringToItemTypeConverter;//将自定义的类型转换器添加到spring容器中@Autowiredprivate StringToBaseEnumConverterFactory stringToBaseEnumConverterFactory;//添加类型转换器@Overridepublic void addFormatters(FormatterRegistry registry) {
      //        registry.addConverter(this.stringToItemTypeConverter);//包含所有实现BasaEnum接口的枚举类都可以进行转化registry.addConverterFactory(this.stringToBaseEnumConverterFactory);}
      }
      

      注意:

      最终采用的是ConverterFactory方案,因此StringToItemTypeConverter相关代码可以直接删除。

  • TypeHandler枚举类型转换

    Mybatis预置的TypeHandler可以处理常用的数据类型转换,例如StringIntegerDate等等,其中也包含枚举类型,但是枚举类型的默认转换规则是枚举对象实例(ItemType.APARTMENT)和实例名称("APARTMENT")相互映射。若想实现code属性到枚举对象实例的相互映射,需要自定义TypeHandler

    不过MybatisPlus提供了一个通用的处理枚举类型的TypeHandler。其使用十分简单,只需在ItemType枚举类的code属性上增加一个注解@EnumValue,Mybatis-Plus便可完成从ItemType对象到code属性之间的相互映射,具体配置如下。

    public enum ItemType {
    ​APARTMENT(1, "公寓"),ROOM(2, "房间");
    ​@EnumValueprivate Integer code;private String name;
    ​ItemType(Integer code, String name) {this.code = code;this.name = name;}
    }

  • HTTPMessageConverter枚举类型转换

    HttpMessageConverter依赖于Json序列化框架(默认使用Jackson)。其对枚举类型的默认处理规则也是枚举对象实例(ItemType.APARTMENT)和实例名称("APARTMENT")相互映射。不过其提供了一个注解@JsonValue,同样只需在ItemType枚举类的code属性上增加一个注解@JsonValue,Jackson便可完成从ItemType对象到code属性之间的互相映射。具体配置如下,详细信息可参考Jackson官方文档。

    @Getter
    public enum ItemType {
    ​APARTMENT(1, "公寓"),ROOM(2, "房间");
    ​@EnumValue@JsonValueprivate Integer code;private String name;
    ​ItemType(Integer code, String name) {this.code = code;this.name = name;}
    }

2.保存或更新标签信息

LabelController中增加如下内容

    @Operation(summary = "新增或修改标签信息")@PostMapping("saveOrUpdate")public Result saveOrUpdateLabel(@RequestBody LabelInfo labelInfo) {Service.saveOrUpdate(labelInfo);return Result.ok();}
3. 根据ID删除标签

LabelController中增加如下内容

    @Operation(summary = "根据id删除标签信息")@DeleteMapping("deleteById")public Result deleteLabelById(@RequestParam Long id) {Service.removeById(id);return Result.ok();}
7.2.2.4 配套管理

配套管理共有三个接口,分别是[根据类型]查询配套列表保存或更新配套信息根据ID删除配套,具体实现如下。

FacilityController中增加如下内容

@Tag(name = "配套管理")
@RestController
@RequestMapping("/admin/facility")
public class FacilityController {@Autowiredprivate FacilityInfoService service;@Operation(summary = "[根据类型]查询配套信息列表")@GetMapping("list")public Result<List<FacilityInfo>> listFacility(@RequestParam(required = false) ItemType type) {LambdaQueryWrapper<FacilityInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(type!=null, FacilityInfo::getType, type);List<FacilityInfo> list = service.list(queryWrapper);return Result.ok(list);}@Operation(summary = "新增或修改配套信息")@PostMapping("saveOrUpdate")public Result saveOrUpdate(@RequestBody FacilityInfo facilityInfo) {service.saveOrUpdate(facilityInfo);return Result.ok();}@Operation(summary = "根据id删除配套信息")@DeleteMapping("deleteById")public Result removeFacilityById(@RequestParam Long id) {service.removeById(id);return Result.ok();}}
7.2.2.5 基本属性管理

房间基本属性管理共有五个接口,分别是保存或更新属性名称保存或更新属性值查询全部属性名称和属性值列表根据ID删除属性名称根据ID删除属性值。下面逐一是实现。

首先在AttrController中注入AttrKeyServiceAttrValueService,如下:

@Tag(name = "房间属性管理")
@RestController
@RequestMapping("/admin/attr")
public class AttrController {
​@Autowiredprivate AttrKeyService attrKeyService;
​@Autowiredprivate AttrValueService attrValueService;
}
1. 保存或更新属性名称

AttrController增加如下内容

    @Operation(summary = "新增或更新属性名称")@PostMapping("key/saveOrUpdate")public Result saveOrUpdateAttrKey(@RequestBody AttrKey attrKey) {attrkeyService.saveOrUpdate(attrKey);return Result.ok();}
2. 保存或更新属性值

AttrController中增加如下内容

    @Operation(summary = "新增或更新属性值")@PostMapping("value/saveOrUpdate")public Result saveOrUpdateAttrValue(@RequestBody AttrValue attrValue) {attrValueService.saveOrUpdate(attrValue);return Result.ok();}
3. 查询全部属性名称和属性值列表
  • 查看响应的数据结构

    查看web-admin模块下的com.atguigu.lease.web.admin.vo.attr.AttrKeyVo,内容如下:

    public class AttrKeyVo extends AttrKey {@Schema(description = "属性value列表")private List<AttrValue> attrValueList;
    }

  • 编写Controller层逻辑

    AttrController中添加如下内容

        @Operation(summary = "查询全部属性名称和属性值列表")@GetMapping("list")public Result<List<AttrKeyVo>> listAttrInfo() {List<AttrKeyVo> list =  attrkeyService.listAttrInfo();return Result.ok(list);}

  • 编写Service层逻辑

    AttrKeyService中增加如下内容

        /*** 查询所有的属性表和属性值** @return*/List<AttrKeyVo> listAttrInfo();

    AttrKeyServiceImpl中增加如下内容

        @Autowiredprivate AttrKeyMapper attrKeyMapper;/*** 查询所有的属性表和属性值** @return*/@Overridepublic List<AttrKeyVo> listAttrInfo() {List<AttrKeyVo> list =  attrKeyMapper.listAttrInfo();return list;}

  • 编写Mapper层逻辑

    AttrKeyMapper中增加如下内容

        List<AttrKeyVo> listAttrInfo();

    对应的在AttrKeyMapper.xml中增加如下内容

        <resultMap id="listAttr" 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_key_id"/><result property="name" column="attr_key_name"/><result property="attrKeyId" column="attr_key_id"/></collection></resultMap><select id="listAttrInfo" resultMap="listAttr">select k.id, k.name,  v.id as attr_key_id , v.name as attr_key_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 = 0where k.is_deleted = 0</select>

    另一种实现方式

    List<AttrKeyVo> listAttrInfo();//    AttrValue listAttrvalue(Integer keyid);
<!--   可用方法不同 \-->
<!--    <resultMap id="listAttr" 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" column="id" select="listAttrvalue"/>-->
<!--    </resultMap>--><!--    <select id="listAttrInfo" resultMap="listAttr">-->
<!--        select * from attr_key-->
<!--    </select>-->
<!--    <select id="listAttrvalue" resultType="com.atguigu.lease.model.entity.AttrValue"-->
<!--            parameterType="java.lang.Integer">-->
<!--        select * from attr_value where attr_key_id = #{keyid}-->
<!--    </select>-->

4. 根据ID删除属性名称

AttrController中增加如下内容,注意删除属性名称时,会一并删除其下的所有属性值

    @Operation(summary = "根据id删除属性名称")@DeleteMapping("key/deleteById")public Result removeAttrKeyById(@RequestParam Long attrKeyId) {//删除名字attrkeyService.removeById(attrKeyId);//删除数据LambdaQueryWrapper<AttrValue> wrapper = new LambdaQueryWrapper<>();wrapper.eq(AttrValue::getAttrKeyId, attrKeyId);attrValueService.remove(wrapper);return Result.ok();}
5. 根据ID删除属性值

AttrController中增加如下内容

    @Operation(summary = "根据id删除属性值")@DeleteMapping("value/deleteById")public Result removeAttrValueById(@RequestParam Long id) {attrValueService.removeById(id);return Result.ok();}
7.2.2.6 公寓杂费管理

房间基本属性管理共有五个接口,分别是保存或更新杂费名称保存或更新杂费值查询全部杂费名称和杂费值列表根据ID删除杂费名称根据ID删除杂费值。下面逐一实现

首先在FeeController中注入FeeKeyServiceFeeValueService,如下


@Tag(name = "房间杂费管理")
@RestController
@RequestMapping("/admin/fee")
public class FeeController {@Autowiredprivate FeeKeyService feeKeyService;@Autowiredprivate FeeValueService feeValueService;
}
1. 保存或更新杂费名称

FeeController中增加如下内容

    @Operation(summary = "保存或更新杂费名称")@PostMapping("key/saveOrUpdate")public Result saveOrUpdateFeeKey(@RequestBody FeeKey feeKey) {feeKeyService.saveOrUpdate(feeKey);return Result.ok();}
2. 保存或更新杂费值

FeeController中增加如下内容

    @Operation(summary = "保存或更新杂费值")@PostMapping("value/saveOrUpdate")public Result saveOrUpdateFeeValue(@RequestBody FeeValue feeValue) {feeValueService.saveOrUpdate(feeValue);return Result.ok();}
3. 查询全部杂费名称和杂费值列表
  • 查看响应的数据结构

    查看web-admin模块下创的com.atguigu.lease.web.admin.vo.fee.FeeKeyVo,内容如下

    @Data
    public class FeeKeyVo extends FeeKey {@Schema(description = "杂费value列表")private List<FeeValue> feeValueList;
    }

  • 编写Controller层逻辑

    FeeController中增加如下内容

        @Operation(summary = "查询全部杂费名称和杂费值列表")@GetMapping("list")public Result<List<FeeKeyVo>> feeInfoList() {List<FeeKeyVo> list =  feeKeyService.feeInfoList();return Result.ok(list);}

  • 编写Service层逻辑

    • FeeKeyService中增加如下内容

          List<FeeKeyVo> feeInfoList();

    • FeeKeyServiceImpl中增加如下内容

          @Autowiredprivate FeeKeyMapper feeKeyMapper;@Overridepublic List<FeeKeyVo> feeInfoList() {List<FeeKeyVo> list =   feeKeyMapper.feeInfoList();return list;}

  • 编写Mapper层逻辑

    • FeeKeyMapper中增加如下内容

       List<FeeKeyVo> feeInfoList();

    • FeeKeyMapper.xml中增加如下内容

          <resultMap id="feeVo" type="com.atguigu.lease.web.admin.vo.fee.FeeKeyVo"><id property="id" column="id"/><result property="name" column="name"/><collection property="feeValueList" ofType="com.atguigu.lease.model.entity.FeeValue"><id property="id" column="fee_value_id"/><result property="name" column="fee_value_name"/><result property="unit" column="unit"/><result property="feeKeyId" column="fee_key_id"/></collection></resultMap><select id="feeInfoList" resultMap="feeVo">select k.id, k.name,  v.id fee_value_id, v.name fee_value_name, v.unit, v.fee_key_id from fee_key k left join fee_value v on k.id = v.fee_key_id and v.is_deleted = 0where k.is_deleted = 0;</select>

4. 根据ID删除杂费名称

FeeController中增加如下内容

    @Operation(summary = "根据id删除杂费名称")@DeleteMapping("key/deleteById")@Transactionalpublic Result deleteFeeKeyById(@RequestParam Long feeKeyId) {//删除名称feeKeyService.removeById(feeKeyId);//删除值LambdaQueryWrapper<FeeValue> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(FeeValue::getFeeKeyId,feeKeyId);feeValueService.remove(queryWrapper);return Result.ok();}
5. 根据ID删除杂费值

FeeController中增加如下内容

    @Operation(summary = "根据id删除杂费值")@DeleteMapping("value/deleteById")public Result deleteFeeValueById(@RequestParam Long id) {feeValueService.removeById(id);return Result.ok();}
7.2.2.7 地区信息管理

地区信息管理共有三个接口,分别是查询省份信息列表根据省份ID查询城市信息列表根据城市ID查询区县信息列表,具体实现如下

RegionInfoController中增加如下内容


@Tag(name = "地区信息管理")
@RestController
@RequestMapping("/admin/region")
public class RegionInfoController {@Autowiredprivate ProvinceInfoService provinceInfoService;@Autowiredprivate CityInfoService cityInfoService;@Autowiredprivate DistrictInfoService districtInfoService;@Operation(summary = "查询省份信息列表")@GetMapping("province/list")public Result<List<ProvinceInfo>> listProvince() {List<ProvinceInfo> list = provinceInfoService.list();return Result.ok(list);}@Operation(summary = "根据省份id查询城市信息列表")@GetMapping("city/listByProvinceId")public Result<List<CityInfo>> listCityInfoByProvinceId(@RequestParam Long id) {LambdaQueryWrapper<CityInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(CityInfo::getProvinceId,id);List<CityInfo> list = cityInfoService.list(queryWrapper);return Result.ok(list);}@GetMapping("district/listByCityId")@Operation(summary = "根据城市id查询区县信息")public Result<List<DistrictInfo>> listDistrictInfoByCityId(@RequestParam Long id) {LambdaQueryWrapper<DistrictInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(DistrictInfo::getCityId,id);List<DistrictInfo> list = districtInfoService.list(queryWrapper);return Result.ok(list);}}
7.2.2.8 图片上传管理

由于公寓、房间等实体均包含图片信息,所以在新增或修改公寓、房间信息时,需要上传图片,因此我们需要实现一个上传图片的接口。

1. 图片上传流程

下图展示了新增房间或公寓时,上传图片的流程。

可以看出图片上传接口接收的是图片文件,返回的Minio对象的URL。

2. 图片上传接口开发

下面为该接口的具体实现

  • 配置Minio Client

    • 引入Minio Maven依赖

      common模块pom.xml文件增加如下内容:

      <!--minio依赖--><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId></dependency>

    • 配置Minio相关参数(注意:配置在web-admin下的配置文件中)

      application.yml中配置Minio的endpointaccessKeysecretKeybucketName等参数

      minio:endpoint: http://192.168.21.100:9000access-key: minioadminsecret-key: minioadminbucket-name: lease

      注意:上述<hostname><port>等信息需根据实际情况进行修改。

    • common模块中创建com.atguigu.lease.common.minio.MinioProperties,内容如下

      //用于指示要读取的配置文件的数据
      @ConfigurationProperties(prefix = "minio")
      @Data
      public class MinioProperties {private String endpoint;private String accessKey;private String secretKey;private String bucketName;
      }
      

    • common模块中创建com.atguigu.lease.common.minio.MinioConfiguration,内容如下

      @Configuration//注释类
      @EnableConfigurationProperties(MinioProperties.class)//指定要映入的类将其注册
      public class MinioConfiguration {@Autowiredprivate MinioProperties properties;@Beanpublic MinioClient minioClient() {return MinioClient.builder().endpoint(properties.getEndpoint()).credentials(properties.getAccessKey(), properties.getSecretKey()).build();}
      }//@Configuration
      @EnableConfigurationProperties(MinioProperties.class)
      //@ConfigurationPropertiesScan(basePackages = "com.atguigu.lease.common.minio")
      //public class MinioConfiguration {
      //
      //    @Autowired
      //    private MinioProperties properties;
      //
      //    @Bean
      //    public MinioClient minioClient() {
      //        // 打印配置值以进行调试
      //        System.out.println("Endpoint: " + properties.getEndpoint());
      //        System.out.println("Access Key: " + properties.getAccessKey());
      //        System.out.println("Secret Key: " + properties.getSecretKey());
      //        System.out.println("Bucket Name: " + properties.getBucketName());
      //
      //        return MinioClient.builder().endpoint(properties.getEndpoint()).credentials(properties.getAccessKey(), properties.getSecretKey()).build();
      //    }
      //}

  • 开发图片上传接口

    • 编写Controller层逻辑

      FileUploadController中增加如下内容

      
      @Tag(name = "文件管理")
      @RequestMapping("/admin/file")
      @RestController
      public class FileUploadController {@Autowiredprivate FileService service;@Operation(summary = "上传文件")@PostMapping("upload")public Result<String> upload(@RequestParam MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {String url = service.upload(file);return Result.ok(url);}}
      

      说明:MultipartFile是Spring框架中用于处理文件上传的类,它包含了上传文件的信息(如文件名、文件内容等)。

    • 编写Service层逻辑

      • FileService中增加如下内容

        String upload(MultipartFile file);

      • FileServiceImpl中增加如下内容

        
        @Service
        public class FileServiceImpl implements FileService {@Autowiredprivate MinioConfiguration minioConfiguration;@Autowiredprivate MinioProperties minioProperties;@Overridepublic String upload(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {//判断桶是否存在MinioClient minioClient = minioConfiguration.minioClient();boolean b = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioProperties.getBucketName()).build());if (!b){//桶不存在//创建桶minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioProperties.getBucketName()).build());//给这个桶设置配置文件minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(minioProperties.getBucketName()).config(createBucketPolicyConfig(minioProperties.getBucketName())).build());}//开始上传稿文件//putObject方法可以上传流文件String filename = filename(file);minioClient.putObject(PutObjectArgs.builder().bucket(minioProperties.getBucketName()).contentType(file.getContentType()).stream(file.getInputStream(),file.getSize(),-1).object(filename).build());return String.join("/", minioProperties.getEndpoint(), minioProperties.getBucketName(), filename);}private String filename(MultipartFile file){return new SimpleDateFormat("yyyyMMdd").format(new Date()) +"/"+ UUID.randomUUID()+"-"+file.getOriginalFilename();}private String createBucketPolicyConfig(String bucketName) {return """{"Statement" : [ {"Action" : "s3:GetObject","Effect" : "Allow","Principal" : "*","Resource" : "arn:aws:s3:::%s/*"} ],"Version" : "2012-10-17"}""".formatted(bucketName);}
        }
        

        注意

        上述createBucketPolicyConfig方法的作用是生成用于描述指定bucket访问权限的JSON字符串。最终生成的字符串格式如下,其表示,允许(Allow)所有人(*)获取(s3:GetObject)指定桶(<bucket-name>)的内容。

        {"Statement" : [ {"Action" : "s3:GetObject","Effect" : "Allow","Principal" : "*","Resource" : "arn:aws:s3:::%s/*"} ],"Version" : "2012-10-17"}

        由于公寓、房间的图片为公开信息,所以将其设置为所有人可访问。

      • 异常处理

        • 问题说明

          上述代码只是对MinioClient方法抛出的各种异常进行了捕获,然后打印了异常信息,目前这种处理逻辑,无论Minio是否发生异常,前端在上传文件时,总是会受到成功的响应信息。可按照以下步骤进行操作,查看具体现象

          关闭虚拟机中的Minio服务

          systemctl stop minio

          启动项目,并上传文件,观察接收的响应信息

        • 问题解决思路

          为保证前端能够接收到正常的错误提示信息,应该将Service方法的异常抛出到Controller方法中,然后在Controller方法中对异常进行捕获并处理。具体操作如下

          Service层代码

          @Override
          public String upload(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException{
          ​boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(properties.getBucketName()).build());if (!bucketExists) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(properties.getBucketName()).build());minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(properties.getBucketName()).config(createBucketPolicyConfig(properties.getBucketName())).build());}String filename = new SimpleDateFormat("yyyyMMdd").format(new Date()) +"/" + UUID.randomUUID() + "-" + file.getOriginalFilename();minioClient.putObject(PutObjectArgs.builder().bucket(properties.getBucketName()).stream(file.getInputStream(), file.getSize(), -1).object(filename).contentType(file.getContentType()).build());
          ​return String.join("/",properties.getEndpoint(),properties.getBucketName(),filename);
          }

          Controller层代码

          public Result<String> upload(@RequestParam MultipartFile file) {try {String url = service.upload(file);return Result.ok(url);} catch (Exception e) {e.printStackTrace();return Result.fail();}
          }

        • 全局异常处理

          按照上述写法,所有的Controller层方法均需要增加try-catch逻辑,使用Spring MVC提供的全局异常处理功能,可以将所有处理异常的逻辑集中起来,进而统一处理所有异常,使代码更容易维护。

          具体用法如下,详细信息可参考官方文档:

          common模块中创建com.atguigu.lease.common.exception.GlobalExceptionHandler类,内容如下

          
          //`@ControllerAdvice`用于声明处理全局Controller方法异常的类
          @ControllerAdvice
          public class GlobalExceptionHandler {//注解表明该方法要捕获的异常@ExceptionHandler(Exception.class)//ResponseBody返回数据@ResponseBodypublic Result error(Exception e){e.printStackTrace();return Result.fail();}
          }

          上述代码中的关键注解的作用如下

          @ControllerAdvice用于声明处理全局Controller方法异常的类

          @ExceptionHandler用于声明处理异常的方法,value属性用于声明该方法处理的异常类型

          @ResponseBody表示将方法的返回值作为HTTP的响应体

          注意:

          全局异常处理功能由SpringMVC提供,因此需要在common模块pom.xml中引入如下依赖

                  <!--spring-web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
        • 修改Controller层代码

          由于前文的GlobalExceptionHandler会处理所有Controller方法抛出的异常,因此Controller层就无序关注异常的处理逻辑了,因此Controller层代码可做出如下调整。

          public Result<String> upload(@RequestParam MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {String url = service.upload(file);return Result.ok(url);
          }
7.2.2.9 公寓管理

公寓管理共有六个接口,下面逐一实现。

首先在ApartmentController中注入ApartmentInfoService,如下

@Tag(name = "公寓信息管理")
@RestController
@RequestMapping("/admin/apartment")
public class ApartmentController {
​@Autowiredprivate ApartmentInfoService service;
}
1. 保存或更新公寓信息
  • 查看请求的数据结构

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

    package com.atguigu.lease.web.admin.vo.apartment;import com.atguigu.lease.model.entity.ApartmentInfo;
    import com.atguigu.lease.web.admin.vo.graph.GraphVo;
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Data;import java.util.List;@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层逻辑

    ApartmentController中增加如下内容

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

  • 编写Service层逻辑

    • ApartmentInfoService中增加如下内容

       void saveOrupdateBatchapartment(ApartmentSubmitVo apartmentSubmitVo);

    • ApartmentInfoServiceImpl中增加如下内容

      注意:所需ServiceMapper的注入语句省略未写。

          @Overridepublic void saveOrupdateBatchapartment(ApartmentSubmitVo apartmentSubmitVo) {//先判断是否有id即判断是新增还是修改boolean isUpdate = apartmentSubmitVo.getId()!=null;super.saveOrUpdate(apartmentSubmitVo);if (isUpdate){//1.删除图片列表LambdaQueryWrapper<GraphInfo> graphQueryWrapper = new LambdaQueryWrapper<>();graphQueryWrapper.eq(GraphInfo::getItemType, ItemType.APARTMENT);graphQueryWrapper.eq(GraphInfo::getItemId,apartmentSubmitVo.getId());graphInfoService.remove(graphQueryWrapper);//2.删除配套列表LambdaQueryWrapper<ApartmentFacility> facilityQueryWrapper = new LambdaQueryWrapper<>();facilityQueryWrapper.eq(ApartmentFacility::getApartmentId,apartmentSubmitVo.getId());apartmentFacilityService.remove(facilityQueryWrapper);//3.删除标签列表LambdaQueryWrapper<ApartmentLabel> labelQueryWrapper = new LambdaQueryWrapper<>();labelQueryWrapper.eq(ApartmentLabel::getApartmentId,apartmentSubmitVo.getId());apartmentLabelService.remove(labelQueryWrapper);//4.删除杂费列表LambdaQueryWrapper<ApartmentFeeValue> feeQueryWrapper = new LambdaQueryWrapper<>();feeQueryWrapper.eq(ApartmentFeeValue::getApartmentId,apartmentSubmitVo.getId());apartmentFeeValueService.remove(feeQueryWrapper);}//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(graphVo.getUrl());graphInfoList.add(graphInfo);}graphInfoService.saveBatch(graphInfoList);}//2.插入配套列表List<Long> facilityInfoIdList = apartmentSubmitVo.getFacilityInfoIds();if (!CollectionUtils.isEmpty(facilityInfoIdList)){ArrayList<ApartmentFacility> facilityList = new ArrayList<>();for (Long facilityId : facilityInfoIdList) {ApartmentFacility apartmentFacility = new ApartmentFacility();apartmentFacility.setApartmentId(apartmentSubmitVo.getId());apartmentFacility.setFacilityId(facilityId);facilityList.add(apartmentFacility);}apartmentFacilityService.saveBatch(facilityList);}//3.插入标签列表List<Long> labelIds = apartmentSubmitVo.getLabelIds();if (!CollectionUtils.isEmpty(labelIds)) {List<ApartmentLabel> apartmentLabelList = new ArrayList<>();for (Long labelId : labelIds) {ApartmentLabel apartmentLabel = new ApartmentLabel();apartmentLabel.setApartmentId(apartmentSubmitVo.getId());apartmentLabel.setLabelId(labelId);apartmentLabelList.add(apartmentLabel);}apartmentLabelService.saveBatch(apartmentLabelList);}//4.插入杂费列表List<Long> feeValueIds = apartmentSubmitVo.getFeeValueIds();if (!CollectionUtils.isEmpty(feeValueIds)) {ArrayList<ApartmentFeeValue> apartmentFeeValueList = new ArrayList<>();for (Long feeValueId : feeValueIds) {ApartmentFeeValue apartmentFeeValue = new ApartmentFeeValue();apartmentFeeValue.setApartmentId(apartmentSubmitVo.getId());apartmentFeeValue.setFeeValueId(feeValueId);apartmentFeeValueList.add(apartmentFeeValue);}apartmentFeeValueService.saveBatch(apartmentFeeValueList);}}

2. 根据条件分页查询公寓列表
  • 查看请求和响应的数据结构

    • 请求数据结构

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

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

        @Data
        @Schema(description = "公寓查询实体")
        public class ApartmentQueryVo {@Schema(description = "省份id")private Long provinceId;@Schema(description = "城市id")private Long cityId;@Schema(description = "区域id")private Long districtId;
        }

    • 响应数据结构

      单个公寓信息记录可查看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) {
      //创建一个page对象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中增加如下内容

            @Overridepublic IPage<ApartmentItemVo> pageApartmentItemByQuery(IPage<ApartmentItemVo> page, ApartmentQueryVo queryVo) {return apartmentInfoMapper.pageApartmentItemByQuery(page, queryVo);}

    • 编写Mapper层逻辑

      • ApartmentInfoMapper中增加如下内容

          IPage<ApartmentItemVo> pageApartmentItemByQuery(IPage<ApartmentItemVo> page, ApartmentQueryVo queryVo);

      • ApartmentInfoMapper.xml中增加如下内容

         <select id="pageApartmentItemByQuery" 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_countfrom (select id,name,introduction,district_id,district_name,city_id,city_name,province_id,province_name,address_detail,latitude,longitude,phone,is_releasefrom 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>) aileft join(select apartment_id,count(*) cntfrom room_infowhere is_deleted = 0and is_release = 1group by apartment_id) tcon ai.id = tc.apartment_idleft join(select apartment_id,count(*) cntfrom lease_agreementwhere is_deleted = 0and status in (2, 5)group by apartment_id) ccon ai.id = cc.apartment_id</select>

    注意:

    默认情况下Knife4j为该接口生成的接口文档如下图所示,其中的queryVo参数不方便调试

    可在application.yml文件中增加如下配置,将queryVo做打平处理

    springdoc:default-flat-param-object: true

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

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 detail =  service.getDetailbyId(id);return Result.ok(detail);}

  • 编写Service层逻辑

    • ApartmentInfoService中增加如下内容

      ApartmentDetailVo getApartmentDetailById(Long id);
    • ApartmentInfoServiceImpl中增加如下内容

      @Override
      public ApartmentDetailVo getApartmentDetailById(Long id) {//1.查询ApartmentInfoApartmentInfo apartmentInfo = this.getById(id);if (apartmentInfo == null) {return null;}
      ​//2.查询GraphInfoList<GraphVo> graphVoList = graphInfoMapper.selectListByItemTypeAndId(ItemType.APARTMENT, id);
      ​//3.查询LabelInfoList<LabelInfo> labelInfoList = labelInfoMapper.selectListByApartmentId(id);
      ​//4.查询FacilityInfoList<FacilityInfo> facilityInfoList = facilityInfoMapper.selectListByApartmentId(id);
      ​//5.查询FeeValueList<FeeValueVo> feeValueVoList = feeValueMapper.selectListByApartmentId(id);
      ​ApartmentDetailVo adminApartmentDetailVo = new ApartmentDetailVo();
      ​BeanUtils.copyProperties(apartmentInfo, adminApartmentDetailVo);adminApartmentDetailVo.setGraphVoList(graphVoList);adminApartmentDetailVo.setLabelInfoList(labelInfoList);adminApartmentDetailVo.setFacilityInfoList(facilityInfoList);adminApartmentDetailVo.setFeeValueVoList(feeValueVoList);
      ​return adminApartmentDetailVo;
      }
  • 编写Mapper层逻辑

    • 编写公寓图片查询逻辑

      • GraphInfoMapper中增加如下内容

        List<GraphVo> selectListByItemTypeAndId(ItemType itemType, Long itemId);
      • GraphInfoMapper.xml中增加如下内容

        <select id="selectListByItemTypeAndId" resultType="com.atguigu.lease.web.admin.vo.graph.GraphVo">selectname,urlfrom graph_infowhere is_deleted=0and item_type=#{itemType}and item_id=#{itemId}
        </select>
    • 编写公寓标签查询逻辑

      • LabelInfoMapper中增加如下内容

        List<LabelInfo> selectListByApartmentId(Long id);
      • LabelInfoMapper.xml中增加如下内容

        <select id="selectListByApartmentId" resultType="com.atguigu.lease.model.entity.LabelInfo">select id,type,namefrom label_infowhere is_deleted = 0and id in(select label_idfrom apartment_labelwhere is_deleted = 0and apartment_id = #{id})
        </select>
    • 编写公寓配套查询逻辑

      • FacilityInfoMapper中增加如下内容

        List<FacilityInfo> selectListByApartmentId(Long id);
      • FacilityInfoMapper.xml中增加如下内容

        <select id="selectListByApartmentId" resultType="com.atguigu.lease.model.entity.FacilityInfo">select id,type,name,iconfrom facility_infowhere is_deleted = 0and id in(select facility_idfrom apartment_facilitywhere is_deleted = 0and apartment_id = #{id})
        </select>
    • 编写公寓杂费查询逻辑

      • FeeValueMapper中增加如下内容

        List<FeeValueVo> selectListByApartmentId(Long id);
      • FeeValueMapper.xml中增加如下内容

        <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_nameFROM fee_value fvJOIN fee_key fk ON fv.fee_key_id = fk.idWHERE fv.is_deleted = 0AND fk.is_deleted = 0and fv.id in (select fee_value_idfrom apartment_fee_valuewhere is_deleted = 0and apartment_id = #{id})
        </select>
4. 根据ID删除公寓信息
  • 编写Controller层逻辑

    ApartmentController中增加如下内容

    @Operation(summary = "根据id删除公寓信息")
    @DeleteMapping("removeById")
    public Result removeById(@RequestParam Long id) {
    ​service.removeApartmentById(id);return Result.ok();
    }
  • 编写Service层逻辑

    • ApartmentInfoService中增加如下内容

      void removeApartmentById(Long id);
    • ApartmentInfoServiceImpl中增加如下内容

      @Override
      public void removeApartmentById(Long id) {super.removeById(id);//1.删除GraphInfoLambdaQueryWrapper<GraphInfo> graphQueryWrapper = new LambdaQueryWrapper<>();graphQueryWrapper.eq(GraphInfo::getItemType, ItemType.APARTMENT);graphQueryWrapper.eq(GraphInfo::getItemId, id);graphInfoService.remove(graphQueryWrapper);
      ​//2.删除ApartmentLabelLambdaQueryWrapper<ApartmentLabel> labelQueryWrapper = new LambdaQueryWrapper<>();labelQueryWrapper.eq(ApartmentLabel::getApartmentId, id);apartmentLabelService.remove(labelQueryWrapper);
      ​//3.删除ApartmentFacilityLambdaQueryWrapper<ApartmentFacility> facilityQueryWrapper = new LambdaQueryWrapper<>();facilityQueryWrapper.eq(ApartmentFacility::getApartmentId, id);apartmentFacilityService.remove(facilityQueryWrapper);
      ​//4.删除ApartmentFeeValueLambdaQueryWrapper<ApartmentFeeValue> feeQueryWrapper = new LambdaQueryWrapper<>();feeQueryWrapper.eq(ApartmentFeeValue::getApartmentId, id);apartmentFeeValueService.remove(feeQueryWrapper);
      ​
      }

      知识点

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

      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();}
      ​@Overridepublic 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;
      }

      removeApartmentById方法的最终实现如下

      @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.ADMIN_APARTMENT_DELETE_ERROR);}
      ​//1.删除GraphInfoLambdaQueryWrapper<GraphInfo> graphQueryWrapper = new LambdaQueryWrapper<>();graphQueryWrapper.eq(GraphInfo::getItemType, ItemType.APARTMENT);graphQueryWrapper.eq(GraphInfo::getItemId, id);graphInfoService.remove(graphQueryWrapper);
      ​//2.删除ApartmentLabelLambdaQueryWrapper<ApartmentLabel> labelQueryWrapper = new LambdaQueryWrapper<>();labelQueryWrapper.eq(ApartmentLabel::getApartmentId, id);apartmentLabelService.remove(labelQueryWrapper);
      ​//3.删除ApartmentFacilityLambdaQueryWrapper<ApartmentFacility> facilityQueryWrapper = new LambdaQueryWrapper<>();facilityQueryWrapper.eq(ApartmentFacility::getApartmentId, id);apartmentFacilityService.remove(facilityQueryWrapper);
      ​//4.删除ApartmentFeeValueLambdaQueryWrapper<ApartmentFeeValue> feeQueryWrapper = new LambdaQueryWrapper<>();feeQueryWrapper.eq(ApartmentFeeValue::getApartmentId, id);apartmentFeeValueService.remove(feeQueryWrapper);
      ​//5.删除ApartmentInfosuper.removeById(id);
      }
5. 根据ID修改公寓发布状态

ApartmentController中增加如下内容:

@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();
}
6. 根据区县ID查询公寓信息列表

ApartmentController中增加如下内容:

@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);
}
7.2.2.10 房间管理

房间管理共有六个接口,下面逐一实现

首先在RoomController中注入RoomInfoService,如下

@Tag(name = "房间信息管理")
@RestController
@RequestMapping("/admin/room")
public class RoomController {
​@Autowiredprivate RoomInfoService service;
}
1. 保存或更新房间信息
  • 查看请求的数据结构

    查看web-admin模块中的com.atguigu.lease.web.admin.vo.room.RoomSubmitVo,内容如下

    @Data
    @Schema(description = "房间信息")
    public class RoomSubmitVo extends RoomInfo {
    ​@Schema(description = "图片列表")private List<GraphVo> graphVoList;
    ​@Schema(description = "属性信息列表")private List<Long> attrValueIds;
    ​@Schema(description = "配套信息列表")private List<Long> facilityInfoIds;
    ​@Schema(description = "标签信息列表")private List<Long> labelInfoIds;
    ​@Schema(description = "支付方式列表")private List<Long> paymentTypeIds;
    ​@Schema(description = "可选租期列表")private List<Long> leaseTermIds;
    }
  • 编写Controller层逻辑

    RoomController中增加如下内容

    @Operation(summary = "保存或更新房间信息")
    @PostMapping("saveOrUpdate")
    public Result saveOrUpdate(@RequestBody RoomSubmitVo roomSubmitVo) {service.saveOrUpdateRoom(roomSubmitVo);return Result.ok();
    }
  • 编写Service 层逻辑

    RoomInfoService中增加如下内容

    void saveOrUpdateRoom(RoomSubmitVo roomSubmitVo);

    RoomInfoServiceImpl中增加如下内容

    @Override
    public void saveOrUpdateRoom(RoomSubmitVo roomSubmitVo) {boolean isUpdate = roomSubmitVo.getId() != null;super.saveOrUpdate(roomSubmitVo);
    ​//若为更新操作,则先删除与Room相关的各项信息列表if (isUpdate) {//1.删除原有graphInfoListLambdaQueryWrapper<GraphInfo> graphQueryWrapper = new LambdaQueryWrapper<>();graphQueryWrapper.eq(GraphInfo::getItemType, ItemType.ROOM);graphQueryWrapper.eq(GraphInfo::getItemId, roomSubmitVo.getId());graphInfoService.remove(graphQueryWrapper);
    ​//2.删除原有roomAttrValueListLambdaQueryWrapper<RoomAttrValue> attrQueryMapper = new LambdaQueryWrapper<>();attrQueryMapper.eq(RoomAttrValue::getRoomId, roomSubmitVo.getId());roomAttrValueService.remove(attrQueryMapper);
    ​//3.删除原有roomFacilityListLambdaQueryWrapper<RoomFacility> facilityQueryWrapper = new LambdaQueryWrapper<>();facilityQueryWrapper.eq(RoomFacility::getRoomId, roomSubmitVo.getId());roomFacilityService.remove(facilityQueryWrapper);
    ​//4.删除原有roomLabelListLambdaQueryWrapper<RoomLabel> labelQueryWrapper = new LambdaQueryWrapper<>();labelQueryWrapper.eq(RoomLabel::getRoomId, roomSubmitVo.getId());roomLabelService.remove(labelQueryWrapper);
    ​//5.删除原有paymentTypeListLambdaQueryWrapper<RoomPaymentType> paymentQueryWrapper = new LambdaQueryWrapper<>();paymentQueryWrapper.eq(RoomPaymentType::getRoomId, roomSubmitVo.getId());roomPaymentTypeService.remove(paymentQueryWrapper);
    ​
    ​//6.删除原有leaseTermListLambdaQueryWrapper<RoomLeaseTerm> termQueryWrapper = new LambdaQueryWrapper<>();termQueryWrapper.eq(RoomLeaseTerm::getRoomId, roomSubmitVo.getId());roomLeaseTermService.remove(termQueryWrapper);}
    ​//1.保存新的graphInfoListList<GraphVo> graphVoList = roomSubmitVo.getGraphVoList();if (!CollectionUtils.isEmpty(graphVoList)) {ArrayList<GraphInfo> graphInfoList = new ArrayList<>();for (GraphVo graphVo : graphVoList) {GraphInfo graphInfo = new GraphInfo();graphInfo.setItemType(ItemType.ROOM);graphInfo.setItemId(roomSubmitVo.getId());graphInfo.setName(graphVo.getName());graphInfo.setUrl(graphVo.getUrl());graphInfoList.add(graphInfo);}graphInfoService.saveBatch(graphInfoList);}
    ​//2.保存新的roomAttrValueListList<Long> attrValueIds = roomSubmitVo.getAttrValueIds();if (!CollectionUtils.isEmpty(attrValueIds)) {List<RoomAttrValue> roomAttrValueList = new ArrayList<>();for (Long attrValueId : attrValueIds) {RoomAttrValue roomAttrValue = RoomAttrValue.builder().roomId(roomSubmitVo.getId()).attrValueId(attrValueId).build();roomAttrValueList.add(roomAttrValue);}roomAttrValueService.saveBatch(roomAttrValueList);}
    ​//3.保存新的facilityInfoListList<Long> facilityInfoIds = roomSubmitVo.getFacilityInfoIds();if (!CollectionUtils.isEmpty(facilityInfoIds)) {List<RoomFacility> roomFacilityList = new ArrayList<>();for (Long facilityInfoId : facilityInfoIds) {RoomFacility roomFacility = RoomFacility.builder().roomId(roomSubmitVo.getId()).facilityId(facilityInfoId).build();roomFacilityList.add(roomFacility);}roomFacilityService.saveBatch(roomFacilityList);}
    ​//4.保存新的labelInfoListList<Long> labelInfoIds = roomSubmitVo.getLabelInfoIds();if (!CollectionUtils.isEmpty(labelInfoIds)) {ArrayList<RoomLabel> roomLabelList = new ArrayList<>();for (Long labelInfoId : labelInfoIds) {RoomLabel roomLabel = RoomLabel.builder().roomId(roomSubmitVo.getId()).labelId(labelInfoId).build();roomLabelList.add(roomLabel);}roomLabelService.saveBatch(roomLabelList);}
    ​//5.保存新的paymentTypeListList<Long> paymentTypeIds = roomSubmitVo.getPaymentTypeIds();if (!CollectionUtils.isEmpty(paymentTypeIds)) {ArrayList<RoomPaymentType> roomPaymentTypeList = new ArrayList<>();for (Long paymentTypeId : paymentTypeIds) {RoomPaymentType roomPaymentType = RoomPaymentType.builder().roomId(roomSubmitVo.getId()).paymentTypeId(paymentTypeId).build();roomPaymentTypeList.add(roomPaymentType);}roomPaymentTypeService.saveBatch(roomPaymentTypeList);}
    ​//6.保存新的leaseTermListList<Long> leaseTermIds = roomSubmitVo.getLeaseTermIds();if (!CollectionUtils.isEmpty(leaseTermIds)) {ArrayList<RoomLeaseTerm> roomLeaseTerms = new ArrayList<>();for (Long leaseTermId : leaseTermIds) {RoomLeaseTerm roomLeaseTerm = RoomLeaseTerm.builder().roomId(roomSubmitVo.getId()).leaseTermId(leaseTermId).build();roomLeaseTerms.add(roomLeaseTerm);}roomLeaseTermService.saveBatch(roomLeaseTerms);}
    }
2. 根据条件分页查询房间列表
  • 查看请求和响应的数据结构

    • 请求数据结构

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

      • RoomQueryVo为房间的查询条件,详细结构如下:

        @Schema(description = "房间查询实体")
        @Data
        public class RoomQueryVo {
        ​@Schema(description = "省份Id")private Long provinceId;
        ​@Schema(description = "城市Id")private Long cityId;
        ​@Schema(description = "区域Id")private Long districtId;
        ​@Schema(description = "公寓Id")private Long apartmentId;
        }
    • 响应数据结构

      单个房间信息记录可查看com.atguigu.lease.web.admin.vo.room.RoomItemVo,内容如下:

      @Data
      @Schema(description = "房间信息")
      public class RoomItemVo extends RoomInfo {
      ​@Schema(description = "租约结束日期")private Date leaseEndDate;
      ​@Schema(description = "当前入住状态")private Boolean isCheckIn;
      ​@Schema(description = "所属公寓信息")private ApartmentInfo apartmentInfo;
      }
  • 编写Controller层逻辑

    RoomController中增加如下内容

    @Operation(summary = "根据条件分页查询房间列表")
    @GetMapping("pageItem")
    public Result<IPage<RoomItemVo>> pageItem(@RequestParam long current, @RequestParam long size, RoomQueryVo queryVo) {IPage<RoomItemVo> page = new Page<>(current, size);IPage<RoomItemVo> result = service.pageRoomItemByQuery(page, queryVo);return Result.ok(result);
    }
  • 编写Service 层逻辑

    • RoomInfoService中增加如下内容

      IPage<RoomItemVo> pageRoomItemByQuery(IPage<RoomItemVo> page, RoomQueryVo queryVo);
    • RoomInfoServiceImpl中增加如下内容

      @Override
      public IPage<RoomItemVo> pageRoomItemByQuery(IPage<RoomItemVo> page, RoomQueryVo queryVo) {return roomInfoMapper.pageRoomItemByQuery(page, queryVo);
      }
  • 编写Mapper层逻辑

    • RoomInfoMapper中增加如下内容

      IPage<RoomItemVo> pageRoomItemByQuery(IPage<RoomItemVo> page, RoomQueryVo queryVo);
    • RoomInfoMapper.xml中增加如下内容

      <resultMap id="RoomItemVoMap" type="com.atguigu.lease.web.admin.vo.room.RoomItemVo" autoMapping="true"><id property="id" column="id"/><association property="apartmentInfo" javaType="com.atguigu.lease.model.entity.ApartmentInfo" autoMapping="true"><id property="id" column="apart_id"/><result property="isRelease" column="apart_is_release"/></association>
      </resultMap>
      ​
      <select id="pageRoomItemByQuery" resultMap="RoomItemVoMap">select ri.id,ri.room_number,ri.rent,ri.apartment_id,ri.is_release,la.room_id is not null is_check_in,la.lease_end_date,ai.id                  apart_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          apart_is_releasefrom room_info rileft join lease_agreement laon ri.id = la.room_idand la.is_deleted = 0and la.status in (2,5)left join apartment_info aion ri.apartment_id = ai.idand ai.is_deleted = 0<where>ri.is_deleted = 0<if test="queryVo.provinceId != null">apart.province_id = #{queryVo.provinceId}</if><if test="queryVo.cityId != null">and apart.city_id = #{queryVo.cityId}</if><if test="queryVo.districtId != null">and apart.district_id = #{queryVo.districtId}</if><if test="queryVo.apartmentId != null">and apartment_id = #{queryVo.apartmentId}</if></where>
      </select>
3. 根据ID获取房间详细信息
  • 查看响应数据结构

    查看web-admin下的com.atguigu.lease.web.admin.vo.room.RoomDetailVo,内容如下

    @Schema(description = "房间信息")
    @Data
    public class RoomDetailVo extends RoomInfo {
    ​@Schema(description = "所属公寓信息")private ApartmentInfo apartmentInfo;
    ​@Schema(description = "图片列表")private List<GraphVo> graphVoList;
    ​@Schema(description = "属性信息列表")private List<AttrValueVo> attrValueVoList;
    ​@Schema(description = "配套信息列表")private List<FacilityInfo> facilityInfoList;
    ​@Schema(description = "标签信息列表")private List<LabelInfo> labelInfoList;
    ​@Schema(description = "支付方式列表")private List<PaymentType> paymentTypeList;
    ​@Schema(description = "可选租期列表")private List<LeaseTerm> leaseTermList;
    }
  • 编写Controller层逻辑

    RoomController中增加如下内容

    @Operation(summary = "根据id获取房间详细信息")
    @GetMapping("getDetailById")
    public Result<RoomDetailVo> getDetailById(@RequestParam Long id) {RoomDetailVo roomInfo = service.getRoomDetailById(id);return Result.ok(roomInfo);
    }
  • 编写Service 层逻辑

    • RoomInfoService中增加如下内容

      RoomDetailVo getRoomDetailById(Long id);
    • RoomInfoServiceImpl中增加如下内容

      @Override
      public RoomDetailVo getRoomDetailById(Long id) {
      ​//1.查询RoomInfoRoomInfo roomInfo = roomInfoMapper.selectById(id);
      ​//2.查询所属公寓信息ApartmentInfo apartmentInfo = apartmentInfoMapper.selectById(roomInfo.getApartmentId());
      ​//3.查询graphInfoListList<GraphVo> graphVoList = graphInfoMapper.selectListByItemTypeAndId(ItemType.ROOM, id);
      ​//4.查询attrValueListList<AttrValueVo> attrvalueVoList = attrValueMapper.selectListByRoomId(id);
      ​//5.查询facilityInfoListList<FacilityInfo> facilityInfoList = facilityInfoMapper.selectListByRoomId(id);
      ​//6.查询labelInfoListList<LabelInfo> labelInfoList = labelInfoMapper.selectListByRoomId(id);
      ​//7.查询paymentTypeListList<PaymentType> paymentTypeList = paymentTypeMapper.selectListByRoomId(id);
      ​//8.查询leaseTermListList<LeaseTerm> leaseTermList = leaseTermMapper.selectListByRoomId(id);
      ​
      ​RoomDetailVo adminRoomDetailVo = new RoomDetailVo();BeanUtils.copyProperties(roomInfo, adminRoomDetailVo);
      ​adminRoomDetailVo.setApartmentInfo(apartmentInfo);adminRoomDetailVo.setGraphVoList(graphVoList);adminRoomDetailVo.setAttrValueVoList(attrvalueVoList);adminRoomDetailVo.setFacilityInfoList(facilityInfoList);adminRoomDetailVo.setLabelInfoList(labelInfoList);adminRoomDetailVo.setPaymentTypeList(paymentTypeList);adminRoomDetailVo.setLeaseTermList(leaseTermList);
      ​return adminRoomDetailVo;
      }
  • 编写Mapper层逻辑

    • 编写房间属性查询逻辑

      • AttrValueMapper中增加如下内容

        List<AttrValueVo> selectListByRoomId(Long id);
      • AttrValueMapper.xml中增加如下内容

        <select id="selectListByRoomId" resultType="com.atguigu.lease.web.admin.vo.attr.AttrValueVo">select v.id,v.name,v.attr_key_id,k.name attr_key_namefrom attr_value vjoin attr_key k on v.attr_key_id = k.idwhere v.is_deleted = 0and k.is_deleted = 0and v.id in (select attr_value_idfrom room_attr_valuewhere is_deleted = 0and room_id = #{id})
        </select>
    • 编写房间配套查询逻辑

      • FacilityInfoMapper中增加如下内容

        List<FacilityInfo> selectListByRoomId(Long id);
      • FacilityInfoMapper.xml中增加如下内容

        <select id="selectListByRoomId" resultType="com.atguigu.lease.model.entity.FacilityInfo">select id,type,name,iconfrom facility_infowhere is_deleted = 0and id in(select facility_idfrom room_facilitywhere is_deleted = 0and room_id = #{id})
        </select>
    • 编写房间标签查询逻辑

      • LabelInfoMapper中增加如下内容

        List<LabelInfo> selectListByRoomId(Long id);
      • LabelInfoMapper.xml中增加如下内容

        <select id="selectListByRoomId" resultType="com.atguigu.lease.model.entity.LabelInfo">select id,type,namefrom label_infowhere is_deleted = 0and id in(select label_idfrom room_labelwhere is_deleted = 0and room_id = #{id})
        </select>
    • 编写房间可选支付方式查询逻辑

      • PaymentTypeMapper中增加如下内容

        List<PaymentType> selectListByRoomId(Long id);
      • PaymentTypeMapper.xml中增加如下内容

        <select id="selectListByRoomId" resultType="com.atguigu.lease.model.entity.PaymentType">select id,name,pay_month_count,additional_infofrom payment_typewhere is_deleted = 0and id in(select payment_type_idfrom room_payment_typewhere is_deleted = 0and room_id = #{id})
        </select> 
    • 编写房间可选租期查询逻辑

      • Mapper中增加如下内容

        List<LeaseTerm> selectListByRoomId(Long id);
      • Mapper.xml中增加如下内容

        <select id="selectListByRoomId" resultType="com.atguigu.lease.model.entity.LeaseTerm">select id,month_count,unitfrom lease_termwhere is_deleted = 0and id in (select lease_term_idfrom room_lease_termwhere is_deleted = 0and room_id = #{id})
        </select>
4. 根据ID删除房间信息
  • 编写Controller层逻辑

    RoomController中增加如下内容

    @Operation(summary = "根据id删除房间信息")
    @DeleteMapping("removeById")
    public Result removeById(@RequestParam Long id) {service.removeRoomById(id);return Result.ok();
    }
  • 编写Service 层逻辑

    • RoomInfoService中增加如下内容

      void removeRoomById(Long id);
    • RoomInfoServiceImpl中增加如下内容

      @Override
      public void removeRoomById(Long id) {//1.删除RoomInfosuper.removeById(id);
      ​//2.删除graphInfoListLambdaQueryWrapper<GraphInfo> graphQueryWrapper = new LambdaQueryWrapper<>();graphQueryWrapper.eq(GraphInfo::getItemType, ItemType.ROOM);graphQueryWrapper.eq(GraphInfo::getItemId, id);graphInfoService.remove(graphQueryWrapper);
      ​//3.删除attrValueListLambdaQueryWrapper<RoomAttrValue> attrQueryWrapper = new LambdaQueryWrapper<>();attrQueryWrapper.eq(RoomAttrValue::getRoomId, id);roomAttrValueService.remove(attrQueryWrapper);
      ​//4.删除facilityInfoListLambdaQueryWrapper<RoomFacility> facilityQueryWrapper = new LambdaQueryWrapper<>();facilityQueryWrapper.eq(RoomFacility::getRoomId, id);roomFacilityService.remove(facilityQueryWrapper);
      ​//5.删除labelInfoListLambdaQueryWrapper<RoomLabel> labelQueryWrapper = new LambdaQueryWrapper<>();labelQueryWrapper.eq(RoomLabel::getRoomId, id);roomLabelService.remove(labelQueryWrapper);
      ​//6.删除paymentTypeListLambdaQueryWrapper<RoomPaymentType> paymentQueryWrapper = new LambdaQueryWrapper<>();paymentQueryWrapper.eq(RoomPaymentType::getRoomId, id);roomPaymentTypeService.remove(paymentQueryWrapper);
      ​//7.删除leaseTermListLambdaQueryWrapper<RoomLeaseTerm> termQueryWrapper = new LambdaQueryWrapper<>();termQueryWrapper.eq(RoomLeaseTerm::getRoomId, id);roomLeaseTermService.remove(termQueryWrapper);
      }
5. 根据id修改房间发布状态

RoomController中增加如下内容

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

RoomController中增加如下内容

@GetMapping("listBasicByApartmentId")
@Operation(summary = "根据公寓id查询房间列表")
public Result<List<RoomInfo>> listBasicByApartmentId(Long id) {LambdaQueryWrapper<RoomInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(RoomInfo::getApartmentId, id);queryWrapper.eq(RoomInfo::getIsRelease, ReleaseStatus.RELEASED);List<RoomInfo> roomInfoList = service.list(queryWrapper);return Result.ok(roomInfoList);
}

7.2.3 租赁管理


http://www.ppmy.cn/server/134416.html

相关文章

HarmonyOS开发(状态管理,页面路由,动画)

官网 https://developer.huawei.com/consumer/cn/ 一、状态管理 在声明式UI中&#xff0c;是以状态驱动视图更新 1.State 状态(State)&#xff1a;指驱动视图更新的数据&#xff0c;被装饰器标记的变量 视图(View)&#xff1a;基于UI描述渲染得到用户界面 说明 State装饰…

实战 | 记一次实战中SelfXSS+CSRF+越权漏洞的组合拳

0x01 前言 在渗透测试中&#xff0c;经常能够遇到这样一种XSS漏洞&#xff0c;它通常存在于比较隐私的个人信息配置等等功能中&#xff0c;有一个非常鲜明的特点就是“只有自己可见&#xff0c;别人不可见”&#xff0c;XSS漏洞只能攻击自己自然是毫无价值的&#xff0c;因此此…

报表工具怎么选?山海鲸VS帆软,哪个更适合你?

概述 在国产报表软件市场中&#xff0c;山海鲸报表和帆软这两款工具都占有一席之地&#xff0c;许多企业在选择报表工具时常常在它们之间徘徊。然而&#xff0c;随着企业对数据分析需求的不断增长和复杂化&#xff0c;如何选取一款高效、易用且性价比高的报表工具&#xff0c;…

[deadlock]死锁导致的设备登录无响应问题

[deadlock]死锁导致的设备登录无响应问题 一、问题现象二、初步观察三、继续深挖查看netlink相关信息查看warnd进程栈 四、再接再厉查看warnd 用户栈 后记 一、问题现象 实验室一台压力测试设备突然无法登录&#xff0c;无论web页面&#xff0c;ssh或者telnet登录&#xff0c;…

跳表(Skip List)介绍

跳表&#xff08;Skip List&#xff09;介绍 跳表是一种用于有序数据的高效数据结构&#xff0c;结合了链表和分层结构的优点&#xff0c;主要用于快速查找、插入和删除操作。跳表由William Pugh在1989年提出&#xff0c;作为一种简单而有效的替代平衡树的数据结构&#xff0c…

oracle 数据库 迁移到 pgsql,防止出现重大bug 回滚到 oracle,需要同步数据如何处理?

oracle 数据库 迁移到 pgsql&#xff0c;防止出现重大bug 回滚到 oracle&#xff0c;需要同步数据如何处理&#xff1f; 用工具 &#xff0c; 出现大问题&#xff0c; 直接把pgsql 数据库同步到新的oracle 数据库

深入理解 WPF 数据绑定机制

一、引言 在现代软件开发中&#xff0c;创建具有良好用户体验的图形用户界面&#xff08;GUI&#xff09;至关重要。WPF 作为一种先进的 GUI 技术&#xff0c;提供了强大的数据绑定机制&#xff0c;使得开发人员能够轻松地将数据与用户界面元素进行关联&#xff0c;实现数据的…

Spark广播变量(类似小表广播)

广播变量&#xff1a; 举例&#xff1a;取前十的学生信息&#xff0c;处理学生分数信息得到前十学生的学号的数组&#xff0c;通过广播将这个变量广播出去 代码展示如下&#xff1a; import org.apache.spark.broadcast.Broadcast import org.apache.spark.rdd.RDD import o…