zhouhui.jiang

update

# 时间查询@JsonFormat注解支持说明
## 功能概述
后端用户查询接口使用 `@JsonFormat` 注解统一处理时间格式,简化了时间参数的处理逻辑,确保时间格式的一致性。
## 修改内容
### 1. DTO类修改
**文件**: `UserQueryReq.java`
**修改前**:
```java
private LocalDateTime startTime;
private LocalDateTime endTime;
```
**修改后**:
```java
/**
* 开始时间(创建时间范围查询)
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDateTime startTime;
/**
* 结束时间(创建时间范围查询)
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDateTime endTime;
```
### 2. 查询条件构建简化
**文件**: `SysUserServiceImpl.java`
**修改前**:
```java
// 开始时间:创建时间 >= 开始时间
if (StringUtils.isNotEmpty(queryReq.getStartTime())) {
LocalDateTime startTime = parseDateTime(queryReq.getStartTime());
if (startTime != null) {
wrapper.ge(SysUser::getCreateTime, startTime);
}
}
```
**修改后**:
```java
// 开始时间:创建时间 >= 开始时间
if (queryReq.getStartTime() != null) {
wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
}
```
## 支持的时间格式
### 1. 标准格式
- `2025-09-23` - 日期格式(推荐)
- 时区:GMT+8(中国标准时间)
### 2. @JsonFormat注解优势
- 自动格式验证
- 时区自动转换
- 代码简洁
- 性能优化
## 功能特点
### 1. 自动格式处理
- Jackson自动处理时间格式转换
- 统一的时区处理(GMT+8)
- 无需手动解析时间字符串
### 2. 代码简化
- 移除了复杂的时间解析逻辑
- 减少了代码维护成本
- 提高了代码可读性
### 3. 性能优化
- 利用Jackson的高效序列化/反序列化
- 减少自定义解析逻辑
- 更好的内存管理
## 使用示例
### 1. API调用示例
```bash
# 标准日期格式(推荐)
curl -X GET "http://localhost:8083/api/system/user/list?startTime=2025-09-23&endTime=2025-09-24"
```
### 2. 前端时间选择器
```javascript
// 前端生成的时间格式(yyyy-MM-dd)
const startTime = "2025-09-23"
const endTime = "2025-09-24"
// API调用
const params = {
startTime: startTime,
endTime: endTime,
pageNum: 1,
pageSize: 10
}
```
### 3. 数据库查询
```sql
-- 实际执行的SQL查询
SELECT * FROM t_sys_user
WHERE del_flag = '0'
AND create_time >= '2025-09-23 00:00:00'
AND create_time <= '2025-09-23 23:59:59'
ORDER BY create_time DESC;
```
## 错误处理
### 1. 格式错误处理
```java
// 解析失败时的处理
if (startTime == null) {
// 记录日志但不中断查询
System.err.println("时间格式解析失败: " + queryReq.getStartTime());
// 跳过该查询条件
}
```
### 2. 空值处理
```java
// 空值检查
if (StringUtils.isNotEmpty(queryReq.getStartTime())) {
// 只有非空时才进行解析
}
```
### 3. 时区处理
```java
// 时区信息移除
if (dateTimeStr.endsWith("Z") || dateTimeStr.contains("+") || dateTimeStr.contains("-")) {
String localTimeStr = dateTimeStr.replaceAll("[+-]\\d{2}:\\d{2}$", "").replace("Z", "");
return LocalDateTime.parse(localTimeStr, formatter);
}
```
## 性能考虑
### 1. 格式化器缓存
- 使用静态格式化器数组
- 避免重复创建格式化器对象
- 按使用频率排序
### 2. 解析优化
- 按常用格式优先尝试
- 快速失败机制
- 避免不必要的字符串操作
### 3. 内存管理
- 及时释放临时对象
- 避免字符串拼接
- 使用高效的字符串操作
## 测试建议
### 1. 格式测试
```java
// 测试各种时间格式
String[] testFormats = {
"2025-09-23T10:06:13.293Z",
"2025-09-23T10:06:13",
"2025-09-23",
"2025-09-23 10:06:13",
"2025/09/23"
};
for (String format : testFormats) {
LocalDateTime result = parseDateTime(format);
System.out.println(format + " -> " + result);
}
```
### 2. 边界测试
- 测试极端时间值
- 测试格式错误的时间
- 测试空值和null值
### 3. 性能测试
- 测试大量时间解析的性能
- 测试并发解析的安全性
- 测试内存使用情况
## 注意事项
1. **时区一致性**: 确保前后端时区设置一致
2. **格式验证**: 前端应验证时间格式的正确性
3. **性能监控**: 监控时间解析的性能影响
4. **日志记录**: 记录解析失败的情况便于调试
## 相关文件
- `UserQueryReq.java` - 查询请求DTO
- `SysUserServiceImpl.java` - 服务实现类
- `API接口文档.md` - API文档更新
# 查询条件组装代码规范说明
## 规范概述
为了保持代码的一致性和可维护性,所有查询条件的组装都应该统一放在 `getQueryWrapper` 方法中,而不是在 Controller 层进行条件组装。
## 修改内容
### 1. 接口层修改
**文件**: `SysUserService.java`
**修改前**:
```java
LambdaQueryWrapper<SysUser> getQueryWrapper(SysUser user);
```
**修改后**:
```java
LambdaQueryWrapper<SysUser> getQueryWrapper(UserQueryReq queryReq);
```
### 2. 实现层修改
**文件**: `SysUserServiceImpl.java`
**修改前**:
```java
@Override
public LambdaQueryWrapper<SysUser> getQueryWrapper(SysUser user) {
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
// 基础查询条件
if (StringUtils.isNotEmpty(user.getUsername())) {
wrapper.like(SysUser::getUsername, user.getUsername());
}
// ... 其他条件
return wrapper;
}
```
**修改后**:
```java
@Override
public LambdaQueryWrapper<SysUser> getQueryWrapper(UserQueryReq queryReq) {
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
// 基础查询条件
if (StringUtils.isNotEmpty(queryReq.getUsername())) {
wrapper.like(SysUser::getUsername, queryReq.getUsername());
}
// 时间范围查询条件
if (queryReq.getStartTime() != null) {
wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
}
if (queryReq.getEndTime() != null) {
wrapper.le(SysUser::getCreateTime, queryReq.getEndTime());
}
// 固定条件
wrapper.eq(SysUser::getDelFlag, "0");
wrapper.orderByDesc(SysUser::getCreateTime);
return wrapper;
}
```
### 3. Controller 层简化
**文件**: `SysUserController.java`
**修改前**:
```java
public ApiRes<IPage<UserRes>> list(UserQueryReq queryReq) {
Page<SysUser> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
// 构建查询条件
SysUser queryUser = new SysUser();
BeanUtils.copyProperties(queryReq, queryUser);
// 获取基础查询条件
LambdaQueryWrapper<SysUser> wrapper = sysUserService.getQueryWrapper(queryUser);
// 添加时间范围查询条件
if (queryReq.getStartTime() != null) {
wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
}
if (queryReq.getEndTime() != null) {
wrapper.le(SysUser::getCreateTime, queryReq.getEndTime());
}
IPage<SysUser> userPage = sysUserService.page(page, wrapper);
// ... 其他逻辑
}
```
**修改后**:
```java
public ApiRes<IPage<UserRes>> list(UserQueryReq queryReq) {
Page<SysUser> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
// 获取查询条件包装器(包含所有查询条件)
LambdaQueryWrapper<SysUser> wrapper = sysUserService.getQueryWrapper(queryReq);
IPage<SysUser> userPage = sysUserService.page(page, wrapper);
// ... 其他逻辑
}
```
## 代码规范优势
### 1. 职责分离
- **Controller 层**: 只负责接收请求和返回响应
- **Service 层**: 负责业务逻辑和查询条件组装
- **Mapper 层**: 负责数据库操作
### 2. 代码复用
- 查询条件组装逻辑集中在 Service 层
- 其他 Controller 可以复用相同的查询逻辑
- 避免重复代码
### 3. 易于维护
- 查询条件修改只需要在一个地方进行
- 逻辑清晰,易于理解和调试
- 便于单元测试
### 4. 扩展性强
- 新增查询条件只需要在 `getQueryWrapper` 方法中添加
- 不影响 Controller 层的代码
- 支持复杂的查询条件组合
## 实现细节
### 1. 参数类型统一
- 使用 `UserQueryReq` 作为查询参数类型
- 包含所有可能的查询条件
- 支持分页参数和查询条件
### 2. 查询条件处理
```java
// 字符串字段:模糊查询
if (StringUtils.isNotEmpty(queryReq.getUsername())) {
wrapper.like(SysUser::getUsername, queryReq.getUsername());
}
// 数值字段:精确查询
if (queryReq.getStatus() != null) {
wrapper.eq(SysUser::getStatus, queryReq.getStatus());
}
// 时间字段:范围查询
if (queryReq.getStartTime() != null) {
wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
}
```
### 3. 固定条件处理
```java
// 软删除条件
wrapper.eq(SysUser::getDelFlag, "0");
// 排序条件
wrapper.orderByDesc(SysUser::getCreateTime);
```
## 最佳实践
### 1. 查询条件顺序
1. 基础查询条件(用户名、姓名等)
2. 状态查询条件
3. 时间范围查询条件
4. 固定条件(软删除、排序)
### 2. 空值处理
- 使用 `StringUtils.isNotEmpty()` 处理字符串字段
- 使用 `!= null` 处理数值和时间字段
- 避免空字符串和 null 值的查询
### 3. 性能优化
- 合理使用索引字段进行查询
- 避免全表扫描
- 使用合适的数据类型
## 扩展指南
### 1. 添加新的查询条件
```java
// 在 getQueryWrapper 方法中添加
if (StringUtils.isNotEmpty(queryReq.getNewField())) {
wrapper.like(SysUser::getNewField, queryReq.getNewField());
}
```
### 2. 添加新的查询类型
```java
// 支持多值查询
if (queryReq.getStatusList() != null && !queryReq.getStatusList().isEmpty()) {
wrapper.in(SysUser::getStatus, queryReq.getStatusList());
}
// 支持时间范围查询
if (queryReq.getStartTime() != null && queryReq.getEndTime() != null) {
wrapper.between(SysUser::getCreateTime, queryReq.getStartTime(), queryReq.getEndTime());
}
```
### 3. 添加复杂查询逻辑
```java
// 支持 OR 条件
if (StringUtils.isNotEmpty(queryReq.getKeyword())) {
wrapper.and(w -> w.like(SysUser::getUsername, queryReq.getKeyword())
.or()
.like(SysUser::getRealName, queryReq.getKeyword()));
}
```
## 注意事项
1. **参数验证**: 在 Service 层添加参数验证逻辑
2. **异常处理**: 合理处理查询异常
3. **日志记录**: 记录重要的查询操作
4. **性能监控**: 监控查询性能,优化慢查询
## 相关文件
- `SysUserService.java` - 服务接口
- `SysUserServiceImpl.java` - 服务实现
- `SysUserController.java` - 控制器
- `UserQueryReq.java` - 查询请求DTO
# 用户列表时间查询功能说明
## 功能概述
后端用户列表查询接口已添加创建时间范围查询功能,支持按创建时间筛选用户数据。
## 实现内容
### 1. 数据库查询条件
- **开始时间查询**: `create_time >= startTime`
- **结束时间查询**: `create_time <= endTime`
- **时间范围查询**: 同时支持开始时间和结束时间,实现时间范围筛选
### 2. 技术实现
#### 2.1 DTO 类修改
**文件**: `UserQueryReq.java`
```java
/**
* 开始时间(创建时间范围查询)
*/
private LocalDateTime startTime;
/**
* 结束时间(创建时间范围查询)
*/
private LocalDateTime endTime;
```
#### 2.2 Controller 层修改
**文件**: `SysUserController.java`
```java
// 添加时间范围查询条件
if (queryReq.getStartTime() != null) {
wrapper.ge(SysUser::getCreateTime, queryReq.getStartTime());
}
if (queryReq.getEndTime() != null) {
wrapper.le(SysUser::getCreateTime, queryReq.getEndTime());
}
```
#### 2.3 查询逻辑
- 使用 MyBatis Plus 的 `LambdaQueryWrapper` 构建查询条件
- `ge()` 方法实现大于等于查询(开始时间)
- `le()` 方法实现小于等于查询(结束时间)
- 支持单独使用开始时间或结束时间查询
## API 接口说明
### 接口路径
`GET /api/system/user/list`
### 新增参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| startTime | String | 否 | 开始时间,格式:yyyy-MM-ddTHH:mm:ss |
| endTime | String | 否 | 结束时间,格式:yyyy-MM-ddTHH:mm:ss |
### 请求示例
```
GET /api/system/user/list?startTime=2024-01-01T00:00:00&endTime=2024-12-31T23:59:59&pageNum=1&pageSize=10
```
### 查询逻辑
1. **只有开始时间**: 查询创建时间 >= 开始时间的用户
2. **只有结束时间**: 查询创建时间 <= 结束时间的用户
3. **开始时间 + 结束时间**: 查询创建时间在指定范围内的用户
4. **无时间参数**: 查询所有用户(原有逻辑)
## 数据库查询示例
### SQL 查询示例
```sql
-- 查询2024年1月创建的用户
SELECT * FROM t_sys_user
WHERE del_flag = '0'
AND create_time >= '2024-01-01 00:00:00'
AND create_time <= '2024-01-31 23:59:59'
ORDER BY create_time DESC;
-- 查询2024年1月1日之后创建的用户
SELECT * FROM t_sys_user
WHERE del_flag = '0'
AND create_time >= '2024-01-01 00:00:00'
ORDER BY create_time DESC;
```
### MyBatis Plus 查询条件
```java
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getDelFlag, "0"); // 未删除
wrapper.ge(SysUser::getCreateTime, startTime); // 开始时间
wrapper.le(SysUser::getCreateTime, endTime); // 结束时间
wrapper.orderByDesc(SysUser::getCreateTime); // 按创建时间降序
```
## 时间格式说明
### 前端传递格式
- **日期格式**: `yyyy-MM-dd` (如: 2024-01-01)
- **日期时间格式**: `yyyy-MM-ddTHH:mm:ss` (如: 2024-01-01T00:00:00)
### 后端处理
- 使用 `LocalDateTime` 类型接收时间参数
- 自动进行类型转换和格式验证
- 支持时区处理(基于服务器时区)
## 性能优化
### 1. 数据库索引
确保 `create_time` 字段有适当的索引:
```sql
-- 创建时间索引
CREATE INDEX idx_user_create_time ON t_sys_user(create_time);
-- 复合索引(状态 + 创建时间)
CREATE INDEX idx_user_status_create_time ON t_sys_user(status, create_time);
```
### 2. 查询优化
- 时间范围查询使用索引
- 避免全表扫描
- 合理使用分页查询
## 测试用例
### 1. 基础功能测试
```bash
# 查询2024年1月创建的用户
curl -X GET "http://localhost:8083/api/system/user/list?startTime=2024-01-01T00:00:00&endTime=2024-01-31T23:59:59"
# 查询2024年1月1日之后创建的用户
curl -X GET "http://localhost:8083/api/system/user/list?startTime=2024-01-01T00:00:00"
# 查询2024年1月31日之前创建的用户
curl -X GET "http://localhost:8083/api/system/user/list?endTime=2024-01-31T23:59:59"
```
### 2. 边界条件测试
- 开始时间 = 结束时间
- 开始时间 > 结束时间
- 时间格式错误处理
- 空值处理
### 3. 性能测试
- 大数据量下的查询性能
- 索引使用情况
- 查询响应时间
## 注意事项
1. **时间格式**: 前端传递的时间格式必须符合 `yyyy-MM-ddTHH:mm:ss` 格式
2. **时区处理**: 后端使用服务器本地时区处理时间
3. **索引优化**: 确保数据库表有适当的索引以提升查询性能
4. **参数验证**: 开始时间不能晚于结束时间(可在前端或后端添加验证)
5. **空值处理**: 时间参数为空时不添加查询条件
## 扩展功能
### 1. 可添加的查询条件
- 更新时间范围查询
- 最后登录时间范围查询
- 多时间字段组合查询
### 2. 可优化的功能
- 时间格式自动转换
- 时区统一处理
- 查询结果缓存
- 时间范围验证
## 相关文件
- `UserQueryReq.java` - 查询请求DTO
- `SysUserController.java` - 用户控制器
- `SysUserService.java` - 用户服务接口
- `SysUserServiceImpl.java` - 用户服务实现
- `API接口文档.md` - API文档更新