房间租期管理

1. 找到对应的数据库

  • 房间租期数据库表lease_term

2. 设计接口

接口1:查询全部租期列表

1
2
3
4
5
6
@GetMapping("list")
@Operation(summary = "查询全部租期列表")
public Result<List<LeaseTerm>> listLeaseTerm() {
List<LeaseTerm> list = leaseTermService.list();
return Result.ok(list);
}

接口2:保存或更新租期信息

1
2
3
4
5
6
7
8
9
10
@PostMapping("saveOrUpdate")
@Operation(summary = "保存或更新租期信息")
public Result saveOrUpdate(@RequestBody LeaseTerm leaseTerm) {
boolean saveOrUpdate = leaseTermService.saveOrUpdate(leaseTerm);
if (saveOrUpdate) {
return Result.ok();
} else {
return Result.fail();
}
}

接口3:根据ID删除租期

1
2
3
4
5
6
7
8
9
10
@DeleteMapping("deleteById")
@Operation(summary = "根据ID删除租期")
public Result deleteLeaseTermById(@RequestParam Long id) {
boolean remove = leaseTermService.removeById(id);
if (remove) {
return Result.ok();
} else {
return Result.fail();
}
}

标签管理

1. 找到对应的数据库

  • 标签管理数据库表label_info

2. 设计接口

接口1:(根据类型)查询标签列表

1
2
3
4
5
6
7
8
@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 = labelInfoService.list(queryWrapper);
return Result.ok(list);
}
  • 运行出错:400,参数类型不匹配

1
2026-07-04T19:30:48.558+08:00  WARN 20000 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'com.atguigu.lease.model.enums.ItemType'; Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam com.atguigu.lease.model.enums.ItemType] for value '2']
  • 原因:传递参数值为1或2,而方法需要美剧类型ItemType,1/2转换成枚举类型时出错

  • 解决方法:

    • 方式一:不使用枚举类型,使用String或Integer
    • 方式二:编写转换器,将1/2转换成枚举类型
  • 自定义转换器解决:

    • 前端传递 type 参数时,通过 SpringMVC 的 WebDataBinderConverter,将请求参数转换为对应的枚举类型。
    • 数据库查询时,通过 MyBatis 的 TypeHandler,将枚举对象中的 type 值转换为数据库字段值,从而拼接 SQL 条件进行查询。
  • 代码实现:

    定义转换器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Component
    public class StringToBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {

    @Override
    public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {

    return new Converter<String, T>() {
    @Override
    public T convert(String code) {
    T[] enumConstants = targetType.getEnumConstants();
    for (T enumConstant : enumConstants) {
    if (enumConstant.getCode().equals(Integer.valueOf(code))) {
    return enumConstant;
    }
    }
    throw new IllegalArgumentException("参数非法");
    }
    };
    }
    }

    StringToBaseEnumConverterFactory 可以让所有“实现了 BaseEnum 接口的枚举类”都支持转换。

    注册转换器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Configuration
    public class WebMvcConfiguration implements WebMvcConfigurer {

    @Autowired
    private StringToBaseEnumConverterFactory stringToBaseEnumConverterFactory;

    @Override
    public void addFormatters(FormatterRegistry registry) {
    registry.addConverterFactory(stringToBaseEnumConverterFactory);
    }
    }
    • 为什么要注册?

    • 答:StringToBaseEnumConverterFactory类只是定义了一个转换规则,但 SpringMVC 默认不一定知道什么时候用它。所以要“注册”,就是告诉 SpringMVC:以后 Controller 接收参数时,如果发现前端传的是 String,但方法参数需要 BaseEnum 类型的枚举,就用这个转换器来转换。

  • ItemType枚举类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public enum ItemType implements BaseEnum {
    APARTMENT(1, "公寓"),
    ROOM(2, "房间");

    @EnumValue
    @JsonValue
    private Integer code;
    private String name;
    ...
    }

    @EnumValue注解:告诉 MyBatis-Plus,数据库里存这个枚举的 code 值。

    @JsonValue注解:告诉 Spring Boot 返回 JSON 时,这个枚举用 code 表示。

接口2:新增或修改标签信息

