本文记录尚庭公寓项目中 房间支付方式管理 模块的开发过程,主要包括接口分析、Controller 编写、Knife4j 测试、逻辑删除配置以及统一返回结果封装。


一、开发前准备

在正式开发接口之前,首先要确认是否有完整的接口文档。

1. 有接口文档

如果项目已经提供接口文档,需要先阅读接口说明,明确以下内容:

  • 接口路径
  • 请求方式
  • 请求参数
  • 返回结果
  • 对接人或负责人

拿到接口文档后,最好提前和负责人确认:如果开发过程中遇到问题,应该找谁沟通。

2. 没有接口文档

如果没有接口文档,不要直接凭感觉开发,而是要进一步询问需求细节。

例如:

  • 这个接口给谁使用?
  • 前端需要哪些字段?
  • 是否需要分页?
  • 是否需要逻辑删除?
  • 返回格式是否统一?

这样可以避免后期返工。


二、开发步骤

1. 找到对应的数据表

本模块对应的数据表为:

1
payment_type

该表主要用于保存房间支持的支付方式信息,例如月付、季付、半年付等。

payment_type


2. 设计接口

房间支付方式管理主要包含以下接口:

接口功能 请求方式 接口路径 说明
查询全部支付方式 GET /list 查询所有未删除的支付方式
根据 id 查询支付方式 GET /getPaymentType/{id} 根据 id 回显支付方式信息
保存或更新支付方式 POST /saveOrUpdate 新增或修改支付方式
根据 id 删除支付方式 DELETE /deleteById 逻辑删除支付方式

三、接口实现

接口 1:查询全部支付方式列表

首先在 Controller 中注入 PaymentTypeService

1
2
@Autowired
private PaymentTypeService paymentTypeService;

然后编写查询全部支付方式的接口:

1
2
3
4
5
6
// 查询所有支付类型
@GetMapping("list")
public List<PaymentType> listPaymentType() {
List<PaymentType> list = paymentTypeService.list();
return list;
}

这段代码的作用是调用 MyBatis-Plus 提供的 list() 方法,查询 payment_type 表中的所有支付方式数据。


使用 Knife4j 测试接口

启动项目后,可以在浏览器访问 Knife4j 接口文档页面:

1
http://localhost:8080/doc.html#/全部接口/支付方式管理/listPaymentType

测试页面如下:

knife4j测试界面

Knife4j 的配置类如下:

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
@Configuration
public class Knife4jConfiguration {

@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI().info(
new Info()
.title("后台管理系统API")
.version("1.0")
.description("后台管理系统API"));
}

@Bean
public GroupedOpenApi systemAPI() {
return GroupedOpenApi.builder()
.group("系统信息管理")
.pathsToMatch("/admin/system/**")
.build();
}

@Bean
public GroupedOpenApi loginAPI() {
return GroupedOpenApi.builder()
.group("后台登录管理")
.pathsToMatch(
"/admin/login/**",
"/admin/info"
)
.build();
}

@Bean
public GroupedOpenApi apartmentAPI() {
return GroupedOpenApi.builder()
.group("公寓信息管理")
.pathsToMatch(
"/admin/apartment/**",
"/admin/room/**",
"/admin/label/**",
"/admin/facility/**",
"/admin/fee/**",
"/admin/attr/**",
"/admin/payment/**",
"/admin/region/**",
"/admin/term/**",
"/admin/file/**"
)
.build();
}

@Bean
public GroupedOpenApi leaseAPI() {
return GroupedOpenApi.builder()
.group("租赁信息管理")
.pathsToMatch(
"/admin/appointment/**",
"/admin/agreement/**"
)
.build();
}

@Bean
public GroupedOpenApi userAPI() {
return GroupedOpenApi.builder()
.group("平台用户管理")
.pathsToMatch("/admin/user/**")
.build();
}

@Bean
public GroupedOpenApi allAPI() {
return GroupedOpenApi.builder()
.group("全部接口")
.pathsToMatch("/**")
.build();
}
}

Knife4j 页面会根据 Controller 或 @Tag 注解自动拆分接口模块。


接口 2:根据 id 查询支付方式

这个接口通常用于修改数据前的数据回显。

1
2
3
4
5
6
// 根据 id 查询支付类型
@GetMapping("/getPaymentType/{id}")
public Result getPaymentType(@PathVariable Long id) {
PaymentType paymentType = paymentTypeService.getById(id);
return Result.ok(paymentType);
}

说明:

  • @PathVariable 用来接收路径中的参数。
  • paymentTypeService.getById(id) 用来根据主键 id 查询数据。
  • Result.ok(paymentType) 表示以统一返回格式返回查询结果。

例如请求路径:

1
/admin/payment/type/getPaymentType/1

其中 1 就会被 @PathVariable Long id 接收。


接口 3:保存或更新支付方式

1
2
3
4
5
6
7
8
9
10
11
12
// 保存或更新支付方式
@Operation(summary = "保存或更新支付方式")
@PostMapping("saveOrUpdate")
public Result saveOrUpdate(@RequestBody PaymentType paymentType) {
boolean isSuccess = paymentTypeService.saveOrUpdate(paymentType);

if (isSuccess) {
return Result.ok();
} else {
return Result.fail();
}
}

这里使用了 MyBatis-Plus 的 saveOrUpdate() 方法。

它的作用是:

  • 如果传入的数据没有 id,执行新增操作。
  • 如果传入的数据有 id,并且数据库中存在对应记录,执行修改操作。

代码中的 if 用来判断操作是否成功:

1
2
3
4
5
if (isSuccess) {
return Result.ok();
} else {
return Result.fail();
}

也可以简写为:

1
return isSuccess ? Result.ok() : Result.fail();

@RequestBody 的作用

