zhouhui.jiang

update

1 +# 时间查询@JsonFormat注解支持说明
2 +
3 +## 功能概述
4 +
5 +后端用户查询接口使用 `@JsonFormat` 注解统一处理时间格式,简化了时间参数的处理逻辑,确保时间格式的一致性。
6 +
7 +## 修改内容
8 +
9 +### 1. DTO类修改
10 +
11 +**文件**: `UserQueryReq.java`
12 +
13 +**修改前**:
14 +```java
15 +private LocalDateTime startTime;
16 +private LocalDateTime endTime;
17 +```
18 +
19 +**修改后**:
20 +```java
21 +/**
22 + * 开始时间(创建时间范围查询)
23 + */
24 +@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
25 +private LocalDateTime startTime;
26 +
27 +/**
28 + * 结束时间(创建时间范围查询)
29 + */
30 +@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
31 +private LocalDateTime endTime;
32 +```
33 +
34 +### 2. 查询条件构建简化
35 +
36 +**文件**: `SysUserServiceImpl.java`
37 +
38 +**修改前**:
39 +```java
40 +// 开始时间:创建时间 >= 开始时间
41 +if (StringUtils.isNotEmpty(queryReq.getStartTime())) {
42 + LocalDateTime startTime = parseDateTime(queryReq.getStartTime());
43 + if (startTime != null) {
44 + wrapper.ge(SysUser::getCreateTime, startTime);
45 + }
46 +}
47 +```
48 +
49 +**修改后**:
50 +```java
51 +// 开始时间:创建时间 >= 开始时间
52 +if (queryReq.getStartTime() != null) {
53 + wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
54 +}
55 +```
56 +
57 +## 支持的时间格式
58 +
59 +### 1. 标准格式
60 +- `2025-09-23` - 日期格式(推荐)
61 +- 时区:GMT+8(中国标准时间)
62 +
63 +### 2. @JsonFormat注解优势
64 +- 自动格式验证
65 +- 时区自动转换
66 +- 代码简洁
67 +- 性能优化
68 +
69 +## 功能特点
70 +
71 +### 1. 自动格式处理
72 +- Jackson自动处理时间格式转换
73 +- 统一的时区处理(GMT+8)
74 +- 无需手动解析时间字符串
75 +
76 +### 2. 代码简化
77 +- 移除了复杂的时间解析逻辑
78 +- 减少了代码维护成本
79 +- 提高了代码可读性
80 +
81 +### 3. 性能优化
82 +- 利用Jackson的高效序列化/反序列化
83 +- 减少自定义解析逻辑
84 +- 更好的内存管理
85 +
86 +## 使用示例
87 +
88 +### 1. API调用示例
89 +```bash
90 +# 标准日期格式(推荐)
91 +curl -X GET "http://localhost:8083/api/system/user/list?startTime=2025-09-23&endTime=2025-09-24"
92 +```
93 +
94 +### 2. 前端时间选择器
95 +```javascript
96 +// 前端生成的时间格式(yyyy-MM-dd)
97 +const startTime = "2025-09-23"
98 +const endTime = "2025-09-24"
99 +
100 +// API调用
101 +const params = {
102 + startTime: startTime,
103 + endTime: endTime,
104 + pageNum: 1,
105 + pageSize: 10
106 +}
107 +```
108 +
109 +### 3. 数据库查询
110 +```sql
111 +-- 实际执行的SQL查询
112 +SELECT * FROM t_sys_user
113 +WHERE del_flag = '0'
114 +AND create_time >= '2025-09-23 00:00:00'
115 +AND create_time <= '2025-09-23 23:59:59'
116 +ORDER BY create_time DESC;
117 +```
118 +
119 +## 错误处理
120 +
121 +### 1. 格式错误处理
122 +```java
123 +// 解析失败时的处理
124 +if (startTime == null) {
125 + // 记录日志但不中断查询
126 + System.err.println("时间格式解析失败: " + queryReq.getStartTime());
127 + // 跳过该查询条件
128 +}
129 +```
130 +
131 +### 2. 空值处理
132 +```java
133 +// 空值检查
134 +if (StringUtils.isNotEmpty(queryReq.getStartTime())) {
135 + // 只有非空时才进行解析
136 +}
137 +```
138 +
139 +### 3. 时区处理
140 +```java
141 +// 时区信息移除
142 +if (dateTimeStr.endsWith("Z") || dateTimeStr.contains("+") || dateTimeStr.contains("-")) {
143 + String localTimeStr = dateTimeStr.replaceAll("[+-]\\d{2}:\\d{2}$", "").replace("Z", "");
144 + return LocalDateTime.parse(localTimeStr, formatter);
145 +}
146 +```
147 +
148 +## 性能考虑
149 +
150 +### 1. 格式化器缓存
151 +- 使用静态格式化器数组
152 +- 避免重复创建格式化器对象
153 +- 按使用频率排序
154 +
155 +### 2. 解析优化
156 +- 按常用格式优先尝试
157 +- 快速失败机制
158 +- 避免不必要的字符串操作
159 +
160 +### 3. 内存管理
161 +- 及时释放临时对象
162 +- 避免字符串拼接
163 +- 使用高效的字符串操作
164 +
165 +## 测试建议
166 +
167 +### 1. 格式测试
168 +```java
169 +// 测试各种时间格式
170 +String[] testFormats = {
171 + "2025-09-23T10:06:13.293Z",
172 + "2025-09-23T10:06:13",
173 + "2025-09-23",
174 + "2025-09-23 10:06:13",
175 + "2025/09/23"
176 +};
177 +
178 +for (String format : testFormats) {
179 + LocalDateTime result = parseDateTime(format);
180 + System.out.println(format + " -> " + result);
181 +}
182 +```
183 +
184 +### 2. 边界测试
185 +- 测试极端时间值
186 +- 测试格式错误的时间
187 +- 测试空值和null值
188 +
189 +### 3. 性能测试
190 +- 测试大量时间解析的性能
191 +- 测试并发解析的安全性
192 +- 测试内存使用情况
193 +
194 +## 注意事项
195 +
196 +1. **时区一致性**: 确保前后端时区设置一致
197 +2. **格式验证**: 前端应验证时间格式的正确性
198 +3. **性能监控**: 监控时间解析的性能影响
199 +4. **日志记录**: 记录解析失败的情况便于调试
200 +
201 +## 相关文件
202 +
203 +- `UserQueryReq.java` - 查询请求DTO
204 +- `SysUserServiceImpl.java` - 服务实现类
205 +- `API接口文档.md` - API文档更新
1 +# 查询条件组装代码规范说明
2 +
3 +## 规范概述
4 +
5 +为了保持代码的一致性和可维护性,所有查询条件的组装都应该统一放在 `getQueryWrapper` 方法中,而不是在 Controller 层进行条件组装。
6 +
7 +## 修改内容
8 +
9 +### 1. 接口层修改
10 +
11 +**文件**: `SysUserService.java`
12 +
13 +**修改前**:
14 +```java
15 +LambdaQueryWrapper<SysUser> getQueryWrapper(SysUser user);
16 +```
17 +
18 +**修改后**:
19 +```java
20 +LambdaQueryWrapper<SysUser> getQueryWrapper(UserQueryReq queryReq);
21 +```
22 +
23 +### 2. 实现层修改
24 +
25 +**文件**: `SysUserServiceImpl.java`
26 +
27 +**修改前**:
28 +```java
29 +@Override
30 +public LambdaQueryWrapper<SysUser> getQueryWrapper(SysUser user) {
31 + LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
32 +
33 + // 基础查询条件
34 + if (StringUtils.isNotEmpty(user.getUsername())) {
35 + wrapper.like(SysUser::getUsername, user.getUsername());
36 + }
37 + // ... 其他条件
38 +
39 + return wrapper;
40 +}
41 +```
42 +
43 +**修改后**:
44 +```java
45 +@Override
46 +public LambdaQueryWrapper<SysUser> getQueryWrapper(UserQueryReq queryReq) {
47 + LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
48 +
49 + // 基础查询条件
50 + if (StringUtils.isNotEmpty(queryReq.getUsername())) {
51 + wrapper.like(SysUser::getUsername, queryReq.getUsername());
52 + }
53 +
54 + // 时间范围查询条件
55 + if (queryReq.getStartTime() != null) {
56 + wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
57 + }
58 + if (queryReq.getEndTime() != null) {
59 + wrapper.le(SysUser::getCreateTime, queryReq.getEndTime());
60 + }
61 +
62 + // 固定条件
63 + wrapper.eq(SysUser::getDelFlag, "0");
64 + wrapper.orderByDesc(SysUser::getCreateTime);
65 +
66 + return wrapper;
67 +}
68 +```
69 +
70 +### 3. Controller 层简化
71 +
72 +**文件**: `SysUserController.java`
73 +
74 +**修改前**:
75 +```java
76 +public ApiRes<IPage<UserRes>> list(UserQueryReq queryReq) {
77 + Page<SysUser> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
78 +
79 + // 构建查询条件
80 + SysUser queryUser = new SysUser();
81 + BeanUtils.copyProperties(queryReq, queryUser);
82 +
83 + // 获取基础查询条件
84 + LambdaQueryWrapper<SysUser> wrapper = sysUserService.getQueryWrapper(queryUser);
85 +
86 + // 添加时间范围查询条件
87 + if (queryReq.getStartTime() != null) {
88 + wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
89 + }
90 + if (queryReq.getEndTime() != null) {
91 + wrapper.le(SysUser::getCreateTime, queryReq.getEndTime());
92 + }
93 +
94 + IPage<SysUser> userPage = sysUserService.page(page, wrapper);
95 + // ... 其他逻辑
96 +}
97 +```
98 +
99 +**修改后**:
100 +```java
101 +public ApiRes<IPage<UserRes>> list(UserQueryReq queryReq) {
102 + Page<SysUser> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
103 +
104 + // 获取查询条件包装器(包含所有查询条件)
105 + LambdaQueryWrapper<SysUser> wrapper = sysUserService.getQueryWrapper(queryReq);
106 +
107 + IPage<SysUser> userPage = sysUserService.page(page, wrapper);
108 + // ... 其他逻辑
109 +}
110 +```
111 +
112 +## 代码规范优势
113 +
114 +### 1. 职责分离
115 +- **Controller 层**: 只负责接收请求和返回响应
116 +- **Service 层**: 负责业务逻辑和查询条件组装
117 +- **Mapper 层**: 负责数据库操作
118 +
119 +### 2. 代码复用
120 +- 查询条件组装逻辑集中在 Service 层
121 +- 其他 Controller 可以复用相同的查询逻辑
122 +- 避免重复代码
123 +
124 +### 3. 易于维护
125 +- 查询条件修改只需要在一个地方进行
126 +- 逻辑清晰,易于理解和调试
127 +- 便于单元测试
128 +
129 +### 4. 扩展性强
130 +- 新增查询条件只需要在 `getQueryWrapper` 方法中添加
131 +- 不影响 Controller 层的代码
132 +- 支持复杂的查询条件组合
133 +
134 +## 实现细节
135 +
136 +### 1. 参数类型统一
137 +- 使用 `UserQueryReq` 作为查询参数类型
138 +- 包含所有可能的查询条件
139 +- 支持分页参数和查询条件
140 +
141 +### 2. 查询条件处理
142 +```java
143 +// 字符串字段:模糊查询
144 +if (StringUtils.isNotEmpty(queryReq.getUsername())) {
145 + wrapper.like(SysUser::getUsername, queryReq.getUsername());
146 +}
147 +
148 +// 数值字段:精确查询
149 +if (queryReq.getStatus() != null) {
150 + wrapper.eq(SysUser::getStatus, queryReq.getStatus());
151 +}
152 +
153 +// 时间字段:范围查询
154 +if (queryReq.getStartTime() != null) {
155 + wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
156 +}
157 +```
158 +
159 +### 3. 固定条件处理
160 +```java
161 +// 软删除条件
162 +wrapper.eq(SysUser::getDelFlag, "0");
163 +
164 +// 排序条件
165 +wrapper.orderByDesc(SysUser::getCreateTime);
166 +```
167 +
168 +## 最佳实践
169 +
170 +### 1. 查询条件顺序
171 +1. 基础查询条件(用户名、姓名等)
172 +2. 状态查询条件
173 +3. 时间范围查询条件
174 +4. 固定条件(软删除、排序)
175 +
176 +### 2. 空值处理
177 +- 使用 `StringUtils.isNotEmpty()` 处理字符串字段
178 +- 使用 `!= null` 处理数值和时间字段
179 +- 避免空字符串和 null 值的查询
180 +
181 +### 3. 性能优化
182 +- 合理使用索引字段进行查询
183 +- 避免全表扫描
184 +- 使用合适的数据类型
185 +
186 +## 扩展指南
187 +
188 +### 1. 添加新的查询条件
189 +```java
190 +// 在 getQueryWrapper 方法中添加
191 +if (StringUtils.isNotEmpty(queryReq.getNewField())) {
192 + wrapper.like(SysUser::getNewField, queryReq.getNewField());
193 +}
194 +```
195 +
196 +### 2. 添加新的查询类型
197 +```java
198 +// 支持多值查询
199 +if (queryReq.getStatusList() != null && !queryReq.getStatusList().isEmpty()) {
200 + wrapper.in(SysUser::getStatus, queryReq.getStatusList());
201 +}
202 +
203 +// 支持时间范围查询
204 +if (queryReq.getStartTime() != null && queryReq.getEndTime() != null) {
205 + wrapper.between(SysUser::getCreateTime, queryReq.getStartTime(), queryReq.getEndTime());
206 +}
207 +```
208 +
209 +### 3. 添加复杂查询逻辑
210 +```java
211 +// 支持 OR 条件
212 +if (StringUtils.isNotEmpty(queryReq.getKeyword())) {
213 + wrapper.and(w -> w.like(SysUser::getUsername, queryReq.getKeyword())
214 + .or()
215 + .like(SysUser::getRealName, queryReq.getKeyword()));
216 +}
217 +```
218 +
219 +## 注意事项
220 +
221 +1. **参数验证**: 在 Service 层添加参数验证逻辑
222 +2. **异常处理**: 合理处理查询异常
223 +3. **日志记录**: 记录重要的查询操作
224 +4. **性能监控**: 监控查询性能,优化慢查询
225 +
226 +## 相关文件
227 +
228 +- `SysUserService.java` - 服务接口
229 +- `SysUserServiceImpl.java` - 服务实现
230 +- `SysUserController.java` - 控制器
231 +- `UserQueryReq.java` - 查询请求DTO
1 +# 用户列表时间查询功能说明
2 +
3 +## 功能概述
4 +
5 +后端用户列表查询接口已添加创建时间范围查询功能,支持按创建时间筛选用户数据。
6 +
7 +## 实现内容
8 +
9 +### 1. 数据库查询条件
10 +- **开始时间查询**: `create_time >= startTime`
11 +- **结束时间查询**: `create_time <= endTime`
12 +- **时间范围查询**: 同时支持开始时间和结束时间,实现时间范围筛选
13 +
14 +### 2. 技术实现
15 +
16 +#### 2.1 DTO 类修改
17 +**文件**: `UserQueryReq.java`
18 +```java
19 +/**
20 + * 开始时间(创建时间范围查询)
21 + */
22 +private LocalDateTime startTime;
23 +
24 +/**
25 + * 结束时间(创建时间范围查询)
26 + */
27 +private LocalDateTime endTime;
28 +```
29 +
30 +#### 2.2 Controller 层修改
31 +**文件**: `SysUserController.java`
32 +```java
33 +// 添加时间范围查询条件
34 +if (queryReq.getStartTime() != null) {
35 + wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
36 +}
37 +if (queryReq.getEndTime() != null) {
38 + wrapper.le(SysUser::getCreateTime, queryReq.getEndTime());
39 +}
40 +```
41 +
42 +#### 2.3 查询逻辑
43 +- 使用 MyBatis Plus 的 `LambdaQueryWrapper` 构建查询条件
44 +- `ge()` 方法实现大于等于查询(开始时间)
45 +- `le()` 方法实现小于等于查询(结束时间)
46 +- 支持单独使用开始时间或结束时间查询
47 +
48 +## API 接口说明
49 +
50 +### 接口路径
51 +`GET /api/system/user/list`
52 +
53 +### 新增参数
54 +| 参数名 | 类型 | 必填 | 说明 |
55 +|--------|------|------|------|
56 +| startTime | String | 否 | 开始时间,格式:yyyy-MM-ddTHH:mm:ss |
57 +| endTime | String | 否 | 结束时间,格式:yyyy-MM-ddTHH:mm:ss |
58 +
59 +### 请求示例
60 +```
61 +GET /api/system/user/list?startTime=2024-01-01T00:00:00&endTime=2024-12-31T23:59:59&pageNum=1&pageSize=10
62 +```
63 +
64 +### 查询逻辑
65 +1. **只有开始时间**: 查询创建时间 >= 开始时间的用户
66 +2. **只有结束时间**: 查询创建时间 <= 结束时间的用户
67 +3. **开始时间 + 结束时间**: 查询创建时间在指定范围内的用户
68 +4. **无时间参数**: 查询所有用户(原有逻辑)
69 +
70 +## 数据库查询示例
71 +
72 +### SQL 查询示例
73 +```sql
74 +-- 查询2024年1月创建的用户
75 +SELECT * FROM t_sys_user
76 +WHERE del_flag = '0'
77 +AND create_time >= '2024-01-01 00:00:00'
78 +AND create_time <= '2024-01-31 23:59:59'
79 +ORDER BY create_time DESC;
80 +
81 +-- 查询2024年1月1日之后创建的用户
82 +SELECT * FROM t_sys_user
83 +WHERE del_flag = '0'
84 +AND create_time >= '2024-01-01 00:00:00'
85 +ORDER BY create_time DESC;
86 +```
87 +
88 +### MyBatis Plus 查询条件
89 +```java
90 +LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
91 +wrapper.eq(SysUser::getDelFlag, "0"); // 未删除
92 +wrapper.ge(SysUser::getCreateTime, startTime); // 开始时间
93 +wrapper.le(SysUser::getCreateTime, endTime); // 结束时间
94 +wrapper.orderByDesc(SysUser::getCreateTime); // 按创建时间降序
95 +```
96 +
97 +## 时间格式说明
98 +
99 +### 前端传递格式
100 +- **日期格式**: `yyyy-MM-dd` (如: 2024-01-01)
101 +- **日期时间格式**: `yyyy-MM-ddTHH:mm:ss` (如: 2024-01-01T00:00:00)
102 +
103 +### 后端处理
104 +- 使用 `LocalDateTime` 类型接收时间参数
105 +- 自动进行类型转换和格式验证
106 +- 支持时区处理(基于服务器时区)
107 +
108 +## 性能优化
109 +
110 +### 1. 数据库索引
111 +确保 `create_time` 字段有适当的索引:
112 +```sql
113 +-- 创建时间索引
114 +CREATE INDEX idx_user_create_time ON t_sys_user(create_time);
115 +
116 +-- 复合索引(状态 + 创建时间)
117 +CREATE INDEX idx_user_status_create_time ON t_sys_user(status, create_time);
118 +```
119 +
120 +### 2. 查询优化
121 +- 时间范围查询使用索引
122 +- 避免全表扫描
123 +- 合理使用分页查询
124 +
125 +## 测试用例
126 +
127 +### 1. 基础功能测试
128 +```bash
129 +# 查询2024年1月创建的用户
130 +curl -X GET "http://localhost:8083/api/system/user/list?startTime=2024-01-01T00:00:00&endTime=2024-01-31T23:59:59"
131 +
132 +# 查询2024年1月1日之后创建的用户
133 +curl -X GET "http://localhost:8083/api/system/user/list?startTime=2024-01-01T00:00:00"
134 +
135 +# 查询2024年1月31日之前创建的用户
136 +curl -X GET "http://localhost:8083/api/system/user/list?endTime=2024-01-31T23:59:59"
137 +```
138 +
139 +### 2. 边界条件测试
140 +- 开始时间 = 结束时间
141 +- 开始时间 > 结束时间
142 +- 时间格式错误处理
143 +- 空值处理
144 +
145 +### 3. 性能测试
146 +- 大数据量下的查询性能
147 +- 索引使用情况
148 +- 查询响应时间
149 +
150 +## 注意事项
151 +
152 +1. **时间格式**: 前端传递的时间格式必须符合 `yyyy-MM-ddTHH:mm:ss` 格式
153 +2. **时区处理**: 后端使用服务器本地时区处理时间
154 +3. **索引优化**: 确保数据库表有适当的索引以提升查询性能
155 +4. **参数验证**: 开始时间不能晚于结束时间(可在前端或后端添加验证)
156 +5. **空值处理**: 时间参数为空时不添加查询条件
157 +
158 +## 扩展功能
159 +
160 +### 1. 可添加的查询条件
161 +- 更新时间范围查询
162 +- 最后登录时间范围查询
163 +- 多时间字段组合查询
164 +
165 +### 2. 可优化的功能
166 +- 时间格式自动转换
167 +- 时区统一处理
168 +- 查询结果缓存
169 +- 时间范围验证
170 +
171 +## 相关文件
172 +
173 +- `UserQueryReq.java` - 查询请求DTO
174 +- `SysUserController.java` - 用户控制器
175 +- `SysUserService.java` - 用户服务接口
176 +- `SysUserServiceImpl.java` - 用户服务实现
177 +- `API接口文档.md` - API文档更新