1
2
3
4
5
6
7
8
9
10
@Operation(summary = "新增或修改标签信息")
@PostMapping("saveOrUpdate")
public Result saveOrUpdateLabel(@RequestBody LabelInfo labelInfo) {
boolean saveOrUpdate = labelInfoService.saveOrUpdate(labelInfo);
if (saveOrUpdate) {
return Result.ok();
} else {
return Result.fail();
}
}

接口3:根据id删除标签信息

1
2
3
4
5
6
7
8
9
10
@Operation(summary = "根据id删除标签信息")
@DeleteMapping("deleteById")
public Result deleteLabelById(@RequestParam Long id) {
boolean removeById = labelInfoService.removeById(id);
if (removeById) {
return Result.ok();
} else {
return Result.fail();
}
}

配套管理

1. 找到对应的数据库

  • 配套管理数据库表facility_info

2. 设计接口

接口1:[根据类型]查询配套信息列表

1
2
3
4
5
6
7
8
@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 = facilityInfoService.list(queryWrapper);
return Result.ok(list);
}

接口2:新增或修改配套信息

1
2
3
4
5
6
7
8
9
10
@Operation(summary = "新增或修改配套信息")
@PostMapping("saveOrUpdate")
public Result saveOrUpdate(@RequestBody FacilityInfo facilityInfo) {
boolean saveOrUpdate = facilityInfoService.saveOrUpdate(facilityInfo);
if (saveOrUpdate) {
return Result.ok();
} else {
return Result.fail();
}
}

接口3:根据id删除配套信息

1
2
3
4
5
6
7
8
9
10
@Operation(summary = "根据id删除配套信息")
@DeleteMapping("deleteById")
public Result removeFacilityById(@RequestParam Long id) {
boolean removeById = facilityInfoService.removeById(id);
if (removeById) {
return Result.ok();
} else {
return Result.fail();
}
}

基本属性管理

![ChatGPT Image 2026年7月4日 22_18_43](http://cdn.gcblog.fun/尚庭公寓%2F3.房间租期管理%2FChatGPT Image 2026年7月4日 22_18_43.png)

1. 找到对应的数据库

  • 基本属性名称表attr_key
  • 基本属性值表attr_value

2. 设计接口

*** 接口1:查询全部属性名称和属性值列表**

1. 拓展实体类

  • 由于返回结果包含多张表数据,创建vo类型用于封装最终结果
1
2
3
4
5
6
@Data
public class AttrKeyVo extends AttrKey {

@Schema(description = "属性value列表")
private List<AttrValue> attrValueList;
}
1
2
3
4
5
6
7
@Schema(description = "属性值")
@Data
public class AttrValueVo extends AttrValue {

@Schema(description = "对应的属性key_name")
private String attrKeyName;
}

2. Controller

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

3. Service

1
2
3
4
public interface AttrKeyService extends IService<AttrKey> {
// 查询全部属性名称和属性值列表
List<AttrKeyVo> listAttrInfo();
}

4. ServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class AttrKeyServiceImpl extends ServiceImpl<AttrKeyMapper, AttrKey>
implements AttrKeyService{

@Autowired
private AttrKeyMapper attrKeyMapper;

// 查询全部属性名称和属性值列表
@Override
public List<AttrKeyVo> listAttrInfo() {
List<AttrKeyVo> list = attrKeyMapper.listAttrInfo();
return list;
}
}

Mapper.java

1
2
3
4
5
6
@Mapper
public interface AttrKeyMapper extends BaseMapper<AttrKey> {

// 查询全部属性名称和属性值列表
List<AttrKeyVo> listAttrInfo();
}

Mapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<mapper namespace="com.atguigu.lease.web.admin.mapper.AttrKeyMapper">

<!--自定义返回结果-->
<resultMap id="BaseResultMap" type="com.atguigu.lease.web.admin.vo.attr.AttrKeyVo">
<id column="id" property="id"/>
<result column="key_name" property="name"/>
<collection property="attrValueList" ofType="com.atguigu.lease.model.entity.AttrValue">
<id column="value_id" property="id"/>
<result column="value_name" property="name"/>
<result column="key_id" property="attrKeyId"/>
</collection>
</resultMap>


<!--查询全部属性名称和属性值列表-->
<select id="listAttrInfo" resultMap="BaseResultMap">
SELECT k.id,
k.name key_name,
v.id value_id,
v.name value_name,
v.attr_key_id key_id
FROM attr_key k
LEFT JOIN attr_value v ON k.id = v.attr_key_id

</select>
</mapper>

拓展

  • 多领域模型介绍
    • POJO : POJO指的是普通的Java对象,没有任何特殊限制或要求,不依赖于特定的框架或接口。它通常用于表示简单的数据对象,只包含私有字段、对应的gettersetter方法以及一些业务逻辑。
    • Entity: Entity表示系统中具有独特身份的业务对象。它通常映射到数据库表中的记录,有唯一的标识符(ID)并包含与业务相关的数据和行为。
    • VO: VO(Value Object)是一种用于表示值的对象,通常是不可变的,只包含数据而没有业务行为。它用于封装一组相关的数据,常用于传递数据结构。

接口2:根据id删除属性值

1
2
3
4
5
6
7
8
9
10
@Operation(summary = "根据id删除属性值")
@DeleteMapping("value/deleteById")
public Result removeAttrValueById(@RequestParam Long id) {
boolean remove = attrValueService.removeById(id);
if(remove) {
return Result.ok();
} else {
return Result.fail();
}
}

*** 接口3:根据id删除属性名称**

1
2
3
4
5
6
7
8
9
10
11
12
@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); // where attr_key_id = #{attrKeyId}
attrValueService.remove(wrapper);
return Result.ok();
}