1
@RequestBody PaymentType paymentType

表示前端需要以 JSON 格式传递参数,例如:

1
2
3
4
{
"name": "月付",
"payMonthCount": 1
}

如果不加 @RequestBody

1
PaymentType paymentType

则通常是通过普通参数形式传递,例如:

1
?name=月付&payMonthCount=1

两者区别如下:

写法 参数位置 常见请求方式
@RequestBody PaymentType paymentType 请求体 JSON POST / PUT
PaymentType paymentType URL 参数或表单参数 GET / POST

需要注意:GET 请求一般没有请求体,所以通常不使用 @RequestBody


接口 4:根据 id 删除支付方式

1
2
3
4
5
6
7
8
9
10
11
12
// 根据 id 删除支付方式
@Operation(summary = "根据id删除支付方式")
@DeleteMapping("deleteById")
public Result deletePaymentType(@RequestParam Long id) {
boolean remove = paymentTypeService.removeById(id);

if (remove) {
return Result.ok();
} else {
return Result.fail();
}
}

说明:

  • @DeleteMapping 表示这是一个删除接口。
  • @RequestParam Long id 表示从请求参数中接收 id。
  • removeById(id) 是 MyBatis-Plus 提供的根据 id 删除方法。

请求示例:

1
/admin/payment/type/deleteById?id=10

四、配置逻辑删除

在实际项目中,删除数据时通常不会直接物理删除,而是使用逻辑删除。

逻辑删除的意思是:

数据仍然保存在数据库中,只是把删除标记字段改为已删除状态。

在实体类中找到逻辑删除字段,并添加 @TableLogic 注解:

1
2
3
4
5
6
7
8
@Data
public class BaseEntity implements Serializable {

@Schema(description = "逻辑删除")
@TableLogic
@TableField("is_deleted")
private Byte isDeleted;
}

添加该注解后,调用:

1
paymentTypeService.removeById(id);

MyBatis-Plus 不会真正删除数据,而是将 is_deleted 字段更新为 1


五、接口测试

1. 测试保存支付方式

保存数据之前,可以将数据表中的 create_timeupdate_time 字段默认值设置为:

1
CURRENT_TIMESTAMP

这样新增数据时,时间字段会自动保存为当前时间。

image-20260702212608902

测试结果:

create_timeupdate_time 成功设置为当前时间。

image-20260702214639152

image-20260702214720594


2. 测试根据 id 删除支付方式

执行删除接口:

1
/admin/payment/type/deleteById?id=10

测试截图:

删除后结果:

id 为 10 的支付方式并没有从数据库中真正删除,而是将逻辑删除字段 is_deleted 设置为 1

image-20260702214946537

3. 测试更新支付方式

更新支付方式时,需要传入已有数据的 id。

image-20260702215413553

更新后结果:

id 为 12 的支付方式的 name 字段被成功修改。

image-20260702215446925


4. 测试查询全部支付方式

查询全部支付方式时,只能查询到未被逻辑删除的数据。

image-20260702215628999


六、统一返回结果

在前后端分离项目中,建议所有接口都使用统一返回结果格式。

这样前端不需要适配各种不同的返回结构,只需要按照固定格式解析即可。

统一返回结果一般包含:

字段 说明
code 状态码
message 提示信息
data 返回数据

1. 查询接口改造

原来的写法:

1
2
3
4
5
@GetMapping("list")
public List<PaymentType> listPaymentType() {
List<PaymentType> list = paymentTypeService.list();
return list;
}

改造后的写法:

1
2
3
4
5
6
// 查询所有支付类型
@GetMapping("list")
public Result listPaymentType() {
List<PaymentType> list = paymentTypeService.list();
return Result.ok(list);
}

这样返回给前端的数据结构更加统一。


2. 手动封装 Result 的写法

1
2
3
4
5
Result result = new Result();
result.setCode(200);
result.setMessage("成功");
result.setData(list);
return result;

这种写法虽然可以实现效果,但是每个接口都这样写会比较麻烦。

所以可以把它封装到 Result 类中。


3. Result 类代码

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
package com.atguigu.lease.common.result;

import lombok.Data;

/**
* 全局统一返回结果类
*/
@Data
public class Result<T> {

// 返回码
private Integer code;

// 返回消息
private String message;

// 返回数据
private T data;

public Result() {
}

private static <T> Result<T> build(T data) {
Result<T> result = new Result<>();
if (data != null) {
result.setData(data);
}
return result;
}

public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}

public static <T> Result<T> ok(T data) {
return build(data, ResultCodeEnum.SUCCESS);
}

public static <T> Result<T> ok() {
return Result.ok(null);
}

public static <T> Result<T> fail() {
return build(null, ResultCodeEnum.FAIL);
}
}

使用封装后的 Result 类,可以让 Controller 代码更加简洁。

例如:

1
return Result.ok(list);

表示返回成功,并携带数据。

1
return Result.ok();

表示返回成功,但不携带数据。

1
return Result.fail();

表示返回失败。


七、开发总结

本次完成了房间支付方式管理模块的基础接口开发,主要实现了:

  • 查询全部支付方式
  • 根据 id 查询支付方式
  • 保存或更新支付方式
  • 根据 id 删除支付方式
  • 配置 MyBatis-Plus 逻辑删除
  • 使用统一返回结果 Result

通过这个模块,可以进一步熟悉 Spring Boot 项目中 Controller、Service、Mapper 的基本配合方式,也能理解前后端分离项目中统一返回结果的重要性。

对于类似的基础数据管理模块,开发流程基本可以总结为:

1
确认需求 → 找到数据表 → 编写接口 → 测试接口 → 统一返回结果 → 总结问题

后续开发其他模块时,也可以按照这个思路进行。