接口4:新增或更新属性名称

1
2
3
4
5
6
@Operation(summary = "新增或更新属性名称")
@PostMapping("key/saveOrUpdate")
public Result saveOrUpdateAttrKey(@RequestBody AttrKey attrKey) {
boolean saveOrUpdate = attrKeyService.saveOrUpdate(attrKey);
return Result.ok(saveOrUpdate);
}

接口5:新增或更新属性值

1
2
3
4
5
6
@Operation(summary = "新增或更新属性值")
@PostMapping("value/saveOrUpdate")
public Result saveOrUpdateAttrValue(@RequestBody AttrValue attrValue) {
boolean saveOrUpdate = attrValueService.saveOrUpdate(attrValue);
return Result.ok(saveOrUpdate);
}

公寓杂费管理

同基本属性管理

1. 找到对应的数据库

  • 杂费名称表fee_key
  • 杂费值表fee_value

2. 设计接口

接口1:查询全部杂费名称和杂费值列表

1. 拓展实体类

1
2
3
4
5
6
@Data
public class FeeKeyVo extends FeeKey {

@Schema(description = "杂费value列表")
private List<FeeValue> feeValueList;
}
1
2
3
4
5
6
7
@Schema(description = "杂费值")
@Data
public class FeeValueVo extends FeeValue {

@Schema(description = "费用所对的fee_key名称")
private String feeKeyName;
}

2. Controller

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

3. Service

1
2
3
4
5
public interface FeeKeyService extends IService<FeeKey> {

List<FeeKeyVo> listFeeInfo();
}

4. ServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class FeeKeyServiceImpl extends ServiceImpl<FeeKeyMapper, FeeKey>
implements FeeKeyService{

@Autowired
private FeeKeyMapper feeKeyMapper;

@Override
public List<FeeKeyVo> listFeeInfo() {
List<FeeKeyVo> list = feeKeyMapper.listFeeInfo();
return list;
}
}

5. Mapper.java

1
2
3
4
5
@Mapper
public interface FeeKeyMapper extends BaseMapper<FeeKey> {

List<FeeKeyVo> listFeeInfo();
}

6. Mapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<mapper namespace="com.atguigu.lease.web.admin.mapper.FeeKeyMapper">

<resultMap id="BaseREsultMap" type="com.atguigu.lease.web.admin.vo.fee.FeeKeyVo">
<id property="id" column="id"/>
<result property="name" column="key_name"/>
<collection property="feeValueList" ofType="com.atguigu.lease.model.entity.FeeValue">
<id property="id" column="value_id"/>
<result property="name" column="value_name"/>
<result property="unit" column="value_unit"/>
<result property="feeKeyId" column="key_id"/>
</collection>
</resultMap>

<select id="listFeeInfo" resultMap="BaseREsultMap">
SELECT k.id,
k.name key_name,
v.id value_id,
v.unit value_unit,
v.fee_key_id key_id
FROM fee_key k
left join fee_value v on k.id = v.fee_key_id
</select>
</mapper>

接口2:根据id删除杂费值

1
2
3
4
5
6
7
8
9
10
@Operation(summary = "根据id删除杂费值")
@DeleteMapping("value/deleteById")
public Result deleteFeeValueById(@RequestParam Long id) {
boolean remove = feeValueService.removeById(id);
if (remove) {
return Result.ok();
} else {
return Result.fail();
}
}

接口3:根据id删除杂费名称

1
2
3
4
5
6
7
8
9
10
@Operation(summary = "根据id删除杂费名称")
@DeleteMapping("key/deleteById")
public Result deleteFeeKeyById(@RequestParam Long feeKeyId) {
feeKeyService.removeById(feeKeyId);

LambdaQueryWrapper<FeeValue> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FeeValue::getFeeKeyId, feeKeyId); // where fee_key_id = feeKeyId
feeValueService.remove(queryWrapper);
return Result.ok();
}

接口4:保存或更新杂费名称

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

接口5:保存或更新杂费值

1
2
3
4
5
6
@Operation(summary = "保存或更新杂费值")
@PostMapping("value/saveOrUpdate")
public Result saveOrUpdateFeeValue(@RequestBody FeeValue feeValue) {
boolean saveOrUpdate = feeValueService.saveOrUpdate(feeValue);
return Result.ok(saveOrUpdate);
}

地区信息管理

1. 概述

  • 实现省市区三级联动

    image-20260706171843576

  • 对应数据库:

    • 省信息表province_info
    • 市信息表city_info
    • 区信息表district_info

2. 设计接口

![ChatGPT Image 2026年7月6日 17_40_41](http://cdn.gcblog.fun/尚庭公寓/3.房间租期管理/ChatGPT Image 2026年7月6日 17_40_41.png)

接口1:查询所有省

1
2
3
4
5
6
@Operation(summary = "查询省份信息列表")
@GetMapping("province/list")
public Result<List<ProvinceInfo>> listProvince() {
List<ProvinceInfo> list = provinceInfoService.list();
return Result.ok(list);
}

接口2:查询省下的所有市

1
2
3
4
5
6
7
8
@Operation(summary = "根据省份id查询城市信息列表")
@GetMapping("city/listByProvinceId")
public Result<List<CityInfo>> listCityInfoByProvinceId(@RequestParam Long id) {
LambdaQueryWrapper<CityInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CityInfo::getProvinceId, id);
List<CityInfo> list = cityInfoService.list(wrapper);
return Result.ok(list);
}

接口3:查询市下的所有区

1
2
3
4
5
6
7
8
@GetMapping("district/listByCityId")
@Operation(summary = "根据城市id查询区县信息")
public Result<List<DistrictInfo>> listDistrictInfoByCityId(@RequestParam Long id) {
LambdaQueryWrapper<DistrictInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DistrictInfo::getCityId, id);
List<DistrictInfo> list = districtInfoService.list(wrapper);
return Result.ok(list);
}

导入图片

1. 引入依赖

1
2
3
4
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>

2. 添加配置信息

1
2
3
4
5
minio:
endpoint: http://127.0.0.1:9000
accesskey: minioadmin
secretkey: minioadmin
bucketname: atguigu

3. 创建类读取配置文件内容

1
2
3
4
5
6
7
8
9
10
@ConfigurationProperties(prefix = "minio")
@Data
@Component
public class MinioProperyties {

private String endpoint;
private String accesskey;
private String secretkey;
private String bucketname;
}

4. 创建配置类将Minio交给Spring管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootConfiguration
public class MinioConfiguration {

@Autowired
private MinioProperyties properyties;

@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint("http://127.0.0.1:9000")
.credentials("minioadmin", "minioadmin")
.build();
}
}
  • 拓展:@EnableConfigurationProperties

    让某个配置属性类生效,把配置文件中的内容绑定到 Java 对象上。

5. 设计接口

  • FileUploadController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Tag(name = "文件管理")
@RequestMapping("/admin/file")
@RestController
public class FileUploadController {

@Autowired
private FileService fileService;

@Operation(summary = "上传文件")
@PostMapping("upload")
public Result<String> upload(@RequestParam MultipartFile file) {
String url = fileService.upload(file);
return Result.ok(url);
}

}

6. Service

7. ServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@Service
public class FileServiceImpl implements FileService {

@Autowired
private MinioClient client;

@Autowired
private MinioProperyties properties;
// 上传到minio服务器
@Override
public String upload(MultipartFile file) {
try {
// 判断bucket是否存在
boolean bucketExists = client.bucketExists(BucketExistsArgs.builder()
.bucket(properties.getBucketname())
.build());
// 如果不存在则创建
if (!bucketExists) {
client.makeBucket(MakeBucketArgs.builder()
.bucket(properties.getBucketname())
.build());
// 设置bucket策略:私有、公共、自定义
client.setBucketPolicy(SetBucketPolicyArgs.builder()
.bucket(properties.getBucketname())
.config(createBucketPolicyConfig(properties.getBucketname()))
.build());
}

String filename = new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/" + UUID.randomUUID() + "-" + file.getOriginalFilename();

//String filename = file.getOriginalFilename();
//上传文件到Minio
client.putObject(PutObjectArgs.builder().
bucket(properties.getBucketname()). // bucket名称
object(filename). // 在bucket的文件名称
stream(file.getInputStream(), file.getSize(), -1).
contentType(file.getContentType()).build());

// 返回地址
return String.join("/", properties.getEndpoint(), properties.getBucketname(), filename);

} catch (Exception e) {
e.printStackTrace();
}
return null;
}

private String createBucketPolicyConfig(String bucketName) {

return """
{
"Statement" : [ {
"Action" : "s3:GetObject",
"Effect" : "Allow",
"Principal" : "*",
"Resource" : "arn:aws:s3:::%s/*"
} ],
"Version" : "2012-10-17"
}
""".formatted(bucketName);
}

}

文件上传大小

1
2
3
4
spring:
servlet:
max-file-size: 100MB
max-request-size: 150MB

统一异常处理

问题演示

  • 目前接口如果出现异常:

    image-20260709141722040

  • 现在需要统一异常处理,就算出现异常也要返回Result格式数据

image-20260709145146154

添加GlobalExceptionHandler

1
2
3
4
5
6
7
8
9
10
@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Exception e){
e.printStackTrace();
return Result.fail();
}
}

image-20260709201417679


公寓管理

1. 数据库表

  • apartment_info:公寓基本信息表
  • apartment_facility:公寓配置信息数据表
  • apartment_label:公寓标签数据
  • apartment_fee_value:公寓杂费管理
  • graph_info:图片表

2. 设计接口

接口1:保存或更新公寓信息

1. 拓展实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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;

}

2. Controller

1
2
3
4
5
6
@Operation(summary = "保存或更新公寓信息")
@PostMapping("saveOrUpdate")
public Result saveOrUpdate(@RequestBody ApartmentSubmitVo apartmentSubmitVo) {
apartmentInfoService.saveOrUpdateApartment(apartmentSubmitVo);
return Result.ok();
}

Service

1
2
3
4
5
public interface ApartmentInfoService extends IService<ApartmentInfo> {

// 保存或更新公寓信息
void saveOrUpdateApartment(ApartmentSubmitVo apartmentSubmitVo);
}

ServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
@Service
public class ApartmentInfoServiceImpl extends ServiceImpl<ApartmentInfoMapper, ApartmentInfo>
implements ApartmentInfoService {


// 注入公寓配套Service
@Autowired
private ApartmentFacilityService apartmentFacilityService;


@Autowired
private ApartmentLabelService apartmentLabelService;


@Autowired
private ApartmentFeeValueService apartmentFeeValueService;

@Autowired
private GraphInfoService graphInfoService;

@Override
public void saveOrUpdateApartment(ApartmentSubmitVo apartmentSubmitVo) {

// 判断是否进行修改操作
// 判断apartmentSubmitVo是否有id
/*Long apartmentId = apartmentSubmitVo.getId();
if (apartmentId != null) {
// 删除配套、杂费、标签、图片数据

}*/
boolean isUpdate = apartmentSubmitVo.getId() != null;

// 1. 添加公寓基本数据到 apartment_info:公寓基本信息表
//apartmentInfoMapper.insert(apartmentSubmitVo);
this.saveOrUpdate(apartmentSubmitVo);


if (isUpdate) {
// 删除配套、杂费、标签、图片数据
// 1. 删除配套数据 apartment_facility
LambdaQueryWrapper<ApartmentFacility> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ApartmentFacility::getApartmentId, apartmentSubmitVo.getId());
apartmentFacilityService.remove(wrapper);

// 2. 删除公寓标签数据 apartment_label
LambdaQueryWrapper<ApartmentLabel> labelWrapper = new LambdaQueryWrapper<>();
labelWrapper.eq(ApartmentLabel::getApartmentId, apartmentSubmitVo.getId());
apartmentLabelService.remove(labelWrapper);

// 3. 删除公寓杂费数据 apartment_fee_value
LambdaQueryWrapper<ApartmentFeeValue> feeWrapper = new LambdaQueryWrapper<>();
feeWrapper.eq(ApartmentFeeValue::getApartmentId, apartmentSubmitVo.getId());
apartmentFeeValueService.remove(feeWrapper);

// 4. 删除公寓图片数据 graph_info
LambdaQueryWrapper<GraphInfo> graphWrapper = new LambdaQueryWrapper<>();
graphWrapper.eq(GraphInfo::getItemId, apartmentSubmitVo.getId());
graphWrapper.eq(GraphInfo::getItemType, ItemType.APARTMENT);
graphInfoService.remove(graphWrapper);
}

// 2. 添加公寓配套数据 apartment_facility:公寓配套信息数据
// 一个公寓id对应多个配套数据
// 获取公寓配套数据
List<Long> facilityInfoIds = apartmentSubmitVo.getFacilityInfoIds();
// 判断配套数据的集合不为空
if (facilityInfoIds != null && !facilityInfoIds.isEmpty()) {
/*// 添加数据
// 遍历facilityInfoIds
for (Long fid:facilityInfoIds) {
// 创建ApartmentFacility对象,向其中设置要添加的值
ApartmentFacility apartmentFacility = new ApartmentFacility();
// 设置配套值
apartmentFacility.setFacilityId(fid);
// 设置公寓id
apartmentFacility.setApartmentId(apartmentSubmitVo.getId());
apartmentFacilityService.save(apartmentFacility);
}*/

ArrayList<ApartmentFacility> afList = new ArrayList<>();
for (Long fid : facilityInfoIds) {
// 创建ApartmentFacility对象,向其中设置要添加的值
ApartmentFacility apartmentFacility = new ApartmentFacility();
// 设置配套值
apartmentFacility.setFacilityId(fid);
// 设置公寓id
apartmentFacility.setApartmentId(apartmentSubmitVo.getId());
// 添加到集合中
afList.add(apartmentFacility);

}
// 调用Service批量添加方法
apartmentFacilityService.saveBatch(afList);
}


// 3. 添加公寓的标签数据 apartment_label:公寓标签数据
// 一个公寓id对应多个标签数据
// 获取公寓标签数据
List<Long> labelIds = apartmentSubmitVo.getLabelIds();
// 判断标签数据的集合不为空
if (labelIds != null && !labelIds.isEmpty()) {
ArrayList<ApartmentLabel> apartmentLabelList = new ArrayList<>();
for (Long labelId : labelIds) {
// 创建ApartmentLabel对象,向其中设置要添加的值
ApartmentLabel apartmentLabel = new ApartmentLabel();
// 设置标签值
apartmentLabel.setLabelId(labelId);
// 设置公寓id
apartmentLabel.setApartmentId(apartmentSubmitVo.getId());
// 添加到集合中
apartmentLabelList.add(apartmentLabel);
}
// 调用Service批量添加方法
apartmentLabelService.saveBatch(apartmentLabelList);
}
// 4. 添加公寓杂费数据 apartment_fee_value:公寓杂费数据
// 一个公寓id对应多条杂费数据
// 获取公寓杂费数据
List<Long> feeValueIds = apartmentSubmitVo.getFeeValueIds();
// 判断杂费数据的集合不为空
if (feeValueIds != null && !feeValueIds.isEmpty()) {
ArrayList<ApartmentFeeValue> feeValueList = new ArrayList<>();
for (Long feeValueId : feeValueIds) {
// 创建ApartmentFeeValue对象,向其中设置要添加的值
ApartmentFeeValue apartmentFeeValue = new ApartmentFeeValue();
// 设置杂费值
apartmentFeeValue.setFeeValueId(feeValueId);
// 设置公寓id
apartmentFeeValue.setApartmentId(apartmentSubmitVo.getId());
// 添加到集合中
feeValueList.add(apartmentFeeValue);
}
// 调用Service批量添加方法
apartmentFeeValueService.saveBatch(feeValueList);
}

// 5. 添加公寓图片数据 graph_info:公寓图片数据
// 一个公寓id对应多条图片数据
// 获取公寓图片数据
List<GraphVo> graphVoList = apartmentSubmitVo.getGraphVoList();
// 判断图片数据的集合不为空
if (graphVoList != null && !graphVoList.isEmpty()) {
ArrayList<GraphInfo> graphVoArrayList = new ArrayList<>();
for (GraphVo graphVo : graphVoList) {
// 创建GraphVo对象,向其中设置要添加的值graphVoList
GraphInfo graphInfo = new GraphInfo();
// 设置图片值
graphInfo.setName(graphVo.getName());
// 设置公寓id
graphInfo.setItemId(apartmentSubmitVo.getId());
graphInfo.setItemType(ItemType.APARTMENT);
graphInfo.setUrl(graphVo.getUrl());
graphVoArrayList.add(graphInfo);
}
// 调用Service批量添加方法
graphInfoService.saveBatch(graphVoArrayList);
}
}

接口2:根据条件分页查询公寓列表

1. 配置分页插件

1
2
3
4
5
6
7
8
9
10
@Configuration
@MapperScan("com.atguigu.lease.web.*.mapper")
public class MybatisPlusConfiguration {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}

2. Controller

1
2
3
4
5
6
7
8
@Operation(summary = "根据条件分页查询公寓列表")
@GetMapping("pageItem")
public Result<IPage<ApartmentItemVo>> pageItem(@RequestParam long current, @RequestParam long size, ApartmentQueryVo queryVo) {
// 创建分页page对象,传递当前页和每页记录数
Page<ApartmentItemVo> page = new Page<>(current, size);
IPage<ApartmentItemVo> pageModel = apartmentInfoService.selectApartmentInfoPage(page, queryVo);
return Result.ok(pageModel);
}

3. Service

1
IPage<ApartmentItemVo> selectApartmentInfoPage(@Param("page") Page<ApartmentItemVo> page,  @Param("queryVo") ApartmentQueryVo queryVo);

4. ServiceImpl

1
2
3
4
@Override
public IPage<ApartmentItemVo> selectApartmentInfoPage(Page<ApartmentItemVo> page, ApartmentQueryVo queryVo) {
return apartmentInfoMapper.selectApartmentInfoPage(page, queryVo);
}

5. Mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<mapper namespace="com.atguigu.lease.web.admin.mapper.ApartmentInfoMapper">

<select id="selectApartmentInfoPage" 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>
</mapper>

  • 测试时开启参数功能

    1
    2
    springdoc:
    default-flat-param-object: true

接口3:根据ID获取公寓详细信息

Controller

1
2
3
4
5
6
@Operation(summary = "根据ID获取公寓详细信息")
@GetMapping("getDetailById")
public Result<ApartmentDetailVo> getDetailById(@RequestParam Long id) {
ApartmentDetailVo apartmentDetailVo = apartmentInfoService.getDetailById(id);
return Result.ok(apartmentDetailVo);
}

Service

1
2
3
4
5
6
7
8
9
public interface ApartmentInfoService extends IService<ApartmentInfo> {

// 保存或更新公寓信息
void saveOrUpdateApartment(ApartmentSubmitVo apartmentSubmitVo);


ApartmentDetailVo getDetailById(Long id);
}

ServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Autowired
private FacilityInfoMapper facilityInfoMapper;

@Autowired
private LabelInfoMapper labelInfoMapper;

@Autowired
private FeeValueMapper feeValueMapper;

@Autowired
private GraphInfoMapper graphInfoMapper;

@Override
public ApartmentDetailVo getDetailById(Long id) {
// 1. 根据公寓id查询公寓基本信息
ApartmentInfo apartmentInfo = this.getById(id);
if (apartmentInfo == null) {
return null;
}

// 2. 根据公寓id查询公寓配套数据selectListByApartmentId
List<FacilityInfo> facilityInfoList = facilityInfoMapper.findFacilityListByApartmentId(id);

// 3. 根据公寓id查询公寓标签数据
List<LabelInfo> labelInfoList = labelInfoMapper.findLabelListByApartmentId(id);

// 4. 根据公寓id查询公寓杂费数据
List<FeeValueVo> feeValueVoList = feeValueMapper.findFeeValueListByApartmentId(id);

// 5. 根据公寓id查询公寓图片数据
List<GraphVo> graphVoList = graphInfoMapper.findGraphListByApartmentId(ItemType.APARTMENT, id);

// 6. 把上面查询出来的所有数据封装到ApartmentDetailVo对象
ApartmentDetailVo apartmentDetailVo = new ApartmentDetailVo();

BeanUtils.copyProperties(apartmentInfo, apartmentDetailVo);

apartmentDetailVo.setFacilityInfoList(facilityInfoList);
apartmentDetailVo.setLabelInfoList(labelInfoList);
apartmentDetailVo.setFeeValueVoList(feeValueVoList);
apartmentDetailVo.setGraphVoList(graphVoList);

// 7. 返回ApartmentDetailVo对象
return apartmentDetailVo;
}

Mapper

省略

接口4:根据ID删除公寓详细信息

Controller

1
2
3
4
5
6
@Operation(summary = "根据id删除公寓信息")
@DeleteMapping("removeById")
public Result removeById(@RequestParam Long id) {
apartmentInfoService.removeApartmentInfo(id);
return Result.ok();
}

Service

1
void removeApartmentInfo(Long id);

ServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Override
public void removeApartmentInfo(Long id) {
// 判断如果公寓下面有房间,不能直接删除公寓
// 根据公寓id查询room_info表,判断是否存在房间数据
LambdaQueryWrapper<RoomInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RoomInfo::getApartmentId, id);
long count = roomInfoService.count(wrapper);
if (count > 0) {
// 存在房间信息
throw new RuntimeException("存在房间信息");
}
// 只关注是否存在房间,而不关注存在几个房间/具体房间信息
/*List<RoomInfo> roomInfoList = roomInfoService.list(wrapper);
if (roomInfoList != null && !roomInfoList.isEmpty()) {
throw new RuntimeException("公寓下面有房间,不能直接删除公寓");
}*/


// 1. 删除公寓基本信息
this.removeById(id);

//2. 删除公寓配套数据
LambdaQueryWrapper<ApartmentFacility> wrapper01 = new LambdaQueryWrapper<>();
wrapper01.eq(ApartmentFacility::getApartmentId, id);
apartmentFacilityService.remove(wrapper01);

// 3. 删除公寓标签数据
LambdaQueryWrapper<ApartmentLabel> wrapper02 = new LambdaQueryWrapper<>();
wrapper02.eq(ApartmentLabel::getApartmentId, id);
apartmentLabelService.remove(wrapper02);

// 4. 删除公寓杂费数据
LambdaQueryWrapper<ApartmentFeeValue> wrapper03 = new LambdaQueryWrapper<>();
wrapper03.eq(ApartmentFeeValue::getApartmentId, id);
apartmentFeeValueService.remove(wrapper03);

// 5. 删除公寓图片数据
LambdaQueryWrapper<GraphInfo> wrapper04 = new LambdaQueryWrapper<>();
wrapper04.eq(GraphInfo::getItemId, id);
graphInfoService.remove(wrapper04);
}

接口5:根据ID修改公寓发布状态

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Operation(summary = "根据id修改公寓发布状态")
@PostMapping("updateReleaseStatusById")
public Result updateReleaseStatusById(@RequestParam Long id, @RequestParam ReleaseStatus status) {
// 方式一:
// 根据id查询公寓信息
ApartmentInfo apartmentInfo = apartmentInfoService.getById(id);
// 设置修改值
apartmentInfo.setIsRelease(status);
// 调用方法进行修改
apartmentInfoService.updateById(apartmentInfo);

// 方式二:
LambdaUpdateWrapper<ApartmentInfo> wrapper = new LambdaUpdateWrapper<>();
// 设置修改条件
wrapper.eq(ApartmentInfo::getId, id);
// 设置要修改的值
wrapper.set(ApartmentInfo::getIsRelease, status);
return Result.ok();
}

接口6: 根据区县ID查询公寓信息列表

1
2
3
4
5
6
7
8
@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 = apartmentInfoService.list(queryWrapper);
return Result.ok(list);
}