接口优化
This commit is contained in:
+182
-30
@@ -6,15 +6,15 @@ type: "always_apply"
|
||||
|
||||
## 基础设置
|
||||
|
||||
1. 保持对话语言为中文/英文
|
||||
2. 系统环境:Mac
|
||||
1. 保持对话语言为中文
|
||||
2. 不允许在未经允许的情况下删除代码和文件,不允许破坏已经正常的业务代码
|
||||
3. 执行终端命令时要关注执行情况,避免无效等待
|
||||
|
||||
## 代码规范
|
||||
|
||||
4. 生成代码时必须添加类级和函数级注释
|
||||
5. 使用import导包,禁止使用全限定名称引用类
|
||||
6. 禁止使用枚举类型作为entity、request、response、dto对象的字段类型
|
||||
6. 禁止使用枚举类型作为entity、request、response、dto对象的字段类型,禁止以枚举类作为方法的入参,确保接口的通用性和可扩展性
|
||||
7. 新增数据的id使用已存在的雪花算法生成器生成
|
||||
|
||||
## 架构规范
|
||||
@@ -34,46 +34,198 @@ type: "always_apply"
|
||||
- 使用项目已有的Result做接口返回
|
||||
13. 接口和方法参数不允许超过两个,超过时使用request或DTO对象封装
|
||||
14. Controller层路由禁止添加/api前缀
|
||||
15. Controller层接口的Mapping注解value属性值不允许重复且不允许为空
|
||||
15. Controller层接口的Mapping注解value属性值不允许重复且不允许为空,必须明确指定value属性值且使用驼峰结构命名,避免使用下划线分隔符。所有接口注解必须显式指定value属性,如@GetMapping(value = "/page")、@PostMapping(value = "/create")等,禁止使用@GetMapping()或@PostMapping等空注解形式
|
||||
16. 用户相关接口禁止直接传递用户id,需要后端根据token获取当前登录用户信息
|
||||
17. Controller层接口的Mapping注解(PostMapping、GetMapping、PutMapping、DeleteMapping等)的value属性值要简洁明了,与接口作用相关,名称不宜过长,使用驼峰结构命名
|
||||
18. 禁止使用/{param}格式的路径参数,避免网关路由冲突和分发错误
|
||||
19. 所有接口注解必须明确指定value属性,不允许使用空注解
|
||||
20. 路径参数统一使用@RequestParam而非@PathVariable,确保网关分发准确性
|
||||
21. 接口路径命名应具有明确的语义,避免使用通用词汇如/get、/list等
|
||||
22. 批量操作接口应使用专门的Request对象封装参数,而非直接传递List
|
||||
23. 接口路径应避免层级过深,建议不超过3级路径结构
|
||||
24. 更新操作应直接使用封装了ID和其他数据的Request对象,而不是单独传递ID参数。Service层方法应保持参数简洁,业务逻辑所需数据应全部包含在Request对象中
|
||||
17. 禁止使用/{param}格式的路径参数,避免网关路由冲突和分发错误
|
||||
18. 路径参数统一使用@RequestParam而非@PathVariable,确保网关分发准确性
|
||||
19. 接口路径命名应具有明确的语义,避免使用通用词汇如/get、/list等
|
||||
20. 批量操作接口应使用专门的Request对象封装参数,而非直接传递List
|
||||
21. 接口路径应避免层级过深,建议不超过3级路径结构
|
||||
22. 更新操作应直接使用封装了ID和其他数据的Request对象,而不是单独传递ID参数。Service层方法应保持参数简洁,业务逻辑所需数据应全部包含在Request对象中
|
||||
23. Controller层接口应保持简洁,避免为特定字段创建独立的更新方法(如updateStatus等),应只保留一个通用的update方法,具体的业务逻辑在Service层实现
|
||||
24. Service层在执行更新操作时,应对每个字段进行空值检查,只更新非空字段,避免空字段覆盖原有值,确保数据完整性
|
||||
25. Controller层应避免创建多个特定条件的查询接口,只保留一个分页查询方法,通过在请求对象中包含所有可查询字段来支持不同的查询需求
|
||||
26. 分页查询接口应支持模糊查询和精确查询,通过在Service层根据字段类型和业务需求判断查询方式
|
||||
|
||||
## 环境配置
|
||||
|
||||
25. 为不同环境(local、dev、prod)创建单独配置文件,部署时通过参数选择
|
||||
26. 启动服务时禁止擅自修改端口号,使用配置文件中的端口设置
|
||||
27. 为不同环境(local、dev、prod)创建单独配置文件,部署时通过参数选择
|
||||
28. 启动服务时禁止擅自修改端口号,使用配置文件中的端口设置
|
||||
|
||||
## 数据库规范
|
||||
|
||||
27. 所有数据表必须包含创建时间(create_time)和更新时间(update_time)字段
|
||||
28. 删除操作优先使用逻辑删除,添加deleted字段标识
|
||||
29. 数据库字段命名使用下划线分隔,Java实体类使用驼峰命名
|
||||
30. 优先使用LambdaQueryWrapper构造条件查询,避免硬编码字段名
|
||||
31. 使用Lambda表达式引用实体类属性,提高代码可维护性和类型安全
|
||||
32. 复杂查询条件应使用LambdaQueryWrapper的链式调用,保持代码清晰
|
||||
33. 避免在查询条件中使用字符串字段名,防止字段名变更导致的运行时错误
|
||||
29. 所有数据表必须包含创建时间(create_time)和更新时间(update_time)字段
|
||||
30. 删除操作优先使用逻辑删除,添加deleted字段标识
|
||||
31. 数据库字段命名使用下划线分隔,Java实体类使用驼峰命名
|
||||
32. 优先使用LambdaQueryWrapper构造条件查询,避免硬编码字段名
|
||||
33. 使用Lambda表达式引用实体类属性,提高代码可维护性和类型安全
|
||||
34. 复杂查询条件应使用LambdaQueryWrapper的链式调用,保持代码清晰
|
||||
35. 避免在查询条件中使用字符串字段名,防止字段名变更导致的运行时错误
|
||||
|
||||
## 安全规范
|
||||
|
||||
34. 所有外部输入必须进行参数校验
|
||||
35. 敏感信息不得在日志中输出
|
||||
36. 数据库操作必须使用参数化查询,防止SQL注入
|
||||
36. 所有外部输入必须进行参数校验
|
||||
37. 敏感信息不得在日志中输出
|
||||
38. 数据库操作必须使用参数化查询,防止SQL注入
|
||||
|
||||
## 性能规范
|
||||
|
||||
37. 避免N+1查询问题,合理使用批量查询
|
||||
38. 大数据量查询必须分页处理
|
||||
39. 缓存策略要考虑数据一致性问题
|
||||
39. 避免N+1查询问题,合理使用批量查询
|
||||
40. 大数据量查询必须分页处理
|
||||
41. 缓存策略要考虑数据一致性问题
|
||||
|
||||
## 日志规范
|
||||
|
||||
40. 关键业务操作必须记录操作日志
|
||||
41. 异常信息要包含足够的上下文信息
|
||||
42. 生产环境禁止输出debug级别日志
|
||||
42. 关键业务操作必须记录操作日志
|
||||
43. 异常信息要包含足够的上下文信息
|
||||
44. 生产环境禁止输出debug级别日志
|
||||
|
||||
---
|
||||
|
||||
## Java Spring Boot 项目开发与代码质量保障规范(扩展)
|
||||
|
||||
适用范围:本规范适用于 logistics-finance 项目所有后端模块(common、gateway、auth、user、order、waybill、vehicle、finance、report、ai、file)。若与本文件前文条款或现有项目规范冲突,以更严格者为准。
|
||||
|
||||
### 一、代码规范完善
|
||||
|
||||
- 编码标准
|
||||
- 严格开启编译参数校验与空指针警告,禁止忽略 IDE/编译器提示。
|
||||
- 代码提交前必须通过本地编译、单元测试、静态扫描(如存在)。
|
||||
- 严禁出现魔法值:使用常量或配置项;跨模块常量放在 common 模块统一管理。
|
||||
- 日志分级:业务关键信息用 info,调试用 debug(生产禁用),异常用 error,禁止打印敏感信息(密码、密钥、Token、手机号等)。
|
||||
|
||||
- 命名规范
|
||||
- 类:PascalCase;方法/变量:camelCase;常量:UPPER_SNAKE_CASE;包名全小写。
|
||||
- 接口路径参照现有规则(禁止 /api 前缀、禁止 PathVariable、Mapping value 必须显式且驼峰命名)。
|
||||
|
||||
- 注释规范
|
||||
- 类注释需包含:职责/用途、作者、创建时间、版本;方法注释需包含:用途、参数、返回、可能抛出的异常。
|
||||
- 复杂算法/易错逻辑必须添加行内注释;废弃方法使用 @Deprecated 并标注替代方案与移除计划。
|
||||
|
||||
- 代码结构规范(建议包结构)
|
||||
- controller、service、service.impl、repository(mapper/dao)、domain/entity、converter、config、client(feign)、event、facade、task、util、constant。
|
||||
- DTO/Request/Response 与 Entity 分离;Controller 不做对象互转,统一在 Service/Converter 层完成。
|
||||
|
||||
### 二、分层架构规范
|
||||
|
||||
- Controller 层职责
|
||||
- 接收请求、参数校验(Jakarta Validation)、鉴权校验(由网关/拦截器)、调用 Service,统一使用 `Result<T>` 返回。
|
||||
- 禁止:业务逻辑、事务控制、数据访问、实体与请求/响应的相互转换。
|
||||
|
||||
- Service 层职责
|
||||
- 聚合业务逻辑、领域编排、事务边界控制(@Transactional,读写分离时仅在写方法上标注),幂等与重试策略在此实现。
|
||||
- 方法命名与 Controller 对应,入参使用 Request 对象,出参使用 Response 对象。
|
||||
|
||||
- Repository 层职责
|
||||
- 仅做数据访问,统一使用 MyBatis-Plus(优先 LambdaQueryWrapper,避免硬编码字段)。
|
||||
|
||||
- Entity 规范
|
||||
- 统一继承 BaseEntity,字段包含:created_by、create_time、updated_by、update_time、logical_delete、remark(与项目约定一致)。
|
||||
- 禁止使用枚举作为实体/DTO 字段类型;以 String/Integer 存储并在注释中补充取值说明。
|
||||
|
||||
### 三、统一异常与返回结果
|
||||
|
||||
- 全局异常处理
|
||||
- 继续使用 common-web 的 GlobalExceptionHandler;禁止在业务代码中大面积 try-catch,异常由全局处理。
|
||||
- 业务异常一律抛出 BusinessException;校验异常统一转为 400 语义返回。
|
||||
|
||||
- 统一返回结构
|
||||
- 统一使用 common-core 的 Result<T> 与 ResultCode;保证 traceId、timestamp 一致性。
|
||||
- Controller 层仅返回 Result,不直接返回实体或集合。
|
||||
|
||||
- 参数校验
|
||||
- 使用 jakarta.validation 注解(@NotNull、@Size 等),在 Request 对象上标注;Controller 使用 @Validated。
|
||||
|
||||
### 四、数据库操作规范
|
||||
|
||||
- 设计规范
|
||||
- 数据表必须包含 create_time、update_time;删除优先逻辑删除 logical_delete;字段命名下划线风格。
|
||||
- 建议仅创建必要索引;避免外键约束(由应用层保证一致性)。
|
||||
|
||||
- 查询构建
|
||||
- 优先使用 LambdaQueryWrapper 和链式调用;分页查询必须使用分页插件;大数据量必须分页。
|
||||
- 避免 N+1 查询;需要时使用批量查询或联表映射。
|
||||
|
||||
- 事务管理
|
||||
- 仅在 Service 层声明事务;方法内仅包含数据库操作或与数据库一致性相关的远程调用;跨服务一致性使用可靠消息/补偿方案。
|
||||
|
||||
---
|
||||
|
||||
## 二、代码质量保障机制
|
||||
|
||||
- 代码审查流程(Code Review)
|
||||
- 所有变更必须走 PR;至少 1 名同组开发+1 名模块 Owner 审核通过方可合并。
|
||||
- 审查清单:命名/注释/分层边界/异常处理/日志/性能/安全/接口兼容性/测试覆盖率/静态扫描告警。
|
||||
- PR 模板需包含:改动描述、影响面、回归范围、测试说明、回滚方案。
|
||||
|
||||
- 静态代码分析与格式
|
||||
- 集成 Checkstyle(代码风格)、SpotBugs/PMD(潜在缺陷)、SonarQube(质量门禁),EditorConfig/格式化工具保持一致风格。
|
||||
- 质量门禁建议:
|
||||
- 新增代码覆盖率 >= 80%,全局覆盖率 >= 70%;
|
||||
- Blocker/Critical 问题为 0;
|
||||
- 重复代码率 < 3%;
|
||||
- 圈复杂度阈值:方法 < 10,类 < 80。
|
||||
|
||||
- 测试规范
|
||||
- 单元测试:命名 {ClassName}Test,方法名 should_xxx_when_xxx;使用 Mockito/AssertJ;隔离外部依赖。
|
||||
- 集成测试:使用 @SpringBootTest,测试容器或嵌入式组件替代真实依赖;构造典型场景与边界场景。
|
||||
- 覆盖率准则:核心业务与关键分支必须覆盖(正常/异常/边界/空数据)。
|
||||
- 用例分层:Service 层单测覆盖业务规则;Controller 层使用 MockMvc;Repository 层覆盖复杂 SQL 与多条件查询。
|
||||
|
||||
- 重构指导原则
|
||||
- 小步快跑、单一职责、先加测试再重构;保持对外行为一致(新增特性使用特性开关/版本控制)。
|
||||
- 严禁大规模跨模块重构合并在单次 PR;拆分为多个可审查的独立提交。
|
||||
|
||||
---
|
||||
|
||||
## 三、变更管理流程
|
||||
|
||||
- 变更前影响分析
|
||||
- 维度:接口兼容性(路径/入参/语义)、数据库(表/字段/索引/数据迁移)、配置(Nacos/环境变量)、依赖版本、性能、容量与并发、安全与合规、可回滚性。
|
||||
- 产出:影响分析文档与回滚方案(包含回滚脚本/开关)。
|
||||
|
||||
- 回归测试策略
|
||||
- 冒烟覆盖主干流程;模块级回归覆盖变更影响域;跨服务联调验证接口契约(通过网关)。
|
||||
- 回归范围基于依赖关系与影响分析确定;优先自动化回归,必要时补充手工用例。
|
||||
|
||||
- 版本管理与发布
|
||||
- 分支策略建议:
|
||||
- 主分支:master;
|
||||
- 功能分支:feature/{module}-{short-desc};
|
||||
- 预发布:release/x.y.z;
|
||||
- 紧急修复:hotfix/x.y.z;
|
||||
- 版本号:语义化版本 SemVer(MAJOR.MINOR.PATCH),发布时打 Tag;所有服务端口遵循 280xx 约定(配置文件统一)。
|
||||
|
||||
- 紧急修复与常规开发
|
||||
- 紧急修复:创建 hotfix → 修复 → 快速测试 → 合并 master 与 release → 立即发布 → 追补测试与文档。
|
||||
- 常规开发:feature → PR 审核 → 合并 → 集成测试 → 发布。
|
||||
|
||||
---
|
||||
|
||||
## 四、持续改进机制
|
||||
|
||||
- 规范评审与更新
|
||||
- 每月一次质量例会,评审新增问题与改进项;规范变更需在本文件留痕(版本/日期/变更点)。
|
||||
|
||||
- 质量指标监控(建议在 Sonar/日志/监控平台看板化)
|
||||
- 构建成功率、静态问题趋势、单元测试覆盖率、平均修复时长、缺陷密度、接口成功率、P95/P99 响应时间、数据库慢查询比例。
|
||||
|
||||
- 团队培训计划
|
||||
- 新人入项必读规范与代码演练;关键模块轮训;每季度至少一次专题分享(性能调优/安全加固/重构实践)。
|
||||
|
||||
- 反馈闭环
|
||||
- 通过代码评审、事后复盘(Postmortem)、问题工单收敛到规范条款;指定责任人与截止时间;下次评审验证落地结果。
|
||||
|
||||
---
|
||||
|
||||
## 附:标准检查清单(节选)
|
||||
|
||||
- PR 自检
|
||||
- 命名/注释达标;无魔法值;日志分级正确且无敏感信息;空指针风险已处理;删除代码/文件已获批准。
|
||||
- Controller 不含业务逻辑;路由命名与网关规范一致;统一 Result 返回;参数校验齐全;不使用 PathVariable。
|
||||
- Service 事务边界清晰;仅更新非空字段;分页/批量/幂等校验到位。
|
||||
- Repository 使用 LambdaQueryWrapper;避免硬编码字段;SQL 有必要索引;避免 N+1。
|
||||
- 测试覆盖关键路径与边界;新增功能附带测试;CI 静态扫描无阻断级问题。
|
||||
|
||||
- 回滚准备
|
||||
- 是否具备配置开关/灰度发布策略;是否提供回滚脚本;数据库迁移是否可逆(或给出补偿方案)。
|
||||
|
||||
+186
-30
@@ -4,23 +4,27 @@ alwaysApply: true
|
||||
# 项目开发规则
|
||||
|
||||
## 基础设置
|
||||
1. 保持对话语言为中文/英文
|
||||
2. 系统环境:Mac
|
||||
|
||||
1. 保持对话语言为中文
|
||||
2. 不允许在未经允许的情况下删除代码和文件,不允许破坏已经正常的业务代码
|
||||
3. 执行终端命令时要关注执行情况,避免无效等待
|
||||
|
||||
## 代码规范
|
||||
|
||||
4. 生成代码时必须添加类级和函数级注释
|
||||
5. 使用import导包,禁止使用全限定名称引用类
|
||||
6. 禁止使用枚举类型作为entity、request、response、dto对象的字段类型
|
||||
6. 禁止使用枚举类型作为entity、request、response、dto对象的字段类型,禁止以枚举类作为方法的入参,确保接口的通用性和可扩展性
|
||||
7. 新增数据的id使用已存在的雪花算法生成器生成
|
||||
|
||||
## 架构规范
|
||||
|
||||
8. 所有开发必须遵循当前项目规范
|
||||
9. Controller层禁止添加业务逻辑
|
||||
10. 使用全局异常处理,禁止使用try-catch
|
||||
11. 前端接口访问尽可能走网关调用
|
||||
|
||||
## 接口设计规范
|
||||
|
||||
12. Controller层接口定义要完整:
|
||||
- 入参使用request封装传递到service层
|
||||
- service层的方法命名与controller层定义的接口的方法名称保持一致
|
||||
@@ -29,46 +33,198 @@ alwaysApply: true
|
||||
- 使用项目已有的Result做接口返回
|
||||
13. 接口和方法参数不允许超过两个,超过时使用request或DTO对象封装
|
||||
14. Controller层路由禁止添加/api前缀
|
||||
15. Controller层接口的Mapping注解value属性值不允许重复且不允许为空
|
||||
15. Controller层接口的Mapping注解value属性值不允许重复且不允许为空,必须明确指定value属性值且使用驼峰结构命名,避免使用下划线分隔符。所有接口注解必须显式指定value属性,如@GetMapping(value = "/page")、@PostMapping(value = "/create")等,禁止使用@GetMapping()或@PostMapping等空注解形式
|
||||
16. 用户相关接口禁止直接传递用户id,需要后端根据token获取当前登录用户信息
|
||||
17. Controller层接口的Mapping注解(PostMapping、GetMapping、PutMapping、DeleteMapping等)的value属性值要简洁明了,与接口作用相关,名称不宜过长,使用驼峰结构命名
|
||||
18. 禁止使用/{param}格式的路径参数,避免网关路由冲突和分发错误
|
||||
19. 所有接口注解必须明确指定value属性,不允许使用空注解
|
||||
20. 路径参数统一使用@RequestParam而非@PathVariable,确保网关分发准确性
|
||||
21. 接口路径命名应具有明确的语义,避免使用通用词汇如/get、/list等
|
||||
22. 批量操作接口应使用专门的Request对象封装参数,而非直接传递List
|
||||
23. 接口路径应避免层级过深,建议不超过3级路径结构
|
||||
24. 更新操作应直接使用封装了ID和其他数据的Request对象,而不是单独传递ID参数。Service层方法应保持参数简洁,业务逻辑所需数据应全部包含在Request对象中
|
||||
17. 禁止使用/{param}格式的路径参数,避免网关路由冲突和分发错误
|
||||
18. 路径参数统一使用@RequestParam而非@PathVariable,确保网关分发准确性
|
||||
19. 接口路径命名应具有明确的语义,避免使用通用词汇如/get、/list等
|
||||
20. 批量操作接口应使用专门的Request对象封装参数,而非直接传递List
|
||||
21. 接口路径应避免层级过深,建议不超过3级路径结构
|
||||
22. 更新操作应直接使用封装了ID和其他数据的Request对象,而不是单独传递ID参数。Service层方法应保持参数简洁,业务逻辑所需数据应全部包含在Request对象中
|
||||
23. Controller层接口应保持简洁,避免为特定字段创建独立的更新方法(如updateStatus等),应只保留一个通用的update方法,具体的业务逻辑在Service层实现
|
||||
24. Service层在执行更新操作时,应对每个字段进行空值检查,只更新非空字段,避免空字段覆盖原有值,确保数据完整性
|
||||
25. Controller层应避免创建多个特定条件的查询接口,只保留一个分页查询方法,通过在请求对象中包含所有可查询字段来支持不同的查询需求
|
||||
26. 分页查询接口应支持模糊查询和精确查询,通过在Service层根据字段类型和业务需求判断查询方式
|
||||
|
||||
## 环境配置
|
||||
|
||||
25. 为不同环境(local、dev、prod)创建单独配置文件,部署时通过参数选择
|
||||
26. 启动服务时禁止擅自修改端口号,使用配置文件中的端口设置
|
||||
27. 为不同环境(local、dev、prod)创建单独配置文件,部署时通过参数选择
|
||||
28. 启动服务时禁止擅自修改端口号,使用配置文件中的端口设置
|
||||
|
||||
## 数据库规范
|
||||
|
||||
27. 所有数据表必须包含创建时间(create_time)和更新时间(update_time)字段
|
||||
28. 删除操作优先使用逻辑删除,添加deleted字段标识
|
||||
29. 数据库字段命名使用下划线分隔,Java实体类使用驼峰命名
|
||||
30. 优先使用LambdaQueryWrapper构造条件查询,避免硬编码字段名
|
||||
31. 使用Lambda表达式引用实体类属性,提高代码可维护性和类型安全
|
||||
32. 复杂查询条件应使用LambdaQueryWrapper的链式调用,保持代码清晰
|
||||
33. 避免在查询条件中使用字符串字段名,防止字段名变更导致的运行时错误
|
||||
29. 所有数据表必须包含创建时间(create_time)和更新时间(update_time)字段
|
||||
30. 删除操作优先使用逻辑删除,添加deleted字段标识
|
||||
31. 数据库字段命名使用下划线分隔,Java实体类使用驼峰命名
|
||||
32. 优先使用LambdaQueryWrapper构造条件查询,避免硬编码字段名
|
||||
33. 使用Lambda表达式引用实体类属性,提高代码可维护性和类型安全
|
||||
34. 复杂查询条件应使用LambdaQueryWrapper的链式调用,保持代码清晰
|
||||
35. 避免在查询条件中使用字符串字段名,防止字段名变更导致的运行时错误
|
||||
|
||||
## 安全规范
|
||||
|
||||
34. 所有外部输入必须进行参数校验
|
||||
35. 敏感信息不得在日志中输出
|
||||
36. 数据库操作必须使用参数化查询,防止SQL注入
|
||||
36. 所有外部输入必须进行参数校验
|
||||
37. 敏感信息不得在日志中输出
|
||||
38. 数据库操作必须使用参数化查询,防止SQL注入
|
||||
|
||||
## 性能规范
|
||||
|
||||
37. 避免N+1查询问题,合理使用批量查询
|
||||
38. 大数据量查询必须分页处理
|
||||
39. 缓存策略要考虑数据一致性问题
|
||||
39. 避免N+1查询问题,合理使用批量查询
|
||||
40. 大数据量查询必须分页处理
|
||||
41. 缓存策略要考虑数据一致性问题
|
||||
|
||||
## 日志规范
|
||||
|
||||
40. 关键业务操作必须记录操作日志
|
||||
41. 异常信息要包含足够的上下文信息
|
||||
42. 生产环境禁止输出debug级别日志
|
||||
42. 关键业务操作必须记录操作日志
|
||||
43. 异常信息要包含足够的上下文信息
|
||||
44. 生产环境禁止输出debug级别日志
|
||||
|
||||
---
|
||||
|
||||
## Java Spring Boot 项目开发与代码质量保障规范(扩展)
|
||||
|
||||
适用范围:本规范适用于 logistics-finance 项目所有后端模块(common、gateway、auth、user、order、waybill、vehicle、finance、report、ai、file)。若与本文件前文条款或现有项目规范冲突,以更严格者为准。
|
||||
|
||||
### 一、代码规范完善
|
||||
|
||||
- 编码标准
|
||||
- 严格开启编译参数校验与空指针警告,禁止忽略 IDE/编译器提示。
|
||||
- 代码提交前必须通过本地编译、单元测试、静态扫描(如存在)。
|
||||
- 严禁出现魔法值:使用常量或配置项;跨模块常量放在 common 模块统一管理。
|
||||
- 日志分级:业务关键信息用 info,调试用 debug(生产禁用),异常用 error,禁止打印敏感信息(密码、密钥、Token、手机号等)。
|
||||
|
||||
- 命名规范
|
||||
- 类:PascalCase;方法/变量:camelCase;常量:UPPER_SNAKE_CASE;包名全小写。
|
||||
- 接口路径参照现有规则(禁止 /api 前缀、禁止 PathVariable、Mapping value 必须显式且驼峰命名)。
|
||||
|
||||
- 注释规范
|
||||
- 类注释需包含:职责/用途、作者、创建时间、版本;方法注释需包含:用途、参数、返回、可能抛出的异常。
|
||||
- 复杂算法/易错逻辑必须添加行内注释;废弃方法使用 @Deprecated 并标注替代方案与移除计划。
|
||||
|
||||
- 代码结构规范(建议包结构)
|
||||
- controller、service、service.impl、repository(mapper/dao)、domain/entity、converter、config、client(feign)、event、facade、task、util、constant。
|
||||
- DTO/Request/Response 与 Entity 分离;Controller 不做对象互转,统一在 Service/Converter 层完成。
|
||||
|
||||
### 二、分层架构规范
|
||||
|
||||
- Controller 层职责
|
||||
- 接收请求、参数校验(Jakarta Validation)、鉴权校验(由网关/拦截器)、调用 Service,统一使用 `Result<T>` 返回。
|
||||
- 禁止:业务逻辑、事务控制、数据访问、实体与请求/响应的相互转换。
|
||||
|
||||
- Service 层职责
|
||||
- 聚合业务逻辑、领域编排、事务边界控制(@Transactional,读写分离时仅在写方法上标注),幂等与重试策略在此实现。
|
||||
- 方法命名与 Controller 对应,入参使用 Request 对象,出参使用 Response 对象。
|
||||
|
||||
- Repository 层职责
|
||||
- 仅做数据访问,统一使用 MyBatis-Plus(优先 LambdaQueryWrapper,避免硬编码字段)。
|
||||
|
||||
- Entity 规范
|
||||
- 统一继承 BaseEntity,字段包含:created_by、create_time、updated_by、update_time、logical_delete、remark(与项目约定一致)。
|
||||
- 禁止使用枚举作为实体/DTO 字段类型;以 String/Integer 存储并在注释中补充取值说明。
|
||||
|
||||
### 三、统一异常与返回结果
|
||||
|
||||
- 全局异常处理
|
||||
- 继续使用 common-web 的 GlobalExceptionHandler;禁止在业务代码中大面积 try-catch,异常由全局处理。
|
||||
- 业务异常一律抛出 BusinessException;校验异常统一转为 400 语义返回。
|
||||
|
||||
- 统一返回结构
|
||||
- 统一使用 common-core 的 Result<T> 与 ResultCode;保证 traceId、timestamp 一致性。
|
||||
- Controller 层仅返回 Result,不直接返回实体或集合。
|
||||
|
||||
- 参数校验
|
||||
- 使用 jakarta.validation 注解(@NotNull、@Size 等),在 Request 对象上标注;Controller 使用 @Validated。
|
||||
|
||||
### 四、数据库操作规范
|
||||
|
||||
- 设计规范
|
||||
- 数据表必须包含 create_time、update_time;删除优先逻辑删除 logical_delete;字段命名下划线风格。
|
||||
- 建议仅创建必要索引;避免外键约束(由应用层保证一致性)。
|
||||
|
||||
- 查询构建
|
||||
- 优先使用 LambdaQueryWrapper 和链式调用;分页查询必须使用分页插件;大数据量必须分页。
|
||||
- 避免 N+1 查询;需要时使用批量查询或联表映射。
|
||||
|
||||
- 事务管理
|
||||
- 仅在 Service 层声明事务;方法内仅包含数据库操作或与数据库一致性相关的远程调用;跨服务一致性使用可靠消息/补偿方案。
|
||||
|
||||
---
|
||||
|
||||
## 二、代码质量保障机制
|
||||
|
||||
- 代码审查流程(Code Review)
|
||||
- 所有变更必须走 PR;至少 1 名同组开发+1 名模块 Owner 审核通过方可合并。
|
||||
- 审查清单:命名/注释/分层边界/异常处理/日志/性能/安全/接口兼容性/测试覆盖率/静态扫描告警。
|
||||
- PR 模板需包含:改动描述、影响面、回归范围、测试说明、回滚方案。
|
||||
|
||||
- 静态代码分析与格式
|
||||
- 集成 Checkstyle(代码风格)、SpotBugs/PMD(潜在缺陷)、SonarQube(质量门禁),EditorConfig/格式化工具保持一致风格。
|
||||
- 质量门禁建议:
|
||||
- 新增代码覆盖率 >= 80%,全局覆盖率 >= 70%;
|
||||
- Blocker/Critical 问题为 0;
|
||||
- 重复代码率 < 3%;
|
||||
- 圈复杂度阈值:方法 < 10,类 < 80。
|
||||
|
||||
- 测试规范
|
||||
- 单元测试:命名 {ClassName}Test,方法名 should_xxx_when_xxx;使用 Mockito/AssertJ;隔离外部依赖。
|
||||
- 集成测试:使用 @SpringBootTest,测试容器或嵌入式组件替代真实依赖;构造典型场景与边界场景。
|
||||
- 覆盖率准则:核心业务与关键分支必须覆盖(正常/异常/边界/空数据)。
|
||||
- 用例分层:Service 层单测覆盖业务规则;Controller 层使用 MockMvc;Repository 层覆盖复杂 SQL 与多条件查询。
|
||||
|
||||
- 重构指导原则
|
||||
- 小步快跑、单一职责、先加测试再重构;保持对外行为一致(新增特性使用特性开关/版本控制)。
|
||||
- 严禁大规模跨模块重构合并在单次 PR;拆分为多个可审查的独立提交。
|
||||
|
||||
---
|
||||
|
||||
## 三、变更管理流程
|
||||
|
||||
- 变更前影响分析
|
||||
- 维度:接口兼容性(路径/入参/语义)、数据库(表/字段/索引/数据迁移)、配置(Nacos/环境变量)、依赖版本、性能、容量与并发、安全与合规、可回滚性。
|
||||
- 产出:影响分析文档与回滚方案(包含回滚脚本/开关)。
|
||||
|
||||
- 回归测试策略
|
||||
- 冒烟覆盖主干流程;模块级回归覆盖变更影响域;跨服务联调验证接口契约(通过网关)。
|
||||
- 回归范围基于依赖关系与影响分析确定;优先自动化回归,必要时补充手工用例。
|
||||
|
||||
- 版本管理与发布
|
||||
- 分支策略建议:
|
||||
- 主分支:master;
|
||||
- 功能分支:feature/{module}-{short-desc};
|
||||
- 预发布:release/x.y.z;
|
||||
- 紧急修复:hotfix/x.y.z;
|
||||
- 版本号:语义化版本 SemVer(MAJOR.MINOR.PATCH),发布时打 Tag;所有服务端口遵循 280xx 约定(配置文件统一)。
|
||||
|
||||
- 紧急修复与常规开发
|
||||
- 紧急修复:创建 hotfix → 修复 → 快速测试 → 合并 master 与 release → 立即发布 → 追补测试与文档。
|
||||
- 常规开发:feature → PR 审核 → 合并 → 集成测试 → 发布。
|
||||
|
||||
---
|
||||
|
||||
## 四、持续改进机制
|
||||
|
||||
- 规范评审与更新
|
||||
- 每月一次质量例会,评审新增问题与改进项;规范变更需在本文件留痕(版本/日期/变更点)。
|
||||
|
||||
- 质量指标监控(建议在 Sonar/日志/监控平台看板化)
|
||||
- 构建成功率、静态问题趋势、单元测试覆盖率、平均修复时长、缺陷密度、接口成功率、P95/P99 响应时间、数据库慢查询比例。
|
||||
|
||||
- 团队培训计划
|
||||
- 新人入项必读规范与代码演练;关键模块轮训;每季度至少一次专题分享(性能调优/安全加固/重构实践)。
|
||||
|
||||
- 反馈闭环
|
||||
- 通过代码评审、事后复盘(Postmortem)、问题工单收敛到规范条款;指定责任人与截止时间;下次评审验证落地结果。
|
||||
|
||||
---
|
||||
|
||||
## 附:标准检查清单(节选)
|
||||
|
||||
- PR 自检
|
||||
- 命名/注释达标;无魔法值;日志分级正确且无敏感信息;空指针风险已处理;删除代码/文件已获批准。
|
||||
- Controller 不含业务逻辑;路由命名与网关规范一致;统一 Result 返回;参数校验齐全;不使用 PathVariable。
|
||||
- Service 事务边界清晰;仅更新非空字段;分页/批量/幂等校验到位。
|
||||
- Repository 使用 LambdaQueryWrapper;避免硬编码字段;SQL 有必要索引;避免 N+1。
|
||||
- 测试覆盖关键路径与边界;新增功能附带测试;CI 静态扫描无阻断级问题。
|
||||
|
||||
- 回滚准备
|
||||
- 是否具备配置开关/灰度发布策略;是否提供回滚脚本;数据库迁移是否可逆(或给出补偿方案)。
|
||||
|
||||
+188
-30
@@ -2,26 +2,31 @@
|
||||
trigger: always_on
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# 项目开发规则
|
||||
|
||||
## 基础设置
|
||||
1. 保持对话语言为中文/英文
|
||||
2. 系统环境:Mac
|
||||
|
||||
1. 保持对话语言为中文
|
||||
2. 不允许在未经允许的情况下删除代码和文件,不允许破坏已经正常的业务代码
|
||||
3. 执行终端命令时要关注执行情况,避免无效等待
|
||||
|
||||
## 代码规范
|
||||
|
||||
4. 生成代码时必须添加类级和函数级注释
|
||||
5. 使用import导包,禁止使用全限定名称引用类
|
||||
6. 禁止使用枚举类型作为entity、request、response、dto对象的字段类型
|
||||
6. 禁止使用枚举类型作为entity、request、response、dto对象的字段类型,禁止以枚举类作为方法的入参,确保接口的通用性和可扩展性
|
||||
7. 新增数据的id使用已存在的雪花算法生成器生成
|
||||
|
||||
## 架构规范
|
||||
|
||||
8. 所有开发必须遵循当前项目规范
|
||||
9. Controller层禁止添加业务逻辑
|
||||
10. 使用全局异常处理,禁止使用try-catch
|
||||
11. 前端接口访问尽可能走网关调用
|
||||
|
||||
## 接口设计规范
|
||||
|
||||
12. Controller层接口定义要完整:
|
||||
- 入参使用request封装传递到service层
|
||||
- service层的方法命名与controller层定义的接口的方法名称保持一致
|
||||
@@ -30,46 +35,199 @@ alwaysApply: true
|
||||
- 使用项目已有的Result做接口返回
|
||||
13. 接口和方法参数不允许超过两个,超过时使用request或DTO对象封装
|
||||
14. Controller层路由禁止添加/api前缀
|
||||
15. Controller层接口的Mapping注解value属性值不允许重复且不允许为空
|
||||
15. Controller层接口的Mapping注解value属性值不允许重复且不允许为空,必须明确指定value属性值且使用驼峰结构命名,避免使用下划线分隔符。所有接口注解必须显式指定value属性,如@GetMapping(value = "/page")、@PostMapping(value = "/create")等,禁止使用@GetMapping()或@PostMapping等空注解形式
|
||||
16. 用户相关接口禁止直接传递用户id,需要后端根据token获取当前登录用户信息
|
||||
17. Controller层接口的Mapping注解(PostMapping、GetMapping、PutMapping、DeleteMapping等)的value属性值要简洁明了,与接口作用相关,名称不宜过长,使用驼峰结构命名
|
||||
18. 禁止使用/{param}格式的路径参数,避免网关路由冲突和分发错误
|
||||
19. 所有接口注解必须明确指定value属性,不允许使用空注解
|
||||
20. 路径参数统一使用@RequestParam而非@PathVariable,确保网关分发准确性
|
||||
21. 接口路径命名应具有明确的语义,避免使用通用词汇如/get、/list等
|
||||
22. 批量操作接口应使用专门的Request对象封装参数,而非直接传递List
|
||||
23. 接口路径应避免层级过深,建议不超过3级路径结构
|
||||
24. 更新操作应直接使用封装了ID和其他数据的Request对象,而不是单独传递ID参数。Service层方法应保持参数简洁,业务逻辑所需数据应全部包含在Request对象中
|
||||
17. 禁止使用/{param}格式的路径参数,避免网关路由冲突和分发错误
|
||||
18. 路径参数统一使用@RequestParam而非@PathVariable,确保网关分发准确性
|
||||
19. 接口路径命名应具有明确的语义,避免使用通用词汇如/get、/list等
|
||||
20. 批量操作接口应使用专门的Request对象封装参数,而非直接传递List
|
||||
21. 接口路径应避免层级过深,建议不超过3级路径结构
|
||||
22. 更新操作应直接使用封装了ID和其他数据的Request对象,而不是单独传递ID参数。Service层方法应保持参数简洁,业务逻辑所需数据应全部包含在Request对象中
|
||||
23. Controller层接口应保持简洁,避免为特定字段创建独立的更新方法(如updateStatus等),应只保留一个通用的update方法,具体的业务逻辑在Service层实现
|
||||
24. Service层在执行更新操作时,应对每个字段进行空值检查,只更新非空字段,避免空字段覆盖原有值,确保数据完整性
|
||||
25. Controller层应避免创建多个特定条件的查询接口,只保留一个分页查询方法,通过在请求对象中包含所有可查询字段来支持不同的查询需求
|
||||
26. 分页查询接口应支持模糊查询和精确查询,通过在Service层根据字段类型和业务需求判断查询方式
|
||||
|
||||
## 环境配置
|
||||
|
||||
25. 为不同环境(local、dev、prod)创建单独配置文件,部署时通过参数选择
|
||||
26. 启动服务时禁止擅自修改端口号,使用配置文件中的端口设置
|
||||
27. 为不同环境(local、dev、prod)创建单独配置文件,部署时通过参数选择
|
||||
28. 启动服务时禁止擅自修改端口号,使用配置文件中的端口设置
|
||||
|
||||
## 数据库规范
|
||||
|
||||
27. 所有数据表必须包含创建时间(create_time)和更新时间(update_time)字段
|
||||
28. 删除操作优先使用逻辑删除,添加deleted字段标识
|
||||
29. 数据库字段命名使用下划线分隔,Java实体类使用驼峰命名
|
||||
30. 优先使用LambdaQueryWrapper构造条件查询,避免硬编码字段名
|
||||
31. 使用Lambda表达式引用实体类属性,提高代码可维护性和类型安全
|
||||
32. 复杂查询条件应使用LambdaQueryWrapper的链式调用,保持代码清晰
|
||||
33. 避免在查询条件中使用字符串字段名,防止字段名变更导致的运行时错误
|
||||
29. 所有数据表必须包含创建时间(create_time)和更新时间(update_time)字段
|
||||
30. 删除操作优先使用逻辑删除,添加deleted字段标识
|
||||
31. 数据库字段命名使用下划线分隔,Java实体类使用驼峰命名
|
||||
32. 优先使用LambdaQueryWrapper构造条件查询,避免硬编码字段名
|
||||
33. 使用Lambda表达式引用实体类属性,提高代码可维护性和类型安全
|
||||
34. 复杂查询条件应使用LambdaQueryWrapper的链式调用,保持代码清晰
|
||||
35. 避免在查询条件中使用字符串字段名,防止字段名变更导致的运行时错误
|
||||
|
||||
## 安全规范
|
||||
|
||||
34. 所有外部输入必须进行参数校验
|
||||
35. 敏感信息不得在日志中输出
|
||||
36. 数据库操作必须使用参数化查询,防止SQL注入
|
||||
36. 所有外部输入必须进行参数校验
|
||||
37. 敏感信息不得在日志中输出
|
||||
38. 数据库操作必须使用参数化查询,防止SQL注入
|
||||
|
||||
## 性能规范
|
||||
|
||||
37. 避免N+1查询问题,合理使用批量查询
|
||||
38. 大数据量查询必须分页处理
|
||||
39. 缓存策略要考虑数据一致性问题
|
||||
39. 避免N+1查询问题,合理使用批量查询
|
||||
40. 大数据量查询必须分页处理
|
||||
41. 缓存策略要考虑数据一致性问题
|
||||
|
||||
## 日志规范
|
||||
|
||||
40. 关键业务操作必须记录操作日志
|
||||
41. 异常信息要包含足够的上下文信息
|
||||
42. 生产环境禁止输出debug级别日志
|
||||
42. 关键业务操作必须记录操作日志
|
||||
43. 异常信息要包含足够的上下文信息
|
||||
44. 生产环境禁止输出debug级别日志
|
||||
|
||||
---
|
||||
|
||||
## Java Spring Boot 项目开发与代码质量保障规范(扩展)
|
||||
|
||||
适用范围:本规范适用于 logistics-finance 项目所有后端模块(common、gateway、auth、user、order、waybill、vehicle、finance、report、ai、file)。若与本文件前文条款或现有项目规范冲突,以更严格者为准。
|
||||
|
||||
### 一、代码规范完善
|
||||
|
||||
- 编码标准
|
||||
- 严格开启编译参数校验与空指针警告,禁止忽略 IDE/编译器提示。
|
||||
- 代码提交前必须通过本地编译、单元测试、静态扫描(如存在)。
|
||||
- 严禁出现魔法值:使用常量或配置项;跨模块常量放在 common 模块统一管理。
|
||||
- 日志分级:业务关键信息用 info,调试用 debug(生产禁用),异常用 error,禁止打印敏感信息(密码、密钥、Token、手机号等)。
|
||||
|
||||
- 命名规范
|
||||
- 类:PascalCase;方法/变量:camelCase;常量:UPPER_SNAKE_CASE;包名全小写。
|
||||
- 接口路径参照现有规则(禁止 /api 前缀、禁止 PathVariable、Mapping value 必须显式且驼峰命名)。
|
||||
|
||||
- 注释规范
|
||||
- 类注释需包含:职责/用途、作者、创建时间、版本;方法注释需包含:用途、参数、返回、可能抛出的异常。
|
||||
- 复杂算法/易错逻辑必须添加行内注释;废弃方法使用 @Deprecated 并标注替代方案与移除计划。
|
||||
|
||||
- 代码结构规范(建议包结构)
|
||||
- controller、service、service.impl、repository(mapper/dao)、domain/entity、converter、config、client(feign)、event、facade、task、util、constant。
|
||||
- DTO/Request/Response 与 Entity 分离;Controller 不做对象互转,统一在 Service/Converter 层完成。
|
||||
|
||||
### 二、分层架构规范
|
||||
|
||||
- Controller 层职责
|
||||
- 接收请求、参数校验(Jakarta Validation)、鉴权校验(由网关/拦截器)、调用 Service,统一使用 `Result<T>` 返回。
|
||||
- 禁止:业务逻辑、事务控制、数据访问、实体与请求/响应的相互转换。
|
||||
|
||||
- Service 层职责
|
||||
- 聚合业务逻辑、领域编排、事务边界控制(@Transactional,读写分离时仅在写方法上标注),幂等与重试策略在此实现。
|
||||
- 方法命名与 Controller 对应,入参使用 Request 对象,出参使用 Response 对象。
|
||||
|
||||
- Repository 层职责
|
||||
- 仅做数据访问,统一使用 MyBatis-Plus(优先 LambdaQueryWrapper,避免硬编码字段)。
|
||||
|
||||
- Entity 规范
|
||||
- 统一继承 BaseEntity,字段包含:created_by、create_time、updated_by、update_time、logical_delete、remark(与项目约定一致)。
|
||||
- 禁止使用枚举作为实体/DTO 字段类型;以 String/Integer 存储并在注释中补充取值说明。
|
||||
|
||||
### 三、统一异常与返回结果
|
||||
|
||||
- 全局异常处理
|
||||
- 继续使用 common-web 的 GlobalExceptionHandler;禁止在业务代码中大面积 try-catch,异常由全局处理。
|
||||
- 业务异常一律抛出 BusinessException;校验异常统一转为 400 语义返回。
|
||||
|
||||
- 统一返回结构
|
||||
- 统一使用 common-core 的 Result<T> 与 ResultCode;保证 traceId、timestamp 一致性。
|
||||
- Controller 层仅返回 Result,不直接返回实体或集合。
|
||||
|
||||
- 参数校验
|
||||
- 使用 jakarta.validation 注解(@NotNull、@Size 等),在 Request 对象上标注;Controller 使用 @Validated。
|
||||
|
||||
### 四、数据库操作规范
|
||||
|
||||
- 设计规范
|
||||
- 数据表必须包含 create_time、update_time;删除优先逻辑删除 logical_delete;字段命名下划线风格。
|
||||
- 建议仅创建必要索引;避免外键约束(由应用层保证一致性)。
|
||||
|
||||
- 查询构建
|
||||
- 优先使用 LambdaQueryWrapper 和链式调用;分页查询必须使用分页插件;大数据量必须分页。
|
||||
- 避免 N+1 查询;需要时使用批量查询或联表映射。
|
||||
|
||||
- 事务管理
|
||||
- 仅在 Service 层声明事务;方法内仅包含数据库操作或与数据库一致性相关的远程调用;跨服务一致性使用可靠消息/补偿方案。
|
||||
|
||||
---
|
||||
|
||||
## 二、代码质量保障机制
|
||||
|
||||
- 代码审查流程(Code Review)
|
||||
- 所有变更必须走 PR;至少 1 名同组开发+1 名模块 Owner 审核通过方可合并。
|
||||
- 审查清单:命名/注释/分层边界/异常处理/日志/性能/安全/接口兼容性/测试覆盖率/静态扫描告警。
|
||||
- PR 模板需包含:改动描述、影响面、回归范围、测试说明、回滚方案。
|
||||
|
||||
- 静态代码分析与格式
|
||||
- 集成 Checkstyle(代码风格)、SpotBugs/PMD(潜在缺陷)、SonarQube(质量门禁),EditorConfig/格式化工具保持一致风格。
|
||||
- 质量门禁建议:
|
||||
- 新增代码覆盖率 >= 80%,全局覆盖率 >= 70%;
|
||||
- Blocker/Critical 问题为 0;
|
||||
- 重复代码率 < 3%;
|
||||
- 圈复杂度阈值:方法 < 10,类 < 80。
|
||||
|
||||
- 测试规范
|
||||
- 单元测试:命名 {ClassName}Test,方法名 should_xxx_when_xxx;使用 Mockito/AssertJ;隔离外部依赖。
|
||||
- 集成测试:使用 @SpringBootTest,测试容器或嵌入式组件替代真实依赖;构造典型场景与边界场景。
|
||||
- 覆盖率准则:核心业务与关键分支必须覆盖(正常/异常/边界/空数据)。
|
||||
- 用例分层:Service 层单测覆盖业务规则;Controller 层使用 MockMvc;Repository 层覆盖复杂 SQL 与多条件查询。
|
||||
|
||||
- 重构指导原则
|
||||
- 小步快跑、单一职责、先加测试再重构;保持对外行为一致(新增特性使用特性开关/版本控制)。
|
||||
- 严禁大规模跨模块重构合并在单次 PR;拆分为多个可审查的独立提交。
|
||||
|
||||
---
|
||||
|
||||
## 三、变更管理流程
|
||||
|
||||
- 变更前影响分析
|
||||
- 维度:接口兼容性(路径/入参/语义)、数据库(表/字段/索引/数据迁移)、配置(Nacos/环境变量)、依赖版本、性能、容量与并发、安全与合规、可回滚性。
|
||||
- 产出:影响分析文档与回滚方案(包含回滚脚本/开关)。
|
||||
|
||||
- 回归测试策略
|
||||
- 冒烟覆盖主干流程;模块级回归覆盖变更影响域;跨服务联调验证接口契约(通过网关)。
|
||||
- 回归范围基于依赖关系与影响分析确定;优先自动化回归,必要时补充手工用例。
|
||||
|
||||
- 版本管理与发布
|
||||
- 分支策略建议:
|
||||
- 主分支:master;
|
||||
- 功能分支:feature/{module}-{short-desc};
|
||||
- 预发布:release/x.y.z;
|
||||
- 紧急修复:hotfix/x.y.z;
|
||||
- 版本号:语义化版本 SemVer(MAJOR.MINOR.PATCH),发布时打 Tag;所有服务端口遵循 280xx 约定(配置文件统一)。
|
||||
|
||||
- 紧急修复与常规开发
|
||||
- 紧急修复:创建 hotfix → 修复 → 快速测试 → 合并 master 与 release → 立即发布 → 追补测试与文档。
|
||||
- 常规开发:feature → PR 审核 → 合并 → 集成测试 → 发布。
|
||||
|
||||
---
|
||||
|
||||
## 四、持续改进机制
|
||||
|
||||
- 规范评审与更新
|
||||
- 每月一次质量例会,评审新增问题与改进项;规范变更需在本文件留痕(版本/日期/变更点)。
|
||||
|
||||
- 质量指标监控(建议在 Sonar/日志/监控平台看板化)
|
||||
- 构建成功率、静态问题趋势、单元测试覆盖率、平均修复时长、缺陷密度、接口成功率、P95/P99 响应时间、数据库慢查询比例。
|
||||
|
||||
- 团队培训计划
|
||||
- 新人入项必读规范与代码演练;关键模块轮训;每季度至少一次专题分享(性能调优/安全加固/重构实践)。
|
||||
|
||||
- 反馈闭环
|
||||
- 通过代码评审、事后复盘(Postmortem)、问题工单收敛到规范条款;指定责任人与截止时间;下次评审验证落地结果。
|
||||
|
||||
---
|
||||
|
||||
## 附:标准检查清单(节选)
|
||||
|
||||
- PR 自检
|
||||
- 命名/注释达标;无魔法值;日志分级正确且无敏感信息;空指针风险已处理;删除代码/文件已获批准。
|
||||
- Controller 不含业务逻辑;路由命名与网关规范一致;统一 Result 返回;参数校验齐全;不使用 PathVariable。
|
||||
- Service 事务边界清晰;仅更新非空字段;分页/批量/幂等校验到位。
|
||||
- Repository 使用 LambdaQueryWrapper;避免硬编码字段;SQL 有必要索引;避免 N+1。
|
||||
- 测试覆盖关键路径与边界;新增功能附带测试;CI 静态扫描无阻断级问题。
|
||||
|
||||
- 回滚准备
|
||||
- 是否具备配置开关/灰度发布策略;是否提供回滚脚本;数据库迁移是否可逆(或给出补偿方案)。
|
||||
|
||||
|
||||
@@ -52,4 +52,12 @@ public class PageResult<T> {
|
||||
public static <T> PageResult<T> of(IPage<T> page) {
|
||||
return new PageResult<>(page);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据列表
|
||||
*/
|
||||
public PageResult<T> setRecords(List<T> records) {
|
||||
this.records = records;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,99 +1,88 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.PageRequest;
|
||||
import com.emotion.dto.response.BaseResponse;
|
||||
import com.emotion.entity.Achievement;
|
||||
import com.emotion.dto.request.achievement.*;
|
||||
import com.emotion.dto.response.achievement.AchievementResponse;
|
||||
import com.emotion.service.AchievementService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
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.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 成就控制器
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-07-23
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/achievement")
|
||||
@Tag(name = "成就管理", description = "成就的增删改查功能")
|
||||
public class AchievementController {
|
||||
|
||||
@Autowired
|
||||
private AchievementService achievementService;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 分页查询成就
|
||||
*/
|
||||
@Operation(summary = "分页查询成就", description = "分页查询成就列表")
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<AchievementResponse>> getPage(@Validated PageRequest request) {
|
||||
IPage<Achievement> page = achievementService.getPage(request);
|
||||
List<AchievementResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<AchievementResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
public Result<PageResult<AchievementResponse>> getPage(@Validated AchievementPageRequest request) {
|
||||
PageResult<AchievementResponse> pageResult = achievementService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取成就
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<AchievementResponse> getById(@PathVariable String id) {
|
||||
Achievement achievement = achievementService.getById(id);
|
||||
if (achievement == null) {
|
||||
@Operation(summary = "根据ID获取成就", description = "根据ID获取成就详情")
|
||||
@GetMapping("/detail")
|
||||
public Result<AchievementResponse> getById(@RequestParam String id) {
|
||||
AchievementResponse response = achievementService.getAchievementResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("成就不存在");
|
||||
}
|
||||
return Result.success(convertToResponse(achievement));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建成就
|
||||
*/
|
||||
@PostMapping
|
||||
public Result<AchievementResponse> create(@RequestBody Achievement achievement) {
|
||||
boolean saved = achievementService.save(achievement);
|
||||
if (!saved) {
|
||||
@Operation(summary = "创建成就", description = "创建新的成就")
|
||||
@PostMapping("/create")
|
||||
public Result<AchievementResponse> create(@RequestBody @Validated AchievementCreateRequest request) {
|
||||
AchievementResponse response = achievementService.createAchievementWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("创建失败");
|
||||
}
|
||||
return Result.success(convertToResponse(achievement));
|
||||
return Result.success("创建成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新成就
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<AchievementResponse> update(@PathVariable String id, @RequestBody Achievement achievement) {
|
||||
achievement.setId(id);
|
||||
boolean updated = achievementService.updateById(achievement);
|
||||
if (!updated) {
|
||||
@Operation(summary = "更新成就", description = "更新指定成就")
|
||||
@PutMapping("/update")
|
||||
public Result<AchievementResponse> update(@RequestBody @Validated AchievementUpdateRequest request) {
|
||||
AchievementResponse response = achievementService.updateAchievementWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
Achievement updatedAchievement = achievementService.getById(id);
|
||||
return Result.success(convertToResponse(updatedAchievement));
|
||||
return Result.success("更新成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除成就
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable String id) {
|
||||
@Operation(summary = "删除成就", description = "删除指定成就")
|
||||
@DeleteMapping("/delete")
|
||||
public Result<Void> delete(@RequestParam String id) {
|
||||
boolean deleted = achievementService.removeById(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
@@ -104,55 +93,48 @@ public class AchievementController {
|
||||
/**
|
||||
* 根据分类查询成就
|
||||
*/
|
||||
@GetMapping("/category/{category}")
|
||||
public Result<List<AchievementResponse>> getByCategory(@PathVariable String category) {
|
||||
List<Achievement> achievements = achievementService.getByCategory(category);
|
||||
List<AchievementResponse> responses = achievements.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
@Operation(summary = "根据分类查询成就", description = "根据分类查询成就列表")
|
||||
@GetMapping("/byCategory")
|
||||
public Result<List<AchievementResponse>> getByCategory(@RequestParam String category) {
|
||||
List<AchievementResponse> responses = achievementService.getByCategoryWithResponse(category);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据稀有度查询成就
|
||||
*/
|
||||
@GetMapping("/rarity/{rarity}")
|
||||
public Result<List<AchievementResponse>> getByRarity(@PathVariable String rarity) {
|
||||
List<Achievement> achievements = achievementService.getByRarity(rarity);
|
||||
List<AchievementResponse> responses = achievements.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
@Operation(summary = "根据稀有度查询成就", description = "根据稀有度查询成就列表")
|
||||
@GetMapping("/byRarity")
|
||||
public Result<List<AchievementResponse>> getByRarity(@RequestParam String rarity) {
|
||||
List<AchievementResponse> responses = achievementService.getByRarityWithResponse(rarity);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已解锁的成就
|
||||
*/
|
||||
@Operation(summary = "查询已解锁的成就", description = "查询已解锁的成就列表")
|
||||
@GetMapping("/unlocked")
|
||||
public Result<List<AchievementResponse>> getUnlockedAchievements() {
|
||||
List<Achievement> achievements = achievementService.getUnlockedAchievements();
|
||||
List<AchievementResponse> responses = achievements.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
List<AchievementResponse> responses = achievementService.getUnlockedAchievementsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询未解锁的成就
|
||||
*/
|
||||
@Operation(summary = "查询未解锁的成就", description = "查询未解锁的成就列表")
|
||||
@GetMapping("/locked")
|
||||
public Result<List<AchievementResponse>> getLockedAchievements() {
|
||||
List<Achievement> achievements = achievementService.getLockedAchievements();
|
||||
List<AchievementResponse> responses = achievements.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
List<AchievementResponse> responses = achievementService.getLockedAchievementsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计已解锁成就数量
|
||||
*/
|
||||
@GetMapping("/count/unlocked")
|
||||
@Operation(summary = "统计已解锁成就数量", description = "统计已解锁成就数量")
|
||||
@GetMapping("/unlockedCount")
|
||||
public Result<Long> countUnlockedAchievements() {
|
||||
Long count = achievementService.countUnlockedAchievements();
|
||||
return Result.success(count);
|
||||
@@ -161,7 +143,8 @@ public class AchievementController {
|
||||
/**
|
||||
* 统计未解锁成就数量
|
||||
*/
|
||||
@GetMapping("/count/locked")
|
||||
@Operation(summary = "统计未解锁成就数量", description = "统计未解锁成就数量")
|
||||
@GetMapping("/lockedCount")
|
||||
public Result<Long> countLockedAchievements() {
|
||||
Long count = achievementService.countLockedAchievements();
|
||||
return Result.success(count);
|
||||
@@ -170,9 +153,10 @@ public class AchievementController {
|
||||
/**
|
||||
* 解锁成就
|
||||
*/
|
||||
@PutMapping("/{id}/unlock")
|
||||
public Result<Void> unlockAchievement(@PathVariable String id) {
|
||||
boolean unlocked = achievementService.unlockAchievement(id, java.time.LocalDateTime.now());
|
||||
@Operation(summary = "解锁成就", description = "解锁指定成就")
|
||||
@PutMapping("/unlock")
|
||||
public Result<Void> unlockAchievement(@RequestBody @Validated AchievementUnlockRequest request) {
|
||||
boolean unlocked = achievementService.unlockAchievement(request.getId(), LocalDateTime.now());
|
||||
if (!unlocked) {
|
||||
return Result.error("解锁失败");
|
||||
}
|
||||
@@ -182,9 +166,10 @@ public class AchievementController {
|
||||
/**
|
||||
* 更新成就进度
|
||||
*/
|
||||
@PutMapping("/{id}/progress")
|
||||
public Result<Void> updateProgress(@PathVariable String id, @RequestParam Double progress) {
|
||||
boolean updated = achievementService.updateProgress(id, progress);
|
||||
@Operation(summary = "更新成就进度", description = "更新指定成就的进度")
|
||||
@PutMapping("/updateProgress")
|
||||
public Result<Void> updateProgress(@RequestBody @Validated AchievementProgressUpdateRequest request) {
|
||||
boolean updated = achievementService.updateProgress(request.getId(), request.getProgress());
|
||||
if (!updated) {
|
||||
return Result.error("更新进度失败");
|
||||
}
|
||||
@@ -192,36 +177,12 @@ public class AchievementController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
* 查询最近解锁的成就
|
||||
*/
|
||||
private AchievementResponse convertToResponse(Achievement achievement) {
|
||||
AchievementResponse response = new AchievementResponse();
|
||||
BeanUtils.copyProperties(achievement, response);
|
||||
response.setId(achievement.getId());
|
||||
if (achievement.getCreateTime() != null) {
|
||||
response.setCreateTime(achievement.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (achievement.getUpdateTime() != null) {
|
||||
response.setUpdateTime(achievement.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
@Operation(summary = "查询最近解锁的成就", description = "查询最近解锁的成就列表")
|
||||
@GetMapping("/recent")
|
||||
public Result<List<AchievementResponse>> getRecentlyUnlocked(@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<AchievementResponse> responses = achievementService.getRecentlyUnlockedWithResponse(limit);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成就响应类
|
||||
*/
|
||||
@lombok.Data
|
||||
@lombok.EqualsAndHashCode(callSuper = true)
|
||||
public static class AchievementResponse extends BaseResponse {
|
||||
private String title;
|
||||
private String description;
|
||||
private String category;
|
||||
private String rarity;
|
||||
private String conditionType;
|
||||
private String conditionValue;
|
||||
private Double progress;
|
||||
private String unlockedTime;
|
||||
private Integer isHidden;
|
||||
private String iconUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import com.emotion.dto.request.AiChatRequest;
|
||||
import com.emotion.dto.request.AiSummaryRequest;
|
||||
import com.emotion.dto.request.GuestChatRequest;
|
||||
import com.emotion.dto.request.ConversationCreateRequest;
|
||||
import com.emotion.dto.request.ChatStatsRequest;
|
||||
import com.emotion.dto.request.GuestUserInfoRequest;
|
||||
import com.emotion.dto.response.AiChatResponse;
|
||||
import com.emotion.dto.response.AiSummaryResponse;
|
||||
import com.emotion.dto.response.AiStatusResponse;
|
||||
@@ -12,19 +14,14 @@ import com.emotion.dto.response.ChatStatsResponse;
|
||||
import com.emotion.dto.response.GuestChatResponse;
|
||||
import com.emotion.dto.response.GuestUserInfoResponse;
|
||||
import com.emotion.dto.response.ConversationResponse;
|
||||
import com.emotion.entity.Conversation;
|
||||
import com.emotion.service.AiChatService;
|
||||
import com.emotion.service.MessageService;
|
||||
import com.emotion.service.ConversationService;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* AI聊天控制器
|
||||
@@ -40,34 +37,16 @@ public class AiChatController {
|
||||
@Autowired
|
||||
private AiChatService aiChatService;
|
||||
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
|
||||
@Autowired
|
||||
private ConversationService conversationService;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 发送聊天消息
|
||||
*/
|
||||
@PostMapping("/chat")
|
||||
public Result<AiChatResponse> sendChatMessage(@Valid @RequestBody AiChatRequest request) {
|
||||
log.info("收到AI聊天请求: conversationId={}, userId={}, message={}",
|
||||
request.getConversationId(), request.getUserId(), request.getMessage());
|
||||
|
||||
// 调用AI服务
|
||||
String aiReply = aiChatService.sendChatMessage(request.getConversationId(), request.getMessage(),
|
||||
request.getUserId());
|
||||
|
||||
// 构建响应
|
||||
AiChatResponse response = new AiChatResponse();
|
||||
response.setConversationId(request.getConversationId());
|
||||
response.setUserMessage(request.getMessage());
|
||||
response.setAiReply(aiReply);
|
||||
response.setUserId(request.getUserId());
|
||||
response.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
log.info("收到AI聊天请求: conversationId={}, userId={}, message={}",
|
||||
request.getConversationId(), request.getUserId(), request.getMessage());
|
||||
|
||||
// 调用AI服务
|
||||
AiChatResponse response = aiChatService.sendChatMessage(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
@@ -77,18 +56,9 @@ public class AiChatController {
|
||||
@PostMapping("/summary")
|
||||
public Result<AiSummaryResponse> generateSummary(@Valid @RequestBody AiSummaryRequest request) {
|
||||
log.info("收到对话总结请求: conversationId={}, userId={}", request.getConversationId(), request.getUserId());
|
||||
|
||||
// 调用AI总结服务
|
||||
String summary = aiChatService.generateConversationSummary(request.getConversationId(),
|
||||
request.getUserId());
|
||||
|
||||
// 构建响应
|
||||
AiSummaryResponse response = new AiSummaryResponse();
|
||||
response.setConversationId(request.getConversationId());
|
||||
response.setSummary(summary);
|
||||
response.setUserId(request.getUserId());
|
||||
response.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
|
||||
// 调用AI总结服务
|
||||
AiSummaryResponse response = aiChatService.generateConversationSummary(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
@@ -99,15 +69,7 @@ public class AiChatController {
|
||||
public Result<AiStatusResponse> getServiceStatus() {
|
||||
log.info("获取AI服务状态");
|
||||
|
||||
boolean available = aiChatService.isServiceAvailable();
|
||||
String status = aiChatService.getServiceStatus();
|
||||
|
||||
// 构建响应
|
||||
AiStatusResponse response = new AiStatusResponse();
|
||||
response.setAvailable(available);
|
||||
response.setStatus(status);
|
||||
response.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
AiStatusResponse response = aiChatService.getServiceStatus();
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
@@ -115,124 +77,48 @@ public class AiChatController {
|
||||
* 获取聊天记录统计
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public Result<ChatStatsResponse> getChatStats(@RequestParam(required = false) String userId,
|
||||
@RequestParam(required = false) String conversationId) {
|
||||
log.info("获取聊天统计: userId={}, conversationId={}", userId, conversationId);
|
||||
public Result<ChatStatsResponse> getChatStats(@Valid ChatStatsRequest request) {
|
||||
log.info("获取聊天统计: userId={}, conversationId={}", request.getUserId(), request.getConversationId());
|
||||
|
||||
// 构建响应
|
||||
ChatStatsResponse response = new ChatStatsResponse();
|
||||
|
||||
if (userId != null && !userId.trim().isEmpty()) {
|
||||
Long userConversationCount = conversationService.countByUserId(userId);
|
||||
Long activeConversationCount = conversationService.countActiveByUserId(userId);
|
||||
|
||||
response.setUserConversationCount(userConversationCount);
|
||||
response.setActiveConversationCount(activeConversationCount);
|
||||
}
|
||||
|
||||
if (conversationId != null && !conversationId.trim().isEmpty()) {
|
||||
Long conversationMessageCount = messageService.countByConversationId(conversationId);
|
||||
response.setConversationMessageCount(conversationMessageCount);
|
||||
}
|
||||
|
||||
response.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 访客聊天(不需要登录)
|
||||
*/
|
||||
@PostMapping("/guest/chat")
|
||||
public Result<GuestChatResponse> guestChat(@Valid @RequestBody GuestChatRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
String clientIp = getClientIp(httpRequest);
|
||||
log.info("访客聊天请求: {}, IP: {}", request.getMessage(), clientIp);
|
||||
|
||||
// 调用AI服务
|
||||
Map<String, Object> aiResponse = aiChatService.guestChat(request.getMessage(), clientIp);
|
||||
|
||||
// 构建响应
|
||||
GuestChatResponse response = new GuestChatResponse();
|
||||
response.setMessage((String) aiResponse.get("message"));
|
||||
response.setMessageId((String) aiResponse.get("messageId"));
|
||||
response.setTimestamp((Long) aiResponse.get("timestamp"));
|
||||
response.setError((Boolean) aiResponse.get("error"));
|
||||
|
||||
return Result.success("发送成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访客用户信息
|
||||
*/
|
||||
@GetMapping("/guest/user/info")
|
||||
public Result<GuestUserInfoResponse> getGuestUserInfo(HttpServletRequest request) {
|
||||
String clientIp = getClientIp(request);
|
||||
log.info("获取访客用户信息: IP={}", clientIp);
|
||||
|
||||
// 调用AI服务
|
||||
Map<String, Object> userInfo = aiChatService.getGuestUserInfo(clientIp);
|
||||
|
||||
// 构建响应
|
||||
GuestUserInfoResponse response = new GuestUserInfoResponse();
|
||||
response.setId((String) userInfo.get("id"));
|
||||
response.setUsername((String) userInfo.get("username"));
|
||||
response.setNickname((String) userInfo.get("nickname"));
|
||||
response.setType((String) userInfo.get("type"));
|
||||
response.setClientIp((String) userInfo.get("clientIp"));
|
||||
response.setUserCreateTime((Long) userInfo.get("createTime"));
|
||||
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建对话
|
||||
*/
|
||||
@PostMapping("/conversation/create")
|
||||
public Result<ConversationResponse> createConversation(@Valid @RequestBody ConversationCreateRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
log.info("创建对话请求: userId={}, title={}", request.getUserId(), request.getTitle());
|
||||
|
||||
String clientIp = getClientIp(httpRequest);
|
||||
|
||||
// 调用AI服务创建对话
|
||||
Map<String, Object> aiConversation = aiChatService.createConversation(request.getUserId(),
|
||||
request.getTitle());
|
||||
|
||||
// 创建数据库对话记录
|
||||
Conversation conversation = conversationService.createConversation(
|
||||
request.getUserId(),
|
||||
request.getTitle(),
|
||||
request.getType() != null ? request.getType() : "user");
|
||||
|
||||
// 构建响应
|
||||
ConversationResponse response = new ConversationResponse();
|
||||
BeanUtils.copyProperties(conversation, response);
|
||||
response.setId(conversation.getId());
|
||||
if (conversation.getCreateTime() != null) {
|
||||
response.setCreateTime(conversation.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (conversation.getUpdateTime() != null) {
|
||||
response.setUpdateTime(conversation.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
|
||||
return Result.success("创建成功", response);
|
||||
ChatStatsResponse response = aiChatService.getChatStats(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP
|
||||
* 访客聊天(不需要登录)
|
||||
*/
|
||||
private String getClientIp(HttpServletRequest request) {
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
|
||||
return xForwardedFor.split(",")[0].trim();
|
||||
}
|
||||
@PostMapping("/guestChat")
|
||||
public Result<GuestChatResponse> guestChat(@Valid @RequestBody GuestChatRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
String clientIp = UserContextUtils.getClientIpAddress(httpRequest);
|
||||
log.info("访客聊天请求: {}, IP: {}", request.getMessage(), clientIp);
|
||||
|
||||
String xRealIp = request.getHeader("X-Real-IP");
|
||||
if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
|
||||
return xRealIp;
|
||||
}
|
||||
|
||||
return request.getRemoteAddr();
|
||||
GuestChatResponse response = aiChatService.guestChat(request, clientIp);
|
||||
return Result.success("发送成功", response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访客用户信息
|
||||
*/
|
||||
@GetMapping("/guestUserInfo")
|
||||
public Result<GuestUserInfoResponse> getGuestUserInfo(HttpServletRequest request) {
|
||||
String clientIp = UserContextUtils.getClientIpAddress(request);
|
||||
log.info("获取访客用户信息: IP={}", clientIp);
|
||||
|
||||
GuestUserInfoResponse response = aiChatService.getGuestUserInfo(clientIp);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建对话
|
||||
*/
|
||||
@PostMapping("/createConversation")
|
||||
public Result<ConversationResponse> createConversation(@Valid @RequestBody ConversationCreateRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
log.info("创建对话请求: userId={}, title={}", request.getUserId(), request.getTitle());
|
||||
|
||||
String clientIp = UserContextUtils.getClientIpAddress(httpRequest);
|
||||
ConversationResponse response = aiChatService.createConversation(request, clientIp);
|
||||
return Result.success("创建成功", response);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.emotion.dto.response.CaptchaResponse;
|
||||
import com.emotion.dto.response.UserInfoResponse;
|
||||
import com.emotion.service.AuthService;
|
||||
import com.emotion.service.TokenService;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -52,10 +53,9 @@ public class AuthController {
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
@GetMapping("/user/info")
|
||||
@GetMapping("/userInfo")
|
||||
public Result<UserInfoResponse> getCurrentUserInfo(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
UserInfoResponse userInfo = tokenService.getUserInfoByToken(token);
|
||||
UserInfoResponse userInfo = tokenService.getUserInfoByToken(request);
|
||||
return Result.success(userInfo);
|
||||
}
|
||||
|
||||
@@ -73,15 +73,14 @@ public class AuthController {
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public Result<Void> logout(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
authService.logoutByToken(token);
|
||||
authService.logoutByToken(request);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*/
|
||||
@PostMapping("/refresh")
|
||||
@PostMapping("/refreshToken")
|
||||
public Result<AuthResponse> refreshToken(@Valid @RequestBody RefreshTokenRequest request) {
|
||||
AuthResponse response = authService.refreshToken(request.getRefreshToken());
|
||||
return Result.success("令牌刷新成功", response);
|
||||
@@ -90,14 +89,9 @@ public class AuthController {
|
||||
/**
|
||||
* 验证访问令牌
|
||||
*/
|
||||
@GetMapping("/validate")
|
||||
@GetMapping("/validateToken")
|
||||
public Result<Boolean> validateToken(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
if (token == null) {
|
||||
return Result.success(false);
|
||||
}
|
||||
|
||||
boolean isValid = authService.validateToken(token);
|
||||
boolean isValid = authService.validateToken(request);
|
||||
return Result.success(isValid);
|
||||
}
|
||||
|
||||
@@ -106,15 +100,14 @@ public class AuthController {
|
||||
*/
|
||||
@GetMapping("/username")
|
||||
public Result<String> getUsernameFromToken(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
String username = tokenService.getUsernameByToken(token);
|
||||
String username = tokenService.getUsernameByToken(request);
|
||||
return Result.success(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查账号是否存在
|
||||
*/
|
||||
@GetMapping("/check-account")
|
||||
@GetMapping("/checkAccount")
|
||||
public Result<Boolean> checkAccount(@RequestParam String account) {
|
||||
boolean exists = authService.existsByAccount(account);
|
||||
return Result.success(exists);
|
||||
@@ -123,7 +116,7 @@ public class AuthController {
|
||||
/**
|
||||
* 检查邮箱是否存在
|
||||
*/
|
||||
@GetMapping("/check-email")
|
||||
@GetMapping("/checkEmail")
|
||||
public Result<Boolean> checkEmail(@RequestParam String email) {
|
||||
boolean exists = authService.existsByEmail(email);
|
||||
return Result.success(exists);
|
||||
@@ -132,27 +125,9 @@ public class AuthController {
|
||||
/**
|
||||
* 检查手机号是否存在
|
||||
*/
|
||||
@GetMapping("/check-phone")
|
||||
@GetMapping("/checkPhone")
|
||||
public Result<Boolean> checkPhone(@RequestParam String phone) {
|
||||
boolean exists = authService.existsByPhone(phone);
|
||||
return Result.success(exists);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中提取访问令牌
|
||||
*/
|
||||
private String extractToken(HttpServletRequest request) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||
return authHeader.substring(7);
|
||||
}
|
||||
|
||||
// 也可以从请求参数中获取
|
||||
String tokenParam = request.getParameter("token");
|
||||
if (tokenParam != null && !tokenParam.trim().isEmpty()) {
|
||||
return tokenParam.trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.dto.websocket.ChatRequest;
|
||||
import com.emotion.dto.request.WebSocketRequest;
|
||||
import com.emotion.dto.websocket.ConnectRequest;
|
||||
import com.emotion.service.WebSocketService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -14,14 +14,15 @@ import javax.validation.Valid;
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* 优化的WebSocket聊天控制器
|
||||
* 使用规范的请求对象封装参数
|
||||
* WebSocket聊天控制器
|
||||
* 使用规范的请求对象封装参数,符合项目开发规则
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-07-23
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
@MessageMapping("/chat")
|
||||
public class ChatWebSocketController {
|
||||
|
||||
@Autowired
|
||||
@@ -29,111 +30,55 @@ public class ChatWebSocketController {
|
||||
|
||||
/**
|
||||
* 处理聊天消息
|
||||
* @param chatRequest 聊天请求对象
|
||||
* @param headerAccessor 消息头访问器
|
||||
* @param principal 用户主体
|
||||
*
|
||||
* @param webSocketRequest WebSocket请求对象
|
||||
* @param headerAccessor 消息头访问器
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
@MessageMapping("/chat.send")
|
||||
public void handleChatMessage(@Valid @Payload ChatRequest chatRequest,
|
||||
SimpMessageHeaderAccessor headerAccessor,
|
||||
Principal principal) {
|
||||
try {
|
||||
log.info("收到WebSocket聊天消息: {}", chatRequest);
|
||||
|
||||
// 获取会话ID
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
|
||||
// 如果请求中没有发送者ID,尝试从Principal获取
|
||||
if (chatRequest.getSenderId() == null && principal != null) {
|
||||
chatRequest.setSenderId(principal.getName());
|
||||
}
|
||||
|
||||
// 如果还是没有发送者ID,使用会话ID作为访客ID
|
||||
if (chatRequest.getSenderId() == null) {
|
||||
chatRequest.setSenderId("guest_" + sessionId);
|
||||
chatRequest.setSenderType(ChatRequest.SenderType.GUEST);
|
||||
}
|
||||
|
||||
// 设置时间戳
|
||||
if (chatRequest.getTimestamp() == null) {
|
||||
chatRequest.setTimestamp(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
// 处理聊天消息
|
||||
webSocketService.handleChatMessage(chatRequest, sessionId, principal);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理WebSocket聊天消息失败", e);
|
||||
}
|
||||
@MessageMapping("/send")
|
||||
public void handleChatMessage(@Valid @Payload WebSocketRequest webSocketRequest,
|
||||
SimpMessageHeaderAccessor headerAccessor,
|
||||
Principal principal) {
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
webSocketService.handleChatMessage(webSocketRequest, sessionId, principal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户连接
|
||||
*
|
||||
* @param connectRequest 连接请求对象
|
||||
* @param headerAccessor 消息头访问器
|
||||
* @param principal 用户主体
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
@MessageMapping("/chat.connect")
|
||||
@MessageMapping("/connect")
|
||||
public void connectUser(@Payload ConnectRequest connectRequest,
|
||||
SimpMessageHeaderAccessor headerAccessor,
|
||||
Principal principal) {
|
||||
try {
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
log.info("用户连接WebSocket: connectRequest={}, sessionId={}, principal={}",
|
||||
connectRequest, sessionId, principal);
|
||||
|
||||
// 如果请求中没有用户ID,尝试从Principal获取
|
||||
if (connectRequest.getUserId() == null && principal != null) {
|
||||
connectRequest.setUserId(principal.getName());
|
||||
}
|
||||
|
||||
// 设置连接时间戳
|
||||
if (connectRequest.getTimestamp() == null) {
|
||||
connectRequest.setTimestamp(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
// 处理用户连接
|
||||
webSocketService.handleUserConnect(connectRequest, sessionId, principal);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理用户WebSocket连接失败", e);
|
||||
}
|
||||
SimpMessageHeaderAccessor headerAccessor,
|
||||
Principal principal) {
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
webSocketService.handleUserConnect(connectRequest, sessionId, principal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户断开连接
|
||||
*
|
||||
* @param headerAccessor 消息头访问器
|
||||
* @param principal 用户主体
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
@MessageMapping("/chat.disconnect")
|
||||
@MessageMapping("/disconnect")
|
||||
public void disconnectUser(SimpMessageHeaderAccessor headerAccessor, Principal principal) {
|
||||
try {
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
log.info("用户断开WebSocket连接: sessionId={}, principal={}", sessionId, principal);
|
||||
|
||||
// 处理用户断开连接
|
||||
webSocketService.handleUserDisconnect(sessionId, principal);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理用户WebSocket断开连接失败", e);
|
||||
}
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
webSocketService.handleUserDisconnect(sessionId, principal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理心跳消息
|
||||
*
|
||||
* @param headerAccessor 消息头访问器
|
||||
* @param principal 用户主体
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
@MessageMapping("/chat.heartbeat")
|
||||
@MessageMapping("/heartbeat")
|
||||
public void heartbeat(SimpMessageHeaderAccessor headerAccessor, Principal principal) {
|
||||
try {
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
|
||||
// 处理心跳消息
|
||||
webSocketService.handleHeartbeat(sessionId, principal);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理WebSocket心跳失败", e);
|
||||
}
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
webSocketService.handleHeartbeat(sessionId, principal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,17 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.PageRequest;
|
||||
import com.emotion.dto.response.BaseResponse;
|
||||
import com.emotion.entity.Comment;
|
||||
import com.emotion.dto.request.comment.CommentCreateRequest;
|
||||
import com.emotion.dto.request.comment.CommentUpdateRequest;
|
||||
import com.emotion.dto.request.comment.CommentPageRequest;
|
||||
import com.emotion.dto.response.comment.CommentResponse;
|
||||
import com.emotion.service.CommentService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 评论控制器
|
||||
*
|
||||
@@ -30,114 +25,44 @@ public class CommentController {
|
||||
@Autowired
|
||||
private CommentService commentService;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 分页查询评论
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<CommentResponse>> getPage(@Validated PageRequest request) {
|
||||
IPage<Comment> page = commentService.getPage(request);
|
||||
List<CommentResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<CommentResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<CommentResponse>> getCommentPage(@Validated CommentPageRequest request) {
|
||||
PageResult<CommentResponse> pageResult = commentService.getPage(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据帖子ID分页查询评论
|
||||
*/
|
||||
@GetMapping("/post/{postId}/page")
|
||||
public Result<PageResult<CommentResponse>> getPageByPostId(@PathVariable String postId, @Validated PageRequest request) {
|
||||
IPage<Comment> page = commentService.getPageByPostId(request, postId);
|
||||
List<CommentResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<CommentResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询评论
|
||||
*/
|
||||
@GetMapping("/user/{userId}/page")
|
||||
public Result<PageResult<CommentResponse>> getPageByUserId(@PathVariable String userId, @Validated PageRequest request) {
|
||||
IPage<Comment> page = commentService.getPageByUserId(request, userId);
|
||||
List<CommentResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<CommentResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取评论
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<CommentResponse> getById(@PathVariable String id) {
|
||||
Comment comment = commentService.getById(id);
|
||||
if (comment == null) {
|
||||
return Result.notFound("评论不存在");
|
||||
}
|
||||
return Result.success(convertToResponse(comment));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建评论
|
||||
*/
|
||||
@PostMapping
|
||||
public Result<CommentResponse> create(@RequestBody @Validated CommentCreateRequest request) {
|
||||
Comment comment = commentService.createComment(
|
||||
request.getPostId(),
|
||||
request.getUserId(),
|
||||
request.getContent(),
|
||||
request.getReplyToId()
|
||||
);
|
||||
return Result.success(convertToResponse(comment));
|
||||
@PostMapping(value = "/create")
|
||||
public Result<CommentResponse> createComment(@RequestBody @Validated CommentCreateRequest request) {
|
||||
// 从上下文中获取当前用户ID,而不是直接使用请求中的用户ID
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
if (currentUserId != null) {
|
||||
request.setUserId(currentUserId);
|
||||
}
|
||||
CommentResponse response = commentService.create(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新评论
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<CommentResponse> update(@PathVariable String id, @RequestBody Comment comment) {
|
||||
comment.setId(id);
|
||||
boolean updated = commentService.updateById(comment);
|
||||
if (!updated) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
Comment updatedComment = commentService.getById(id);
|
||||
return Result.success(convertToResponse(updatedComment));
|
||||
@PutMapping(value = "/update")
|
||||
public Result<CommentResponse> updateComment(@RequestBody @Validated CommentUpdateRequest request) {
|
||||
CommentResponse response = commentService.update(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable String id) {
|
||||
boolean deleted = commentService.removeById(id);
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> deleteComment(@RequestParam String id) {
|
||||
boolean deleted = commentService.delete(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
@@ -145,105 +70,14 @@ public class CommentController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据帖子ID查询顶级评论
|
||||
* 根据ID获取评论
|
||||
*/
|
||||
@GetMapping("/post/{postId}/top-level")
|
||||
public Result<List<CommentResponse>> getTopLevelCommentsByPostId(@PathVariable String postId) {
|
||||
List<Comment> comments = commentService.getTopLevelCommentsByPostId(postId);
|
||||
List<CommentResponse> responses = comments.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据评论ID查询回复
|
||||
*/
|
||||
@GetMapping("/{commentId}/replies")
|
||||
public Result<List<CommentResponse>> getRepliesByCommentId(@PathVariable String commentId) {
|
||||
List<Comment> replies = commentService.getRepliesByCommentId(commentId);
|
||||
List<CommentResponse> responses = replies.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计帖子的评论数量
|
||||
*/
|
||||
@GetMapping("/post/{postId}/count")
|
||||
public Result<Long> countByPostId(@PathVariable String postId) {
|
||||
Long count = commentService.countByPostId(postId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞评论
|
||||
*/
|
||||
@PutMapping("/{id}/like")
|
||||
public Result<Void> likeComment(@PathVariable String id) {
|
||||
boolean updated = commentService.updateLikes(id, 1);
|
||||
if (!updated) {
|
||||
return Result.error("点赞失败");
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<CommentResponse> getCommentById(@RequestParam String id) {
|
||||
CommentResponse response = commentService.getById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("评论不存在");
|
||||
}
|
||||
return Result.success();
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消点赞评论
|
||||
*/
|
||||
@PutMapping("/{id}/unlike")
|
||||
public Result<Void> unlikeComment(@PathVariable String id) {
|
||||
boolean updated = commentService.updateLikes(id, -1);
|
||||
if (!updated) {
|
||||
return Result.error("取消点赞失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private CommentResponse convertToResponse(Comment comment) {
|
||||
CommentResponse response = new CommentResponse();
|
||||
BeanUtils.copyProperties(comment, response);
|
||||
response.setId(comment.getId());
|
||||
if (comment.getCreateTime() != null) {
|
||||
response.setCreateTime(comment.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (comment.getUpdateTime() != null) {
|
||||
response.setUpdateTime(comment.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 评论创建请求
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class CommentCreateRequest {
|
||||
@NotBlank(message = "帖子ID不能为空")
|
||||
private String postId;
|
||||
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
|
||||
@NotBlank(message = "评论内容不能为空")
|
||||
private String content;
|
||||
|
||||
private String replyToId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 评论响应类
|
||||
*/
|
||||
@lombok.Data
|
||||
@lombok.EqualsAndHashCode(callSuper = true)
|
||||
public static class CommentResponse extends BaseResponse {
|
||||
private String postId;
|
||||
private String userId;
|
||||
private String content;
|
||||
private String replyToId;
|
||||
private Integer likes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,16 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.PageRequest;
|
||||
import com.emotion.dto.response.BaseResponse;
|
||||
import com.emotion.entity.CommunityPost;
|
||||
import com.emotion.dto.request.community.CommunityPostCreateRequest;
|
||||
import com.emotion.dto.request.community.CommunityPostUpdateRequest;
|
||||
import com.emotion.dto.request.community.CommunityPostPageRequest;
|
||||
import com.emotion.dto.response.community.CommunityPostResponse;
|
||||
import com.emotion.service.CommunityPostService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 社区帖子控制器
|
||||
*
|
||||
@@ -24,266 +18,60 @@ import java.util.stream.Collectors;
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/community-post")
|
||||
@RequestMapping("/communityPost")
|
||||
public class CommunityPostController {
|
||||
|
||||
@Autowired
|
||||
private CommunityPostService communityPostService;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 分页查询帖子
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<CommunityPostResponse>> getPage(@Validated PageRequest request) {
|
||||
IPage<CommunityPost> page = communityPostService.getPage(request);
|
||||
List<CommunityPostResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<CommunityPostResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询公开帖子
|
||||
*/
|
||||
@GetMapping("/public/page")
|
||||
public Result<PageResult<CommunityPostResponse>> getPublicPostsPage(@Validated PageRequest request) {
|
||||
IPage<CommunityPost> page = communityPostService.getPublicPostsPage(request);
|
||||
List<CommunityPostResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<CommunityPostResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询帖子
|
||||
*/
|
||||
@GetMapping("/user/{userId}/page")
|
||||
public Result<PageResult<CommunityPostResponse>> getPageByUserId(@PathVariable String userId, @Validated PageRequest request) {
|
||||
IPage<CommunityPost> page = communityPostService.getPageByUserId(request, userId);
|
||||
List<CommunityPostResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<CommunityPostResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<CommunityPostResponse>> getPage(@Validated CommunityPostPageRequest request) {
|
||||
PageResult<CommunityPostResponse> pageResult = communityPostService.getPage(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取帖子
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<CommunityPostResponse> getById(@PathVariable String id) {
|
||||
CommunityPost post = communityPostService.getById(id);
|
||||
if (post == null) {
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<CommunityPostResponse> getById(@RequestParam String id) {
|
||||
CommunityPostResponse response = communityPostService.getById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("帖子不存在");
|
||||
}
|
||||
// 增加浏览数
|
||||
communityPostService.incrementViewCount(id);
|
||||
return Result.success(convertToResponse(post));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建帖子
|
||||
*/
|
||||
@PostMapping
|
||||
@PostMapping(value = "/create")
|
||||
public Result<CommunityPostResponse> create(@RequestBody @Validated CommunityPostCreateRequest request) {
|
||||
CommunityPost post = communityPostService.createPost(
|
||||
request.getUserId(),
|
||||
request.getTitle(),
|
||||
request.getContent(),
|
||||
request.getType(),
|
||||
request.getLocationId(),
|
||||
request.getTags(),
|
||||
request.getIsPrivate()
|
||||
);
|
||||
return Result.success(convertToResponse(post));
|
||||
CommunityPostResponse response = communityPostService.create(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新帖子
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<CommunityPostResponse> update(@PathVariable String id, @RequestBody CommunityPost post) {
|
||||
post.setId(id);
|
||||
boolean updated = communityPostService.updateById(post);
|
||||
if (!updated) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
CommunityPost updatedPost = communityPostService.getById(id);
|
||||
return Result.success(convertToResponse(updatedPost));
|
||||
@PutMapping(value = "/update")
|
||||
public Result<CommunityPostResponse> update(@RequestBody @Validated CommunityPostUpdateRequest request) {
|
||||
CommunityPostResponse response = communityPostService.update(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除帖子
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable String id) {
|
||||
boolean deleted = communityPostService.removeById(id);
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@RequestParam String id) {
|
||||
boolean deleted = communityPostService.delete(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类型查询帖子
|
||||
*/
|
||||
@GetMapping("/type/{type}")
|
||||
public Result<List<CommunityPostResponse>> getByType(@PathVariable String type) {
|
||||
List<CommunityPost> posts = communityPostService.getByType(type);
|
||||
List<CommunityPostResponse> responses = posts.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据地点ID查询帖子
|
||||
*/
|
||||
@GetMapping("/location/{locationId}")
|
||||
public Result<List<CommunityPostResponse>> getByLocationId(@PathVariable String locationId) {
|
||||
List<CommunityPost> posts = communityPostService.getByLocationId(locationId);
|
||||
List<CommunityPostResponse> responses = posts.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询最受欢迎的帖子
|
||||
*/
|
||||
@GetMapping("/popular")
|
||||
public Result<List<CommunityPostResponse>> getMostLikedPosts(@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<CommunityPost> posts = communityPostService.getMostLikedPosts(limit);
|
||||
List<CommunityPostResponse> responses = posts.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询最新的帖子
|
||||
*/
|
||||
@GetMapping("/latest")
|
||||
public Result<List<CommunityPostResponse>> getLatestPosts(@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<CommunityPost> posts = communityPostService.getLatestPosts(limit);
|
||||
List<CommunityPostResponse> responses = posts.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞帖子
|
||||
*/
|
||||
@PutMapping("/{id}/like")
|
||||
public Result<Void> likePost(@PathVariable String id) {
|
||||
boolean updated = communityPostService.updateLikes(id, 1);
|
||||
if (!updated) {
|
||||
return Result.error("点赞失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消点赞帖子
|
||||
*/
|
||||
@PutMapping("/{id}/unlike")
|
||||
public Result<Void> unlikePost(@PathVariable String id) {
|
||||
boolean updated = communityPostService.updateLikes(id, -1);
|
||||
if (!updated) {
|
||||
return Result.error("取消点赞失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签搜索帖子
|
||||
*/
|
||||
@GetMapping("/search/tag")
|
||||
public Result<List<CommunityPostResponse>> getByTag(@RequestParam String tag) {
|
||||
List<CommunityPost> posts = communityPostService.getByTag(tag);
|
||||
List<CommunityPostResponse> responses = posts.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private CommunityPostResponse convertToResponse(CommunityPost post) {
|
||||
CommunityPostResponse response = new CommunityPostResponse();
|
||||
BeanUtils.copyProperties(post, response);
|
||||
response.setId(post.getId());
|
||||
if (post.getCreateTime() != null) {
|
||||
response.setCreateTime(post.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (post.getUpdateTime() != null) {
|
||||
response.setUpdateTime(post.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 帖子创建请求
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class CommunityPostCreateRequest {
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
|
||||
@NotBlank(message = "标题不能为空")
|
||||
private String title;
|
||||
|
||||
@NotBlank(message = "内容不能为空")
|
||||
private String content;
|
||||
|
||||
private String type;
|
||||
private String locationId;
|
||||
private String tags;
|
||||
private Integer isPrivate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 帖子响应类
|
||||
*/
|
||||
@lombok.Data
|
||||
@lombok.EqualsAndHashCode(callSuper = true)
|
||||
public static class CommunityPostResponse extends BaseResponse {
|
||||
private String userId;
|
||||
private String title;
|
||||
private String content;
|
||||
private String type;
|
||||
private String locationId;
|
||||
private String tags;
|
||||
private Integer likes;
|
||||
private Integer viewCount;
|
||||
private Integer commentCount;
|
||||
private Integer isPrivate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,17 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.PageRequest;
|
||||
import com.emotion.dto.request.ConversationCreateRequest;
|
||||
import com.emotion.dto.request.ConversationPageRequest;
|
||||
import com.emotion.dto.response.ConversationResponse;
|
||||
import com.emotion.entity.Conversation;
|
||||
import com.emotion.service.ConversationService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 对话控制器
|
||||
@@ -31,95 +26,51 @@ public class ConversationController {
|
||||
@Autowired
|
||||
private ConversationService conversationService;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 分页查询对话
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<ConversationResponse>> getPage(@Valid PageRequest request) {
|
||||
IPage<Conversation> page = conversationService.getPage(request);
|
||||
List<ConversationResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<ConversationResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询对话
|
||||
*/
|
||||
@GetMapping("/user/{userId}/page")
|
||||
public Result<PageResult<ConversationResponse>> getPageByUserId(@PathVariable String userId,
|
||||
@Valid PageRequest request) {
|
||||
IPage<Conversation> page = conversationService.getPageByUserId(request, userId);
|
||||
List<ConversationResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<ConversationResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<ConversationResponse>> getPage(@Valid ConversationPageRequest request) {
|
||||
PageResult<ConversationResponse> pageResult = conversationService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取对话
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<ConversationResponse> getById(@PathVariable String id) {
|
||||
Conversation conversation = conversationService.getById(id);
|
||||
if (conversation == null) {
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<ConversationResponse> getById(@RequestParam String id) {
|
||||
ConversationResponse response = conversationService.getConversationResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("对话不存在");
|
||||
}
|
||||
return Result.success(convertToResponse(conversation));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建对话
|
||||
*/
|
||||
@PostMapping
|
||||
@PostMapping(value = "/create")
|
||||
public Result<ConversationResponse> create(@Valid @RequestBody ConversationCreateRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
String clientIp = getClientIp(httpRequest);
|
||||
Conversation conversation = conversationService.createConversation(
|
||||
request.getUserId(),
|
||||
request.getTitle(),
|
||||
null // cozeConversationId
|
||||
);
|
||||
return Result.success(convertToResponse(conversation));
|
||||
ConversationResponse response = conversationService.createConversationWithResponse(request, httpRequest);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对话
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<ConversationResponse> update(@PathVariable String id, @RequestBody Conversation conversation) {
|
||||
conversation.setId(id);
|
||||
boolean updated = conversationService.updateById(conversation);
|
||||
if (!updated) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
Conversation updatedConversation = conversationService.getById(id);
|
||||
return Result.success(convertToResponse(updatedConversation));
|
||||
@PutMapping(value = "/update")
|
||||
public Result<ConversationResponse> update(@RequestBody ConversationCreateRequest request) {
|
||||
ConversationResponse response = conversationService.updateConversationWithResponse(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对话
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable String id) {
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@RequestParam String id) {
|
||||
boolean deleted = conversationService.removeById(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
@@ -127,93 +78,35 @@ public class ConversationController {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID查询对话
|
||||
*/
|
||||
@GetMapping("/user/{userId}")
|
||||
public Result<List<ConversationResponse>> getByUserId(@PathVariable String userId) {
|
||||
List<Conversation> conversations = conversationService.getByUserId(userId);
|
||||
List<ConversationResponse> responses = conversations.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃对话
|
||||
*/
|
||||
@GetMapping("/active")
|
||||
@GetMapping(value = "/active")
|
||||
public Result<List<ConversationResponse>> getActiveConversations() {
|
||||
// 暂时返回空列表,因为接口中没有这个方法
|
||||
return Result.success();
|
||||
List<ConversationResponse> responses = conversationService.getActiveConversationsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取归档对话
|
||||
*/
|
||||
@GetMapping("/archived")
|
||||
@GetMapping(value = "/archived")
|
||||
public Result<List<ConversationResponse>> getArchivedConversations() {
|
||||
// 暂时返回空列表,因为接口中没有这个方法
|
||||
List<ConversationResponse> responses = conversationService.getArchivedConversationsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对话状态
|
||||
*/
|
||||
@PutMapping(value = "/status")
|
||||
public Result<Void> updateConversationStatus(
|
||||
@RequestParam String id,
|
||||
@RequestParam String status) {
|
||||
boolean updated = conversationService.updateConversationStatus(id, status);
|
||||
if (!updated) {
|
||||
return Result.error("更新状态失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 归档对话
|
||||
*/
|
||||
@PutMapping("/{id}/archive")
|
||||
public Result<Void> archiveConversation(@PathVariable String id) {
|
||||
// 暂时返回成功,因为接口中没有这个方法
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活对话
|
||||
*/
|
||||
@PutMapping("/{id}/activate")
|
||||
public Result<Void> activateConversation(@PathVariable String id) {
|
||||
// 暂时返回成功,因为接口中没有这个方法
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户对话数量
|
||||
*/
|
||||
@GetMapping("/user/{userId}/count")
|
||||
public Result<Long> countByUserId(@PathVariable String userId) {
|
||||
Long count = conversationService.countByUserId(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP
|
||||
*/
|
||||
private String getClientIp(HttpServletRequest request) {
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
|
||||
return xForwardedFor.split(",")[0];
|
||||
}
|
||||
|
||||
String xRealIp = request.getHeader("X-Real-IP");
|
||||
if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
|
||||
return xRealIp;
|
||||
}
|
||||
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private ConversationResponse convertToResponse(Conversation conversation) {
|
||||
ConversationResponse response = new ConversationResponse();
|
||||
BeanUtils.copyProperties(conversation, response);
|
||||
response.setId(conversation.getId());
|
||||
if (conversation.getCreateTime() != null) {
|
||||
response.setCreateTime(conversation.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (conversation.getUpdateTime() != null) {
|
||||
response.setUpdateTime(conversation.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.PageRequest;
|
||||
import com.emotion.dto.response.BaseResponse;
|
||||
import com.emotion.entity.CozeApiCall;
|
||||
import com.emotion.dto.request.coze.CozeApiCallPageRequest;
|
||||
import com.emotion.dto.request.coze.CozeApiCallCreateRequest;
|
||||
import com.emotion.dto.request.coze.CozeApiCallUpdateRequest;
|
||||
import com.emotion.dto.response.coze.CozeApiCallResponse;
|
||||
import com.emotion.service.CozeApiCallService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Coze API调用记录控制器
|
||||
@@ -29,229 +26,54 @@ public class CozeApiCallController {
|
||||
@Autowired
|
||||
private CozeApiCallService cozeApiCallService;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 分页查询API调用记录
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<CozeApiCallResponse>> getPage(@Validated PageRequest request) {
|
||||
IPage<CozeApiCall> page = cozeApiCallService.getPage(request);
|
||||
List<CozeApiCallResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<CozeApiCallResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话ID分页查询API调用记录
|
||||
*/
|
||||
@GetMapping("/conversation/{conversationId}/page")
|
||||
public Result<PageResult<CozeApiCallResponse>> getPageByConversationId(@PathVariable String conversationId, @Validated PageRequest request) {
|
||||
IPage<CozeApiCall> page = cozeApiCallService.getPageByConversationId(request, conversationId);
|
||||
List<CozeApiCallResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<CozeApiCallResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询API调用记录
|
||||
*/
|
||||
@GetMapping("/user/{userId}/page")
|
||||
public Result<PageResult<CozeApiCallResponse>> getPageByUserId(@PathVariable String userId, @Validated PageRequest request) {
|
||||
IPage<CozeApiCall> page = cozeApiCallService.getPageByUserId(request, userId);
|
||||
List<CozeApiCallResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<CozeApiCallResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<CozeApiCallResponse>> getCozeApiCallPage(@Validated CozeApiCallPageRequest request) {
|
||||
PageResult<CozeApiCallResponse> pageResult = cozeApiCallService.getPage(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取API调用记录
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<CozeApiCallResponse> getById(@PathVariable String id) {
|
||||
CozeApiCall apiCall = cozeApiCallService.getById(id);
|
||||
if (apiCall == null) {
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<CozeApiCallResponse> getCozeApiCallById(@RequestParam String id) {
|
||||
CozeApiCallResponse response = cozeApiCallService.getById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("API调用记录不存在");
|
||||
}
|
||||
return Result.success(convertToResponse(apiCall));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Bot ID查询API调用记录
|
||||
*/
|
||||
@GetMapping("/bot/{botId}")
|
||||
public Result<List<CozeApiCallResponse>> getByBotId(@PathVariable String botId) {
|
||||
List<CozeApiCall> apiCalls = cozeApiCallService.getByBotId(botId);
|
||||
List<CozeApiCallResponse> responses = apiCalls.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据状态查询API调用记录
|
||||
*/
|
||||
@GetMapping("/status/{status}")
|
||||
public Result<List<CozeApiCallResponse>> getByStatus(@PathVariable String status) {
|
||||
List<CozeApiCall> apiCalls = cozeApiCallService.getByStatus(status);
|
||||
List<CozeApiCallResponse> responses = apiCalls.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据请求类型查询API调用记录
|
||||
*/
|
||||
@GetMapping("/request-type/{requestType}")
|
||||
public Result<List<CozeApiCallResponse>> getByRequestType(@PathVariable String requestType) {
|
||||
List<CozeApiCall> apiCalls = cozeApiCallService.getByRequestType(requestType);
|
||||
List<CozeApiCallResponse> responses = apiCalls.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户的API调用次数
|
||||
*/
|
||||
@GetMapping("/user/{userId}/count")
|
||||
public Result<Long> countByUserId(@PathVariable String userId) {
|
||||
Long count = cozeApiCallService.countByUserId(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计Bot的API调用次数
|
||||
*/
|
||||
@GetMapping("/bot/{botId}/count")
|
||||
public Result<Long> countByBotId(@PathVariable String botId) {
|
||||
Long count = cozeApiCallService.countByBotId(botId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计指定状态的API调用次数
|
||||
*/
|
||||
@GetMapping("/status/{status}/count")
|
||||
public Result<Long> countByStatus(@PathVariable String status) {
|
||||
Long count = cozeApiCallService.countByStatus(status);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户的Token使用量
|
||||
*/
|
||||
@GetMapping("/user/{userId}/tokens")
|
||||
public Result<Long> sumTokensByUserId(@PathVariable String userId) {
|
||||
Long totalTokens = cozeApiCallService.sumTokensByUserId(userId);
|
||||
return Result.success(totalTokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户的API调用费用
|
||||
*/
|
||||
@GetMapping("/user/{userId}/cost")
|
||||
public Result<java.math.BigDecimal> sumCostByUserId(@PathVariable String userId) {
|
||||
java.math.BigDecimal totalCost = cozeApiCallService.sumCostByUserId(userId);
|
||||
return Result.success(totalCost);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询失败的API调用记录
|
||||
*/
|
||||
@GetMapping("/failed")
|
||||
public Result<List<CozeApiCallResponse>> getFailedCalls() {
|
||||
List<CozeApiCall> apiCalls = cozeApiCallService.getFailedCalls();
|
||||
List<CozeApiCallResponse> responses = apiCalls.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询超时的API调用记录
|
||||
*/
|
||||
@GetMapping("/timeout")
|
||||
public Result<List<CozeApiCallResponse>> getTimeoutCalls() {
|
||||
List<CozeApiCall> apiCalls = cozeApiCallService.getTimeoutCalls();
|
||||
List<CozeApiCallResponse> responses = apiCalls.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据追踪ID查询API调用记录
|
||||
*/
|
||||
@GetMapping("/trace/{traceId}")
|
||||
public Result<CozeApiCallResponse> getByTraceId(@PathVariable String traceId) {
|
||||
CozeApiCall apiCall = cozeApiCallService.getByTraceId(traceId);
|
||||
if (apiCall == null) {
|
||||
return Result.notFound("API调用记录不存在");
|
||||
}
|
||||
return Result.success(convertToResponse(apiCall));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建API调用记录
|
||||
*/
|
||||
@PostMapping
|
||||
public Result<CozeApiCallResponse> create(@RequestBody CozeApiCall apiCall) {
|
||||
boolean saved = cozeApiCallService.save(apiCall);
|
||||
if (!saved) {
|
||||
return Result.error("创建失败");
|
||||
}
|
||||
return Result.success(convertToResponse(apiCall));
|
||||
@PostMapping(value = "/create")
|
||||
public Result<CozeApiCallResponse> createCozeApiCall(@RequestBody @Validated CozeApiCallCreateRequest request) {
|
||||
CozeApiCallResponse response = cozeApiCallService.create(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新API调用记录
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<CozeApiCallResponse> update(@PathVariable String id, @RequestBody CozeApiCall apiCall) {
|
||||
apiCall.setId(id);
|
||||
boolean updated = cozeApiCallService.updateById(apiCall);
|
||||
if (!updated) {
|
||||
return Result.error("更新失败");
|
||||
@PutMapping(value = "/update")
|
||||
public Result<CozeApiCallResponse> updateCozeApiCall(@RequestBody @Validated CozeApiCallUpdateRequest request) {
|
||||
CozeApiCallResponse response = cozeApiCallService.update(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败,记录不存在");
|
||||
}
|
||||
CozeApiCall updatedApiCall = cozeApiCallService.getById(id);
|
||||
return Result.success(convertToResponse(updatedApiCall));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除API调用记录
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable String id) {
|
||||
boolean deleted = cozeApiCallService.removeById(id);
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> deleteCozeApiCall(@RequestParam String id) {
|
||||
boolean deleted = cozeApiCallService.delete(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
@@ -259,49 +81,47 @@ public class CozeApiCallController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
* 统计用户的API调用次数
|
||||
*/
|
||||
private CozeApiCallResponse convertToResponse(CozeApiCall apiCall) {
|
||||
CozeApiCallResponse response = new CozeApiCallResponse();
|
||||
BeanUtils.copyProperties(apiCall, response);
|
||||
response.setId(apiCall.getId());
|
||||
if (apiCall.getCreateTime() != null) {
|
||||
response.setCreateTime(apiCall.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (apiCall.getUpdateTime() != null) {
|
||||
response.setUpdateTime(apiCall.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (apiCall.getStartTime() != null) {
|
||||
response.setStartTime(apiCall.getStartTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (apiCall.getEndTime() != null) {
|
||||
response.setEndTime(apiCall.getEndTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
@GetMapping(value = "/countByUser")
|
||||
public Result<Long> countByUserId(@RequestParam String userId) {
|
||||
Long count = cozeApiCallService.countByUserId(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* API调用记录响应类
|
||||
* 统计Bot的API调用次数
|
||||
*/
|
||||
@lombok.Data
|
||||
@lombok.EqualsAndHashCode(callSuper = true)
|
||||
public static class CozeApiCallResponse extends BaseResponse {
|
||||
private String conversationId;
|
||||
private String messageId;
|
||||
private String userId;
|
||||
private String botId;
|
||||
private String requestType;
|
||||
private String requestUrl;
|
||||
private String requestBody;
|
||||
private Integer responseStatus;
|
||||
private String responseBody;
|
||||
private String aiReply;
|
||||
private Integer totalTokens;
|
||||
private java.math.BigDecimal cost;
|
||||
private String status;
|
||||
private String finalStatus;
|
||||
private String startTime;
|
||||
private String endTime;
|
||||
private String traceId;
|
||||
@GetMapping(value = "/countByBot")
|
||||
public Result<Long> countByBotId(@RequestParam String botId) {
|
||||
Long count = cozeApiCallService.countByBotId(botId);
|
||||
return Result.success(count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计指定状态的API调用次数
|
||||
*/
|
||||
@GetMapping(value = "/countByStatus")
|
||||
public Result<Long> countByStatus(@RequestParam String status) {
|
||||
Long count = cozeApiCallService.countByStatus(status);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户的Token使用量
|
||||
*/
|
||||
@GetMapping(value = "/tokensByUser")
|
||||
public Result<Long> sumTokensByUserId(@RequestParam String userId) {
|
||||
Long totalTokens = cozeApiCallService.sumTokensByUserId(userId);
|
||||
return Result.success(totalTokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户的API调用费用
|
||||
*/
|
||||
@GetMapping(value = "/costByUser")
|
||||
public Result<BigDecimal> sumCostByUserId(@RequestParam String userId) {
|
||||
BigDecimal totalCost = cozeApiCallService.sumCostByUserId(userId);
|
||||
return Result.success(totalCost);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,19 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.DiaryCommentCreateRequest;
|
||||
import com.emotion.dto.request.DiaryCommentPageRequest;
|
||||
import com.emotion.dto.response.DiaryCommentResponse;
|
||||
import com.emotion.entity.DiaryComment;
|
||||
import com.emotion.service.DiaryCommentService;
|
||||
import com.emotion.service.DiaryPostService;
|
||||
import com.emotion.service.UserService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 日记评论控制器
|
||||
@@ -30,7 +22,7 @@ import java.util.stream.Collectors;
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/diary-comment")
|
||||
@RequestMapping("/diaryComment")
|
||||
public class DiaryCommentController {
|
||||
|
||||
@Autowired
|
||||
@@ -39,152 +31,79 @@ public class DiaryCommentController {
|
||||
@Autowired
|
||||
private DiaryPostService diaryPostService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 分页查询评论
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<DiaryCommentResponse>> getPage(@Validated BasePageRequest request) {
|
||||
IPage<DiaryComment> page = diaryCommentService.getPage(request);
|
||||
List<DiaryCommentResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<DiaryCommentResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据日记ID分页查询评论
|
||||
*/
|
||||
@GetMapping("/diary/{diaryId}/page")
|
||||
public Result<PageResult<DiaryCommentResponse>> getPageByDiaryId(@PathVariable String diaryId,
|
||||
@Validated BasePageRequest request) {
|
||||
IPage<DiaryComment> page = diaryCommentService.getPageByDiaryId(diaryId, request);
|
||||
List<DiaryCommentResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<DiaryCommentResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询评论
|
||||
*/
|
||||
@GetMapping("/user/{userId}/page")
|
||||
public Result<PageResult<DiaryCommentResponse>> getPageByUserId(@PathVariable String userId,
|
||||
@Validated BasePageRequest request) {
|
||||
IPage<DiaryComment> page = diaryCommentService.getPageByUserId(userId, request);
|
||||
List<DiaryCommentResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<DiaryCommentResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<DiaryCommentResponse>> getPage(@Validated DiaryCommentPageRequest request) {
|
||||
PageResult<DiaryCommentResponse> pageResult = diaryCommentService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评论树结构
|
||||
*/
|
||||
@GetMapping("/diary/{diaryId}/tree")
|
||||
public Result<List<DiaryCommentResponse>> getCommentTree(@PathVariable String diaryId) {
|
||||
List<DiaryComment> comments = diaryCommentService.getCommentTree(diaryId);
|
||||
List<DiaryCommentResponse> responses = comments.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
@GetMapping(value = "/commentTree")
|
||||
public Result<List<DiaryCommentResponse>> getCommentTree(@RequestParam String diaryId) {
|
||||
List<DiaryCommentResponse> responses = diaryCommentService.getCommentTreeWithResponse(diaryId);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取评论详情
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<DiaryCommentResponse> getById(@PathVariable String id) {
|
||||
DiaryComment comment = diaryCommentService.getById(id);
|
||||
if (comment == null) {
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<DiaryCommentResponse> getById(@RequestParam String id) {
|
||||
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("评论不存在");
|
||||
}
|
||||
return Result.success(convertToResponse(comment));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建评论
|
||||
*/
|
||||
@PostMapping
|
||||
@PostMapping(value = "/create")
|
||||
public Result<DiaryCommentResponse> create(@Valid @RequestBody DiaryCommentCreateRequest request) {
|
||||
DiaryComment comment = diaryCommentService.createComment(
|
||||
request.getDiaryId(),
|
||||
request.getUserId(),
|
||||
request.getContent(),
|
||||
request.getImages(),
|
||||
request.getParentCommentId(),
|
||||
request.getIsAnonymous()
|
||||
);
|
||||
// 从上下文中获取当前用户ID,而不是直接使用请求中的用户ID
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
if (currentUserId != null) {
|
||||
request.setUserId(currentUserId);
|
||||
}
|
||||
DiaryCommentResponse response = diaryCommentService.createCommentWithResponse(request);
|
||||
|
||||
// 更新日记的评论数
|
||||
diaryPostService.incrementCommentCount(request.getDiaryId());
|
||||
diaryPostService.updateLastCommentTime(request.getDiaryId());
|
||||
|
||||
return Result.success(convertToResponse(comment));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新评论
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<DiaryCommentResponse> update(@PathVariable String id, @Valid @RequestBody DiaryCommentCreateRequest request) {
|
||||
boolean updated = diaryCommentService.updateComment(id, request.getContent(), request.getImages());
|
||||
if (!updated) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
|
||||
DiaryComment updatedComment = diaryCommentService.getById(id);
|
||||
return Result.success(convertToResponse(updatedComment));
|
||||
@PutMapping(value = "/update")
|
||||
public Result<DiaryCommentResponse> update(@Valid @RequestBody DiaryCommentCreateRequest request) {
|
||||
DiaryCommentResponse response = diaryCommentService.updateCommentWithResponse(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable String id) {
|
||||
DiaryComment comment = diaryCommentService.getById(id);
|
||||
if (comment == null) {
|
||||
return Result.error("评论不存在");
|
||||
}
|
||||
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@RequestParam String id) {
|
||||
boolean deleted = diaryCommentService.deleteComment(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
|
||||
// 更新日记的评论数
|
||||
diaryPostService.decrementCommentCount(comment.getDiaryId());
|
||||
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
|
||||
if (response != null) {
|
||||
diaryPostService.decrementCommentCount(response.getDiaryId());
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
@@ -192,20 +111,18 @@ public class DiaryCommentController {
|
||||
/**
|
||||
* 软删除评论
|
||||
*/
|
||||
@DeleteMapping("/{id}/soft")
|
||||
public Result<Void> softDelete(@PathVariable String id) {
|
||||
DiaryComment comment = diaryCommentService.getById(id);
|
||||
if (comment == null) {
|
||||
return Result.error("评论不存在");
|
||||
}
|
||||
|
||||
@DeleteMapping(value = "/softDelete")
|
||||
public Result<Void> softDelete(@RequestParam String id) {
|
||||
boolean deleted = diaryCommentService.softDeleteComment(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
|
||||
// 更新日记的评论数
|
||||
diaryPostService.decrementCommentCount(comment.getDiaryId());
|
||||
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
|
||||
if (response != null) {
|
||||
diaryPostService.decrementCommentCount(response.getDiaryId());
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
@@ -213,20 +130,18 @@ public class DiaryCommentController {
|
||||
/**
|
||||
* 恢复评论
|
||||
*/
|
||||
@PutMapping("/{id}/restore")
|
||||
public Result<Void> restore(@PathVariable String id) {
|
||||
DiaryComment comment = diaryCommentService.getById(id);
|
||||
if (comment == null) {
|
||||
return Result.error("评论不存在");
|
||||
}
|
||||
|
||||
@PutMapping(value = "/restore")
|
||||
public Result<Void> restore(@RequestParam String id) {
|
||||
boolean restored = diaryCommentService.restoreComment(id);
|
||||
if (!restored) {
|
||||
return Result.error("恢复失败");
|
||||
}
|
||||
|
||||
// 更新日记的评论数
|
||||
diaryPostService.incrementCommentCount(comment.getDiaryId());
|
||||
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
|
||||
if (response != null) {
|
||||
diaryPostService.incrementCommentCount(response.getDiaryId());
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
@@ -234,8 +149,8 @@ public class DiaryCommentController {
|
||||
/**
|
||||
* 点赞评论
|
||||
*/
|
||||
@PostMapping("/{id}/like")
|
||||
public Result<Void> like(@PathVariable String id) {
|
||||
@PostMapping(value = "/like")
|
||||
public Result<Void> like(@RequestParam String id) {
|
||||
boolean liked = diaryCommentService.incrementLikeCount(id);
|
||||
if (!liked) {
|
||||
return Result.error("点赞失败");
|
||||
@@ -246,8 +161,8 @@ public class DiaryCommentController {
|
||||
/**
|
||||
* 取消点赞评论
|
||||
*/
|
||||
@DeleteMapping("/{id}/like")
|
||||
public Result<Void> unlike(@PathVariable String id) {
|
||||
@DeleteMapping(value = "/unlike")
|
||||
public Result<Void> unlike(@RequestParam String id) {
|
||||
boolean unliked = diaryCommentService.decrementLikeCount(id);
|
||||
if (!unliked) {
|
||||
return Result.error("取消点赞失败");
|
||||
@@ -258,75 +173,12 @@ public class DiaryCommentController {
|
||||
/**
|
||||
* 设置置顶状态
|
||||
*/
|
||||
@PutMapping("/{id}/top/{isTop}")
|
||||
public Result<Void> setTop(@PathVariable String id, @PathVariable Integer isTop) {
|
||||
@PutMapping(value = "/setTop")
|
||||
public Result<Void> setTop(@RequestParam String id, @RequestParam Integer isTop) {
|
||||
boolean set = diaryCommentService.setTop(id, isTop);
|
||||
if (!set) {
|
||||
return Result.error("设置置顶状态失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计日记评论数量
|
||||
*/
|
||||
@GetMapping("/diary/{diaryId}/count")
|
||||
public Result<Long> countByDiaryId(@PathVariable String diaryId) {
|
||||
Long count = diaryCommentService.countByDiaryId(diaryId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户评论数量
|
||||
*/
|
||||
@GetMapping("/user/{userId}/count")
|
||||
public Result<Long> countByUserId(@PathVariable String userId) {
|
||||
Long count = diaryCommentService.countByUserId(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计回复数量
|
||||
*/
|
||||
@GetMapping("/parent/{parentCommentId}/replies/count")
|
||||
public Result<Long> countReplies(@PathVariable String parentCommentId) {
|
||||
Long count = diaryCommentService.countReplies(parentCommentId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换实体为响应DTO
|
||||
*/
|
||||
private DiaryCommentResponse convertToResponse(DiaryComment comment) {
|
||||
DiaryCommentResponse response = new DiaryCommentResponse();
|
||||
BeanUtils.copyProperties(comment, response);
|
||||
|
||||
// 转换时间格式
|
||||
if (comment.getPublishTime() != null) {
|
||||
response.setPublishTime(comment.getPublishTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (comment.getLastReplyTime() != null) {
|
||||
response.setLastReplyTime(comment.getLastReplyTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (comment.getCreateTime() != null) {
|
||||
response.setCreateTime(comment.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (comment.getUpdateTime() != null) {
|
||||
response.setUpdateTime(comment.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
|
||||
// 转换JSON字段
|
||||
try {
|
||||
if (comment.getImages() != null) {
|
||||
response.setImages(objectMapper.readValue(comment.getImages(), new TypeReference<List<String>>() {}));
|
||||
}
|
||||
if (comment.getMetadata() != null) {
|
||||
response.setMetadata(objectMapper.readValue(comment.getMetadata(), Object.class));
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
// 忽略JSON解析错误
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,18 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.DiaryPostCreateRequest;
|
||||
import com.emotion.dto.request.DiaryPostPageRequest;
|
||||
import com.emotion.dto.request.DiaryPostUpdateRequest;
|
||||
import com.emotion.dto.response.DiaryPostResponse;
|
||||
import com.emotion.entity.DiaryPost;
|
||||
import com.emotion.service.DiaryPostService;
|
||||
import com.emotion.service.DiaryCommentService;
|
||||
import com.emotion.service.UserService;
|
||||
import com.emotion.service.AiChatService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import com.emotion.entity.DiaryComment;
|
||||
|
||||
/**
|
||||
* 用户日记控制器
|
||||
@@ -33,161 +21,75 @@ import com.emotion.entity.DiaryComment;
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/diary-post")
|
||||
@RequestMapping("/diaryPost")
|
||||
public class DiaryPostController {
|
||||
|
||||
@Autowired
|
||||
private DiaryPostService diaryPostService;
|
||||
|
||||
@Autowired
|
||||
private DiaryCommentService diaryCommentService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private AiChatService aiChatService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 分页查询日记
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<DiaryPostResponse>> getPage(@Validated BasePageRequest request) {
|
||||
IPage<DiaryPost> page = diaryPostService.getPage(request);
|
||||
List<DiaryPostResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<DiaryPostResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询日记
|
||||
*/
|
||||
@GetMapping("/user/{userId}/page")
|
||||
public Result<PageResult<DiaryPostResponse>> getPageByUserId(@PathVariable String userId,
|
||||
@Validated BasePageRequest request) {
|
||||
IPage<DiaryPost> page = diaryPostService.getPageByUserId(userId, request);
|
||||
List<DiaryPostResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<DiaryPostResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID查询公开日记
|
||||
*/
|
||||
@GetMapping("/user/{userId}/public/page")
|
||||
public Result<PageResult<DiaryPostResponse>> getPublicPageByUserId(@PathVariable String userId,
|
||||
@Validated BasePageRequest request) {
|
||||
IPage<DiaryPost> page = diaryPostService.getPublicPageByUserId(userId, request);
|
||||
List<DiaryPostResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<DiaryPostResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询精选日记
|
||||
*/
|
||||
@GetMapping("/featured/page")
|
||||
public Result<PageResult<DiaryPostResponse>> getFeaturedPage(@Validated BasePageRequest request) {
|
||||
IPage<DiaryPost> page = diaryPostService.getFeaturedPage(request);
|
||||
List<DiaryPostResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<DiaryPostResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<DiaryPostResponse>> getPage(@Validated DiaryPostPageRequest request) {
|
||||
return Result.success(diaryPostService.getPageWithResponse(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取日记详情
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<DiaryPostResponse> getById(@PathVariable String id) {
|
||||
DiaryPost diaryPost = diaryPostService.getById(id);
|
||||
if (diaryPost == null) {
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<DiaryPostResponse> getById(@RequestParam String id) {
|
||||
DiaryPostResponse response = diaryPostService.getDiaryPostResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("日记不存在");
|
||||
}
|
||||
|
||||
// 增加浏览数
|
||||
diaryPostService.incrementViewCount(id);
|
||||
|
||||
return Result.success(convertToResponse(diaryPost));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建日记
|
||||
*/
|
||||
@PostMapping
|
||||
@PostMapping(value = "/create")
|
||||
public Result<DiaryPostResponse> create(@Valid @RequestBody DiaryPostCreateRequest request) {
|
||||
DiaryPost diaryPost = diaryPostService.createDiaryPost(request);
|
||||
return Result.success(convertToResponse(diaryPost));
|
||||
// 从上下文中获取当前用户ID,而不是直接使用请求中的用户ID
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
if (currentUserId != null) {
|
||||
request.setUserId(currentUserId);
|
||||
}
|
||||
return Result.success(diaryPostService.createDiaryPostWithResponse(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发表日记并生成AI评论
|
||||
*/
|
||||
@PostMapping("/publish")
|
||||
@PostMapping(value = "/publish")
|
||||
public Result<DiaryPostResponse> publish(@Valid @RequestBody DiaryPostCreateRequest request) {
|
||||
// 从上下文中获取当前用户ID,而不是直接使用请求中的用户ID
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
if (currentUserId != null) {
|
||||
request.setUserId(currentUserId);
|
||||
}
|
||||
return Result.success(diaryPostService.publishDiaryWithAiComment(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新日记
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<DiaryPostResponse> update(@PathVariable String id, @Valid @RequestBody DiaryPostUpdateRequest request) {
|
||||
boolean updated = diaryPostService.updateDiaryPost(id, request);
|
||||
|
||||
if (!updated) {
|
||||
@PutMapping(value = "/update")
|
||||
public Result<DiaryPostResponse> update(@Valid @RequestBody DiaryPostUpdateRequest request) {
|
||||
DiaryPostResponse response = diaryPostService.updateDiaryPostWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
|
||||
DiaryPost updatedDiaryPost = diaryPostService.getById(id);
|
||||
return Result.success(convertToResponse(updatedDiaryPost));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除日记
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable String id) {
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@RequestParam String id) {
|
||||
boolean deleted = diaryPostService.deleteDiaryPost(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
@@ -198,8 +100,8 @@ public class DiaryPostController {
|
||||
/**
|
||||
* 软删除日记
|
||||
*/
|
||||
@DeleteMapping("/{id}/soft")
|
||||
public Result<Void> softDelete(@PathVariable String id) {
|
||||
@DeleteMapping(value = "/softDelete")
|
||||
public Result<Void> softDelete(@RequestParam String id) {
|
||||
boolean deleted = diaryPostService.softDeleteDiaryPost(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
@@ -210,8 +112,8 @@ public class DiaryPostController {
|
||||
/**
|
||||
* 恢复日记
|
||||
*/
|
||||
@PutMapping("/{id}/restore")
|
||||
public Result<Void> restore(@PathVariable String id) {
|
||||
@PutMapping(value = "/restore")
|
||||
public Result<Void> restore(@RequestParam String id) {
|
||||
boolean restored = diaryPostService.restoreDiaryPost(id);
|
||||
if (!restored) {
|
||||
return Result.error("恢复失败");
|
||||
@@ -222,8 +124,8 @@ public class DiaryPostController {
|
||||
/**
|
||||
* 点赞日记
|
||||
*/
|
||||
@PostMapping("/{id}/like")
|
||||
public Result<Void> like(@PathVariable String id) {
|
||||
@PostMapping(value = "/like")
|
||||
public Result<Void> like(@RequestParam String id) {
|
||||
boolean liked = diaryPostService.incrementLikeCount(id);
|
||||
if (!liked) {
|
||||
return Result.error("点赞失败");
|
||||
@@ -234,8 +136,8 @@ public class DiaryPostController {
|
||||
/**
|
||||
* 取消点赞日记
|
||||
*/
|
||||
@DeleteMapping("/{id}/like")
|
||||
public Result<Void> unlike(@PathVariable String id) {
|
||||
@DeleteMapping(value = "/unlike")
|
||||
public Result<Void> unlike(@RequestParam String id) {
|
||||
boolean unliked = diaryPostService.decrementLikeCount(id);
|
||||
if (!unliked) {
|
||||
return Result.error("取消点赞失败");
|
||||
@@ -246,8 +148,8 @@ public class DiaryPostController {
|
||||
/**
|
||||
* 分享日记
|
||||
*/
|
||||
@PostMapping("/{id}/share")
|
||||
public Result<Void> share(@PathVariable String id) {
|
||||
@PostMapping(value = "/share")
|
||||
public Result<Void> share(@RequestParam String id) {
|
||||
boolean shared = diaryPostService.incrementShareCount(id);
|
||||
if (!shared) {
|
||||
return Result.error("分享失败");
|
||||
@@ -258,8 +160,8 @@ public class DiaryPostController {
|
||||
/**
|
||||
* 设置精选状态
|
||||
*/
|
||||
@PutMapping("/{id}/featured/{featured}")
|
||||
public Result<Void> setFeatured(@PathVariable String id, @PathVariable Integer featured) {
|
||||
@PutMapping(value = "/setFeatured")
|
||||
public Result<Void> setFeatured(@RequestParam String id, @RequestParam Integer featured) {
|
||||
boolean set = diaryPostService.setFeatured(id, featured);
|
||||
if (!set) {
|
||||
return Result.error("设置精选状态失败");
|
||||
@@ -270,90 +172,12 @@ public class DiaryPostController {
|
||||
/**
|
||||
* 设置置顶优先级
|
||||
*/
|
||||
@PutMapping("/{id}/priority/{priority}")
|
||||
public Result<Void> setPriority(@PathVariable String id, @PathVariable Integer priority) {
|
||||
@PutMapping(value = "/setPriority")
|
||||
public Result<Void> setPriority(@RequestParam String id, @RequestParam Integer priority) {
|
||||
boolean set = diaryPostService.setPriority(id, priority);
|
||||
if (!set) {
|
||||
return Result.error("设置优先级失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户日记数量
|
||||
*/
|
||||
@GetMapping("/user/{userId}/count")
|
||||
public Result<Long> countByUserId(@PathVariable String userId) {
|
||||
Long count = diaryPostService.countByUserId(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户公开日记数量
|
||||
*/
|
||||
@GetMapping("/user/{userId}/public/count")
|
||||
public Result<Long> countPublicByUserId(@PathVariable String userId) {
|
||||
Long count = diaryPostService.countPublicByUserId(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计精选日记数量
|
||||
*/
|
||||
@GetMapping("/featured/count")
|
||||
public Result<Long> countFeatured() {
|
||||
Long count = diaryPostService.countFeatured();
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换实体为响应DTO
|
||||
*/
|
||||
private DiaryPostResponse convertToResponse(DiaryPost diaryPost) {
|
||||
DiaryPostResponse response = new DiaryPostResponse();
|
||||
BeanUtils.copyProperties(diaryPost, response);
|
||||
|
||||
// 转换时间格式
|
||||
if (diaryPost.getPublishTime() != null) {
|
||||
response.setPublishTime(diaryPost.getPublishTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (diaryPost.getLastCommentTime() != null) {
|
||||
response.setLastCommentTime(diaryPost.getLastCommentTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (diaryPost.getAiCommentTime() != null) {
|
||||
response.setAiCommentTime(diaryPost.getAiCommentTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (diaryPost.getCreateTime() != null) {
|
||||
response.setCreateTime(diaryPost.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (diaryPost.getUpdateTime() != null) {
|
||||
response.setUpdateTime(diaryPost.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
|
||||
// 转换JSON字段
|
||||
try {
|
||||
if (diaryPost.getImages() != null) {
|
||||
response.setImages(objectMapper.readValue(diaryPost.getImages(), new TypeReference<List<String>>() {}));
|
||||
}
|
||||
if (diaryPost.getVideos() != null) {
|
||||
response.setVideos(objectMapper.readValue(diaryPost.getVideos(), new TypeReference<List<String>>() {}));
|
||||
}
|
||||
if (diaryPost.getTags() != null) {
|
||||
response.setTags(objectMapper.readValue(diaryPost.getTags(), new TypeReference<List<String>>() {}));
|
||||
}
|
||||
if (diaryPost.getAiKeywords() != null) {
|
||||
response.setAiKeywords(objectMapper.readValue(diaryPost.getAiKeywords(), new TypeReference<List<String>>() {}));
|
||||
}
|
||||
if (diaryPost.getAiEmotionAnalysis() != null) {
|
||||
response.setAiEmotionAnalysis(objectMapper.readValue(diaryPost.getAiEmotionAnalysis(), Object.class));
|
||||
}
|
||||
if (diaryPost.getMetadata() != null) {
|
||||
response.setMetadata(objectMapper.readValue(diaryPost.getMetadata(), Object.class));
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
// 忽略JSON解析错误
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
-160
@@ -1,24 +1,22 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.PageRequest;
|
||||
import com.emotion.dto.response.BaseResponse;
|
||||
import com.emotion.entity.EmotionAnalysis;
|
||||
import com.emotion.dto.request.EmotionAnalysisCreateRequest;
|
||||
import com.emotion.dto.request.EmotionAnalysisPageRequest;
|
||||
import com.emotion.dto.request.EmotionAnalysisUpdateRequest;
|
||||
import com.emotion.dto.response.EmotionAnalysisResponse;
|
||||
import com.emotion.service.EmotionAnalysisService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 情绪分析控制器
|
||||
@@ -33,108 +31,74 @@ public class EmotionAnalysisController {
|
||||
@Autowired
|
||||
private EmotionAnalysisService emotionAnalysisService;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 分页查询情绪分析记录
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<EmotionAnalysisResponse>> getPage(@Validated PageRequest request) {
|
||||
IPage<EmotionAnalysis> page = emotionAnalysisService.getPage(request);
|
||||
List<EmotionAnalysisResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<EmotionAnalysisResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
public Result<PageResult<EmotionAnalysisResponse>> getPage(@Validated EmotionAnalysisPageRequest request) {
|
||||
return Result.success(emotionAnalysisService.getPageWithResponse(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询情绪分析记录
|
||||
*/
|
||||
@GetMapping("/user/{userId}/page")
|
||||
public Result<PageResult<EmotionAnalysisResponse>> getPageByUserId(@PathVariable String userId, @Validated PageRequest request) {
|
||||
IPage<EmotionAnalysis> page = emotionAnalysisService.getPageByUserId(request, userId);
|
||||
List<EmotionAnalysisResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<EmotionAnalysisResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
|
||||
return Result.success(pageResult);
|
||||
@GetMapping("/user/page")
|
||||
public Result<PageResult<EmotionAnalysisResponse>> getPageByUserId(
|
||||
@RequestParam String userId,
|
||||
@Validated EmotionAnalysisPageRequest request) {
|
||||
return Result.success(emotionAnalysisService.getPageByUserIdWithResponse(userId, request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取情绪分析记录
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<EmotionAnalysisResponse> getById(@PathVariable String id) {
|
||||
EmotionAnalysis analysis = emotionAnalysisService.getById(id);
|
||||
if (analysis == null) {
|
||||
@GetMapping
|
||||
public Result<EmotionAnalysisResponse> getById(@RequestParam String id) {
|
||||
EmotionAnalysisResponse response = emotionAnalysisService.getEmotionAnalysisResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("情绪分析记录不存在");
|
||||
}
|
||||
return Result.success(convertToResponse(analysis));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据消息ID获取情绪分析记录
|
||||
*/
|
||||
@GetMapping("/message/{messageId}")
|
||||
public Result<EmotionAnalysisResponse> getByMessageId(@PathVariable String messageId) {
|
||||
EmotionAnalysis analysis = emotionAnalysisService.getByMessageId(messageId);
|
||||
if (analysis == null) {
|
||||
@GetMapping("/message")
|
||||
public Result<EmotionAnalysisResponse> getByMessageId(@RequestParam String messageId) {
|
||||
EmotionAnalysisResponse response = emotionAnalysisService.getEmotionAnalysisResponseByMessageId(messageId);
|
||||
if (response == null) {
|
||||
return Result.notFound("情绪分析记录不存在");
|
||||
}
|
||||
return Result.success(convertToResponse(analysis));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建情绪分析记录
|
||||
*/
|
||||
@PostMapping
|
||||
public Result<EmotionAnalysisResponse> create(@RequestBody @Validated EmotionAnalysisCreateRequest request) {
|
||||
EmotionAnalysis analysis = emotionAnalysisService.createEmotionAnalysis(
|
||||
request.getMessageId(),
|
||||
request.getUserId(),
|
||||
request.getPrimaryEmotion(),
|
||||
request.getPolarity(),
|
||||
request.getIntensity(),
|
||||
request.getConfidence()
|
||||
);
|
||||
return Result.success(convertToResponse(analysis));
|
||||
public Result<EmotionAnalysisResponse> create(@RequestBody @Valid EmotionAnalysisCreateRequest request) {
|
||||
return Result.success(emotionAnalysisService.createEmotionAnalysisWithResponse(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新情绪分析记录
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<EmotionAnalysisResponse> update(@PathVariable String id, @RequestBody EmotionAnalysis analysis) {
|
||||
analysis.setId(id);
|
||||
boolean updated = emotionAnalysisService.updateById(analysis);
|
||||
if (!updated) {
|
||||
@PutMapping
|
||||
public Result<EmotionAnalysisResponse> update(@RequestBody @Valid EmotionAnalysisUpdateRequest request) {
|
||||
EmotionAnalysisResponse response = emotionAnalysisService.updateEmotionAnalysisWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
EmotionAnalysis updatedAnalysis = emotionAnalysisService.getById(id);
|
||||
return Result.success(convertToResponse(updatedAnalysis));
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除情绪分析记录
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable String id) {
|
||||
boolean deleted = emotionAnalysisService.removeById(id);
|
||||
@DeleteMapping
|
||||
public Result<Void> delete(@RequestParam String id) {
|
||||
boolean deleted = emotionAnalysisService.deleteEmotionAnalysis(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
@@ -144,59 +108,53 @@ public class EmotionAnalysisController {
|
||||
/**
|
||||
* 根据主要情绪查询分析记录
|
||||
*/
|
||||
@GetMapping("/emotion/{primaryEmotion}")
|
||||
public Result<List<EmotionAnalysisResponse>> getByPrimaryEmotion(@PathVariable String primaryEmotion) {
|
||||
List<EmotionAnalysis> analyses = emotionAnalysisService.getByPrimaryEmotion(primaryEmotion);
|
||||
List<EmotionAnalysisResponse> responses = analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
@GetMapping("/emotion")
|
||||
public Result<List<EmotionAnalysisResponse>> getByPrimaryEmotion(@RequestParam String primaryEmotion) {
|
||||
List<EmotionAnalysisResponse> responses = emotionAnalysisService
|
||||
.getEmotionAnalysisResponsesByPrimaryEmotion(primaryEmotion);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据情绪极性查询分析记录
|
||||
*/
|
||||
@GetMapping("/polarity/{polarity}")
|
||||
public Result<List<EmotionAnalysisResponse>> getByPolarity(@PathVariable String polarity) {
|
||||
List<EmotionAnalysis> analyses = emotionAnalysisService.getByPolarity(polarity);
|
||||
List<EmotionAnalysisResponse> responses = analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
@GetMapping("/polarity")
|
||||
public Result<List<EmotionAnalysisResponse>> getByPolarity(@RequestParam String polarity) {
|
||||
List<EmotionAnalysisResponse> responses = emotionAnalysisService
|
||||
.getEmotionAnalysisResponsesByPolarity(polarity);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID和情绪类型查询分析记录
|
||||
*/
|
||||
@GetMapping("/user/{userId}/emotion/{primaryEmotion}")
|
||||
public Result<List<EmotionAnalysisResponse>> getByUserIdAndEmotion(@PathVariable String userId, @PathVariable String primaryEmotion) {
|
||||
List<EmotionAnalysis> analyses = emotionAnalysisService.getByUserIdAndEmotion(userId, primaryEmotion);
|
||||
List<EmotionAnalysisResponse> responses = analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
@GetMapping("/user/emotion")
|
||||
public Result<List<EmotionAnalysisResponse>> getByUserIdAndEmotion(
|
||||
@RequestParam String userId,
|
||||
@RequestParam String primaryEmotion) {
|
||||
List<EmotionAnalysisResponse> responses = emotionAnalysisService
|
||||
.getEmotionAnalysisResponsesByUserIdAndEmotion(userId, primaryEmotion);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间范围查询用户情绪分析记录
|
||||
*/
|
||||
@GetMapping("/user/{userId}/time-range")
|
||||
@GetMapping("/user/time-range")
|
||||
public Result<List<EmotionAnalysisResponse>> getByUserIdAndTimeRange(
|
||||
@PathVariable String userId,
|
||||
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
|
||||
@RequestParam String userId,
|
||||
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
|
||||
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime) {
|
||||
List<EmotionAnalysis> analyses = emotionAnalysisService.getByUserIdAndTimeRange(userId, startTime, endTime);
|
||||
List<EmotionAnalysisResponse> responses = analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
List<EmotionAnalysisResponse> responses = emotionAnalysisService
|
||||
.getEmotionAnalysisResponsesByUserIdAndTimeRange(userId, startTime, endTime);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户的情绪分析记录数量
|
||||
*/
|
||||
@GetMapping("/user/{userId}/count")
|
||||
public Result<Long> countByUserId(@PathVariable String userId) {
|
||||
@GetMapping("/user/count")
|
||||
public Result<Long> countByUserId(@RequestParam String userId) {
|
||||
Long count = emotionAnalysisService.countByUserId(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
@@ -204,20 +162,20 @@ public class EmotionAnalysisController {
|
||||
/**
|
||||
* 查询用户最近的情绪分析记录
|
||||
*/
|
||||
@GetMapping("/user/{userId}/recent")
|
||||
public Result<List<EmotionAnalysisResponse>> getRecentByUserId(@PathVariable String userId, @RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<EmotionAnalysis> analyses = emotionAnalysisService.getRecentByUserId(userId, limit);
|
||||
List<EmotionAnalysisResponse> responses = analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
@GetMapping("/user/recent")
|
||||
public Result<List<EmotionAnalysisResponse>> getRecentByUserId(
|
||||
@RequestParam String userId,
|
||||
@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<EmotionAnalysisResponse> responses = emotionAnalysisService
|
||||
.getEmotionAnalysisResponsesRecentByUserId(userId, limit);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户的平均情绪强度
|
||||
*/
|
||||
@GetMapping("/user/{userId}/avg-intensity")
|
||||
public Result<Double> getAvgIntensityByUserId(@PathVariable String userId) {
|
||||
@GetMapping("/user/avg-intensity")
|
||||
public Result<Double> getAvgIntensityByUserId(@RequestParam String userId) {
|
||||
Double avgIntensity = emotionAnalysisService.getAvgIntensityByUserId(userId);
|
||||
return Result.success(avgIntensity);
|
||||
}
|
||||
@@ -225,63 +183,9 @@ public class EmotionAnalysisController {
|
||||
/**
|
||||
* 查询用户最常见的情绪类型
|
||||
*/
|
||||
@GetMapping("/user/{userId}/most-frequent-emotion")
|
||||
public Result<String> getMostFrequentEmotionByUserId(@PathVariable String userId) {
|
||||
@GetMapping("/user/most-frequent-emotion")
|
||||
public Result<String> getMostFrequentEmotionByUserId(@RequestParam String userId) {
|
||||
String emotion = emotionAnalysisService.getMostFrequentEmotionByUserId(userId);
|
||||
return Result.success(emotion);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private EmotionAnalysisResponse convertToResponse(EmotionAnalysis analysis) {
|
||||
EmotionAnalysisResponse response = new EmotionAnalysisResponse();
|
||||
BeanUtils.copyProperties(analysis, response);
|
||||
response.setId(analysis.getId());
|
||||
if (analysis.getCreateTime() != null) {
|
||||
response.setCreateTime(analysis.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (analysis.getUpdateTime() != null) {
|
||||
response.setUpdateTime(analysis.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 情绪分析创建请求
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class EmotionAnalysisCreateRequest {
|
||||
@NotBlank(message = "消息ID不能为空")
|
||||
private String messageId;
|
||||
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
|
||||
@NotBlank(message = "主要情绪不能为空")
|
||||
private String primaryEmotion;
|
||||
|
||||
private String polarity;
|
||||
|
||||
@NotNull(message = "情绪强度不能为空")
|
||||
private Double intensity;
|
||||
|
||||
@NotNull(message = "置信度不能为空")
|
||||
private Double confidence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 情绪分析响应类
|
||||
*/
|
||||
@lombok.Data
|
||||
@lombok.EqualsAndHashCode(callSuper = true)
|
||||
public static class EmotionAnalysisResponse extends BaseResponse {
|
||||
private String messageId;
|
||||
private String userId;
|
||||
private String primaryEmotion;
|
||||
private String polarity;
|
||||
private Double intensity;
|
||||
private Double confidence;
|
||||
private String emotionDetails;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.entity.EmotionRecord;
|
||||
import com.emotion.dto.request.EmotionRecordCreateRequest;
|
||||
import com.emotion.dto.request.EmotionRecordPageRequest;
|
||||
import com.emotion.dto.request.EmotionRecordUpdateRequest;
|
||||
import com.emotion.dto.response.EmotionRecordResponse;
|
||||
import com.emotion.service.EmotionRecordService;
|
||||
import com.emotion.util.CurrentUserUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -11,10 +14,14 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 情绪记录控制器
|
||||
@@ -36,29 +43,16 @@ public class EmotionRecordController {
|
||||
* 创建情绪记录
|
||||
*/
|
||||
@PostMapping
|
||||
public Result<Map<String, Object>> createRecord(@RequestBody Map<String, Object> request) {
|
||||
log.info("创建情绪记录: {}", request);
|
||||
@Operation(summary = "创建情绪记录", description = "创建新的情绪记录")
|
||||
public Result<EmotionRecordResponse> createRecord(@RequestBody @Valid EmotionRecordCreateRequest request) {
|
||||
log.info("创建情绪记录: userId={}", request.getUserId());
|
||||
|
||||
try {
|
||||
Map<String, Object> record = new HashMap<>();
|
||||
record.put("id", "record-" + System.currentTimeMillis());
|
||||
record.put("userId", request.get("userId"));
|
||||
record.put("recordDate", request.get("recordDate"));
|
||||
record.put("emotionType", request.get("emotionType"));
|
||||
record.put("intensity", request.get("intensity"));
|
||||
record.put("triggers", request.get("triggers"));
|
||||
record.put("description", request.get("description"));
|
||||
record.put("tags", request.get("tags"));
|
||||
record.put("weather", request.get("weather"));
|
||||
record.put("location", request.get("location"));
|
||||
record.put("activity", request.get("activity"));
|
||||
record.put("createTime", LocalDateTime.now());
|
||||
|
||||
return Result.success("创建成功", record);
|
||||
} catch (Exception e) {
|
||||
log.error("创建情绪记录失败: {}", e.getMessage());
|
||||
return Result.error("创建失败");
|
||||
}
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = CurrentUserUtil.requireCurrentUserId();
|
||||
request.setUserId(userId);
|
||||
|
||||
EmotionRecordResponse response = emotionRecordService.createEmotionRecordWithResponse(request);
|
||||
return Result.success("创建成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,30 +60,20 @@ public class EmotionRecordController {
|
||||
*/
|
||||
@Operation(summary = "获取用户情绪记录列表", description = "分页获取当前用户的情绪记录,按创建时间倒序")
|
||||
@GetMapping("/user")
|
||||
public Result<IPage<EmotionRecord>> getRecordList(
|
||||
@Parameter(description = "页码,从1开始") @RequestParam(defaultValue = "1") Integer current,
|
||||
@Parameter(description = "每页大小") @RequestParam(defaultValue = "10") Integer size) {
|
||||
public Result<PageResult<EmotionRecordResponse>> getRecordList(
|
||||
@Validated EmotionRecordPageRequest request) {
|
||||
|
||||
try {
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = CurrentUserUtil.requireCurrentUserId();
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = CurrentUserUtil.requireCurrentUserId();
|
||||
|
||||
log.info("获取用户情绪记录列表: userId={}, current={}, size={}", userId, current, size);
|
||||
log.info("获取用户情绪记录列表: userId={}, current={}, size={}", userId, request.getCurrent(), request.getSize());
|
||||
|
||||
IPage<EmotionRecord> page = emotionRecordService.getByUserIdWithPage(userId, current, size);
|
||||
PageResult<EmotionRecordResponse> page = emotionRecordService.getPageByUserIdWithResponse(userId, request);
|
||||
|
||||
log.info("获取用户情绪记录成功: userId={}, total={}, records={}",
|
||||
userId, page.getTotal(), page.getRecords().size());
|
||||
log.info("获取用户情绪记录成功: userId={}, total={}, records={}",
|
||||
userId, page.getTotal(), page.getRecords().size());
|
||||
|
||||
return Result.success(page);
|
||||
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("用户认证失败: {}", e.getMessage());
|
||||
return Result.error(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户情绪记录失败", e);
|
||||
return Result.error("获取情绪记录失败: " + e.getMessage());
|
||||
}
|
||||
return Result.success(page);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,138 +81,82 @@ public class EmotionRecordController {
|
||||
*/
|
||||
@Operation(summary = "获取用户最近情绪记录", description = "获取当前用户最近的情绪记录列表")
|
||||
@GetMapping("/user/recent")
|
||||
public Result<List<EmotionRecord>> getRecentRecords(
|
||||
public Result<List<EmotionRecordResponse>> getRecentRecords(
|
||||
@Parameter(description = "限制数量") @RequestParam(defaultValue = "5") Integer limit) {
|
||||
|
||||
try {
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = CurrentUserUtil.requireCurrentUserId();
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = CurrentUserUtil.requireCurrentUserId();
|
||||
|
||||
log.info("获取用户最近情绪记录: userId={}, limit={}", userId, limit);
|
||||
log.info("获取用户最近情绪记录: userId={}, limit={}", userId, limit);
|
||||
|
||||
List<EmotionRecord> records = emotionRecordService.getRecentByUserId(userId, limit);
|
||||
List<EmotionRecordResponse> records = emotionRecordService.getRecentByUserIdWithResponse(userId, limit);
|
||||
|
||||
log.info("获取用户最近情绪记录成功: userId={}, records={}", userId, records.size());
|
||||
log.info("获取用户最近情绪记录成功: userId={}, records={}", userId, records.size());
|
||||
|
||||
return Result.success(records);
|
||||
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("用户认证失败: {}", e.getMessage());
|
||||
return Result.error(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户最近情绪记录失败", e);
|
||||
return Result.error("获取最近记录失败: " + e.getMessage());
|
||||
}
|
||||
return Result.success(records);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取情绪记录详情
|
||||
*/
|
||||
@GetMapping("/{recordId}")
|
||||
public Result<Map<String, Object>> getRecord(@PathVariable String recordId) {
|
||||
log.info("获取情绪记录详情: {}", recordId);
|
||||
@Operation(summary = "获取情绪记录详情", description = "根据ID获取情绪记录详情")
|
||||
@GetMapping
|
||||
public Result<EmotionRecordResponse> getRecord(@RequestParam String id) {
|
||||
log.info("获取情绪记录详情: {}", id);
|
||||
|
||||
try {
|
||||
Map<String, Object> record = new HashMap<>();
|
||||
record.put("id", recordId);
|
||||
record.put("userId", "user123");
|
||||
record.put("recordDate", LocalDate.now());
|
||||
record.put("emotionType", "joy");
|
||||
record.put("intensity", 0.8);
|
||||
record.put("triggers", "完成了重要项目");
|
||||
record.put("description", "今天心情很好,完成了一个重要的项目");
|
||||
record.put("tags", Arrays.asList("工作", "成就感"));
|
||||
record.put("weather", "晴天");
|
||||
record.put("location", "办公室");
|
||||
record.put("activity", "工作");
|
||||
record.put("createTime", LocalDateTime.now());
|
||||
|
||||
return Result.success(record);
|
||||
} catch (Exception e) {
|
||||
log.error("获取情绪记录详情失败: {}", e.getMessage());
|
||||
return Result.error("获取详情失败");
|
||||
EmotionRecordResponse response = emotionRecordService.getEmotionRecordResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("情绪记录不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新情绪记录
|
||||
*/
|
||||
@PutMapping("/{recordId}")
|
||||
public Result<Map<String, Object>> updateRecord(@PathVariable String recordId,
|
||||
@RequestBody Map<String, Object> request) {
|
||||
log.info("更新情绪记录: {}", recordId);
|
||||
@Operation(summary = "更新情绪记录", description = "更新指定的情绪记录")
|
||||
@PutMapping
|
||||
public Result<EmotionRecordResponse> updateRecord(@RequestBody @Valid EmotionRecordUpdateRequest request) {
|
||||
log.info("更新情绪记录: {}", request.getId());
|
||||
|
||||
try {
|
||||
Map<String, Object> record = new HashMap<>();
|
||||
record.put("id", recordId);
|
||||
record.put("emotionType", request.get("emotionType"));
|
||||
record.put("intensity", request.get("intensity"));
|
||||
record.put("triggers", request.get("triggers"));
|
||||
record.put("description", request.get("description"));
|
||||
record.put("tags", request.get("tags"));
|
||||
record.put("updateTime", LocalDateTime.now());
|
||||
|
||||
return Result.success("更新成功", record);
|
||||
} catch (Exception e) {
|
||||
log.error("更新情绪记录失败: {}", e.getMessage());
|
||||
return Result.error("更新失败");
|
||||
EmotionRecordResponse response = emotionRecordService.updateEmotionRecordWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.notFound("情绪记录不存在");
|
||||
}
|
||||
return Result.success("更新成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除情绪记录
|
||||
*/
|
||||
@DeleteMapping("/{recordId}")
|
||||
public Result<String> deleteRecord(@PathVariable String recordId) {
|
||||
log.info("删除情绪记录: {}", recordId);
|
||||
@Operation(summary = "删除情绪记录", description = "删除指定的情绪记录")
|
||||
@DeleteMapping
|
||||
public Result<String> deleteRecord(@RequestParam String id) {
|
||||
log.info("删除情绪记录: {}", id);
|
||||
|
||||
try {
|
||||
return Result.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除情绪记录失败: {}", e.getMessage());
|
||||
return Result.error("删除失败");
|
||||
boolean deleted = emotionRecordService.deleteEmotionRecord(id);
|
||||
if (!deleted) {
|
||||
return Result.notFound("情绪记录不存在");
|
||||
}
|
||||
return Result.success("删除成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取情绪统计
|
||||
*/
|
||||
@GetMapping("/stats/{userId}")
|
||||
public Result<Map<String, Object>> getEmotionStats(@PathVariable String userId,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate) {
|
||||
@Operation(summary = "获取情绪统计", description = "获取当前用户的情绪统计信息")
|
||||
@GetMapping("/stats")
|
||||
public Result<Map<String, Object>> getEmotionStats(
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate) {
|
||||
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = CurrentUserUtil.requireCurrentUserId();
|
||||
|
||||
log.info("获取情绪统计: userId={}, startDate={}, endDate={}", userId, startDate, endDate);
|
||||
|
||||
try {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
// 情绪类型分布
|
||||
Map<String, Integer> emotionDistribution = new HashMap<>();
|
||||
emotionDistribution.put("joy", 15);
|
||||
emotionDistribution.put("sadness", 5);
|
||||
emotionDistribution.put("anger", 3);
|
||||
emotionDistribution.put("fear", 2);
|
||||
emotionDistribution.put("surprise", 8);
|
||||
emotionDistribution.put("neutral", 12);
|
||||
|
||||
stats.put("emotionDistribution", emotionDistribution);
|
||||
stats.put("totalRecords", 45);
|
||||
stats.put("averageIntensity", 0.72);
|
||||
stats.put("mostFrequentEmotion", "joy");
|
||||
stats.put("emotionTrend", "improving");
|
||||
|
||||
return Result.success(stats);
|
||||
} catch (Exception e) {
|
||||
log.error("获取情绪统计失败: {}", e.getMessage());
|
||||
return Result.error("获取统计失败");
|
||||
}
|
||||
Map<String, Object> stats = emotionRecordService.getEmotionStats(userId, startDate, endDate);
|
||||
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机情绪类型
|
||||
*/
|
||||
private String getRandomEmotion() {
|
||||
String[] emotions = {"joy", "sadness", "anger", "fear", "surprise", "neutral"};
|
||||
return emotions[new Random().nextInt(emotions.length)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,4 +22,4 @@ public class ChatStatsRequest extends BaseRequest {
|
||||
* 会话ID
|
||||
*/
|
||||
private String conversationId;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,11 @@ import javax.validation.constraints.NotBlank;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ConversationCreateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 对话ID(更新时使用)
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 对话分页请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ConversationPageRequest extends PageRequest {
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 对话状态(可选)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 对话类型(可选)
|
||||
*/
|
||||
private String type;
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -16,6 +15,11 @@ import java.util.List;
|
||||
@Data
|
||||
public class DiaryCommentCreateRequest {
|
||||
|
||||
/**
|
||||
* 评论ID (用于更新操作)
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 日记ID
|
||||
*/
|
||||
@@ -49,4 +53,4 @@ public class DiaryCommentCreateRequest {
|
||||
*/
|
||||
@NotNull(message = "是否匿名不能为空")
|
||||
private Integer isAnonymous;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* 日记评论分页请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DiaryCommentPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 日记ID(可选)
|
||||
*/
|
||||
private String diaryId;
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 评论类型(可选)
|
||||
*/
|
||||
private String commentType;
|
||||
|
||||
/**
|
||||
* 是否只查询顶级评论(可选)
|
||||
*/
|
||||
private Boolean topLevelOnly;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 日记分页请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DiaryPostPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 是否只查询公开日记(可选)
|
||||
*/
|
||||
private Boolean publicOnly;
|
||||
|
||||
/**
|
||||
* 是否只查询精选日记(可选)
|
||||
*/
|
||||
private Boolean featuredOnly;
|
||||
|
||||
/**
|
||||
* 心情状态(可选)
|
||||
*/
|
||||
private String mood;
|
||||
|
||||
/**
|
||||
* 标签(可选)
|
||||
*/
|
||||
private String tag;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
@@ -15,6 +16,12 @@ import java.util.List;
|
||||
@Data
|
||||
public class DiaryPostUpdateRequest {
|
||||
|
||||
/**
|
||||
* 日记ID
|
||||
*/
|
||||
@NotBlank(message = "日记ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 日记标题
|
||||
*/
|
||||
@@ -88,4 +95,4 @@ public class DiaryPostUpdateRequest {
|
||||
* 状态: draft-草稿, published-已发布, hidden-隐藏, deleted-已删除
|
||||
*/
|
||||
private String status;
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 情绪分析创建请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionAnalysisCreateRequest {
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
@NotBlank(message = "消息ID不能为空")
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 主要情绪
|
||||
*/
|
||||
@NotBlank(message = "主要情绪不能为空")
|
||||
private String primaryEmotion;
|
||||
|
||||
/**
|
||||
* 情绪极性
|
||||
*/
|
||||
private String polarity;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
@NotNull(message = "情绪强度不能为空")
|
||||
private Double intensity;
|
||||
|
||||
/**
|
||||
* 置信度
|
||||
*/
|
||||
@NotNull(message = "置信度不能为空")
|
||||
private Double confidence;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 情绪分析分页请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EmotionAnalysisPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 消息ID(可选)
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 主要情绪(可选)
|
||||
*/
|
||||
private String primaryEmotion;
|
||||
|
||||
/**
|
||||
* 情绪极性(可选)
|
||||
*/
|
||||
private String polarity;
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 情绪分析更新请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionAnalysisUpdateRequest {
|
||||
|
||||
/**
|
||||
* 情绪分析ID
|
||||
*/
|
||||
@NotBlank(message = "情绪分析ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 主要情绪
|
||||
*/
|
||||
private String primaryEmotion;
|
||||
|
||||
/**
|
||||
* 情绪极性
|
||||
*/
|
||||
private String polarity;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
private Double intensity;
|
||||
|
||||
/**
|
||||
* 置信度
|
||||
*/
|
||||
private Double confidence;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 情绪记录创建请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionRecordCreateRequest {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 记录日期
|
||||
*/
|
||||
@NotNull(message = "记录日期不能为空")
|
||||
private LocalDate recordDate;
|
||||
|
||||
/**
|
||||
* 情绪类型
|
||||
*/
|
||||
@NotBlank(message = "情绪类型不能为空")
|
||||
private String emotionType;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
@NotNull(message = "情绪强度不能为空")
|
||||
private BigDecimal intensity;
|
||||
|
||||
/**
|
||||
* 触发因素
|
||||
*/
|
||||
private String triggers;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private List<String> tags;
|
||||
|
||||
/**
|
||||
* 天气
|
||||
*/
|
||||
private String weather;
|
||||
|
||||
/**
|
||||
* 地点
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 活动
|
||||
*/
|
||||
private String activity;
|
||||
|
||||
/**
|
||||
* 相关人物
|
||||
*/
|
||||
private String people;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String notes;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 情绪记录分页请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EmotionRecordPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 情绪类型(可选)
|
||||
*/
|
||||
private String emotionType;
|
||||
|
||||
/**
|
||||
* 地点(可选)
|
||||
*/
|
||||
private String location;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 情绪记录更新请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionRecordUpdateRequest {
|
||||
|
||||
/**
|
||||
* 情绪记录ID
|
||||
*/
|
||||
@NotBlank(message = "情绪记录ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 记录日期
|
||||
*/
|
||||
private LocalDate recordDate;
|
||||
|
||||
/**
|
||||
* 情绪类型
|
||||
*/
|
||||
private String emotionType;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
private BigDecimal intensity;
|
||||
|
||||
/**
|
||||
* 触发因素
|
||||
*/
|
||||
private String triggers;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private List<String> tags;
|
||||
|
||||
/**
|
||||
* 天气
|
||||
*/
|
||||
private String weather;
|
||||
|
||||
/**
|
||||
* 地点
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 活动
|
||||
*/
|
||||
private String activity;
|
||||
|
||||
/**
|
||||
* 相关人物
|
||||
*/
|
||||
private String people;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String notes;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 访客用户信息请求类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-07-24
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GuestUserInfoRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
private String clientIp;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* WebSocket请求对象
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WebSocketRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 发送者ID
|
||||
*/
|
||||
private String senderId;
|
||||
|
||||
/**
|
||||
* 发送者类型
|
||||
*/
|
||||
private String senderType;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private String messageType;
|
||||
|
||||
/**
|
||||
* 会话ID(可选)
|
||||
*/
|
||||
private String conversationId;
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.emotion.dto.request.achievement;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 创建成就请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class AchievementCreateRequest {
|
||||
|
||||
/**
|
||||
* 成就标题
|
||||
*/
|
||||
@NotBlank(message = "成就标题不能为空")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 分类
|
||||
*/
|
||||
@NotBlank(message = "分类不能为空")
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
|
||||
/**
|
||||
* 稀有度
|
||||
*/
|
||||
@NotBlank(message = "稀有度不能为空")
|
||||
private String rarity;
|
||||
|
||||
/**
|
||||
* 条件类型
|
||||
*/
|
||||
private String conditionType;
|
||||
|
||||
/**
|
||||
* 条件值
|
||||
*/
|
||||
private String conditionValue;
|
||||
|
||||
/**
|
||||
* 奖励
|
||||
*/
|
||||
private String rewards;
|
||||
|
||||
/**
|
||||
* 是否隐藏
|
||||
*/
|
||||
private Integer isHidden;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.emotion.dto.request.achievement;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 成就分页查询请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AchievementPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 成就分类
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 成就稀有度
|
||||
*/
|
||||
private String rarity;
|
||||
|
||||
/**
|
||||
* 是否已解锁
|
||||
*/
|
||||
private Boolean unlocked;
|
||||
|
||||
/**
|
||||
* 是否隐藏
|
||||
*/
|
||||
private Boolean hidden;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.emotion.dto.request.achievement;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 更新成就进度请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class AchievementProgressUpdateRequest {
|
||||
|
||||
/**
|
||||
* 成就ID
|
||||
*/
|
||||
@NotBlank(message = "成就ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 进度值
|
||||
*/
|
||||
@NotNull(message = "进度值不能为空")
|
||||
private Double progress;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.emotion.dto.request.achievement;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 解锁成就请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class AchievementUnlockRequest {
|
||||
|
||||
/**
|
||||
* 成就ID
|
||||
*/
|
||||
@NotBlank(message = "成就ID不能为空")
|
||||
private String id;
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package com.emotion.dto.request.achievement;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 更新成就请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class AchievementUpdateRequest {
|
||||
|
||||
/**
|
||||
* 成就ID
|
||||
*/
|
||||
@NotBlank(message = "成就ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 成就标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 分类
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
|
||||
/**
|
||||
* 稀有度
|
||||
*/
|
||||
private String rarity;
|
||||
|
||||
/**
|
||||
* 条件类型
|
||||
*/
|
||||
private String conditionType;
|
||||
|
||||
/**
|
||||
* 条件值
|
||||
*/
|
||||
private String conditionValue;
|
||||
|
||||
/**
|
||||
* 奖励
|
||||
*/
|
||||
private String rewards;
|
||||
|
||||
/**
|
||||
* 是否隐藏
|
||||
*/
|
||||
private Integer isHidden;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.emotion.dto.request.comment;
|
||||
|
||||
import com.emotion.dto.request.BaseRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 评论创建请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommentCreateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 帖子ID
|
||||
*/
|
||||
@NotBlank(message = "帖子ID不能为空")
|
||||
private String postId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId; // 不再使用@NotBlank注解,因为将从上下文中获取
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
@NotBlank(message = "评论内容不能为空")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 回复的评论ID
|
||||
*/
|
||||
private String replyToId;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.emotion.dto.request.comment;
|
||||
|
||||
import com.emotion.dto.request.PageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* 评论分页请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommentPageRequest extends PageRequest {
|
||||
|
||||
/**
|
||||
* 帖子ID(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "帖子ID格式不正确")
|
||||
private String postId;
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "用户ID格式不正确")
|
||||
private String userId;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.emotion.dto.request.comment;
|
||||
|
||||
import com.emotion.dto.request.BaseRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 评论查询请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommentQueryRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 帖子ID
|
||||
*/
|
||||
private String postId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 回复的评论ID
|
||||
*/
|
||||
private String replyToId;
|
||||
|
||||
/**
|
||||
* 最小点赞数
|
||||
*/
|
||||
private Integer minLikes;
|
||||
|
||||
/**
|
||||
* 最大点赞数
|
||||
*/
|
||||
private Integer maxLikes;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.emotion.dto.request.comment;
|
||||
|
||||
import com.emotion.dto.request.BaseRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 评论更新请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommentUpdateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 评论ID
|
||||
*/
|
||||
@NotBlank(message = "评论ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
@NotBlank(message = "评论内容不能为空")
|
||||
private String content;
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.emotion.dto.request.community;
|
||||
|
||||
import com.emotion.dto.request.BaseRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 社区帖子创建请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommunityPostCreateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
@NotBlank(message = "标题不能为空")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
@NotBlank(message = "内容不能为空")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 帖子类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 地点ID
|
||||
*/
|
||||
private String locationId;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private String tags;
|
||||
|
||||
/**
|
||||
* 是否私密
|
||||
*/
|
||||
private Integer isPrivate;
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.emotion.dto.request.community;
|
||||
|
||||
import com.emotion.dto.request.PageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* 社区帖子分页请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommunityPostPageRequest extends PageRequest {
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "用户ID格式不正确")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 帖子类型(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\u4e00-\\u9fa5]{1,20}$", message = "帖子类型格式不正确")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 地点ID(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "地点ID格式不正确")
|
||||
private String locationId;
|
||||
|
||||
/**
|
||||
* 是否只查询公开帖子
|
||||
*/
|
||||
private Boolean publicOnly;
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.emotion.dto.request.community;
|
||||
|
||||
import com.emotion.dto.request.BaseRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 社区帖子更新请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommunityPostUpdateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 帖子ID
|
||||
*/
|
||||
@NotBlank(message = "帖子ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
@NotBlank(message = "标题不能为空")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
@NotBlank(message = "内容不能为空")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 帖子类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 地点ID
|
||||
*/
|
||||
private String locationId;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private String tags;
|
||||
|
||||
/**
|
||||
* 是否私密
|
||||
*/
|
||||
private Integer isPrivate;
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
package com.emotion.dto.request.coze;
|
||||
|
||||
import com.emotion.dto.request.BaseRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Coze API调用记录创建请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CozeApiCallCreateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 对话ID
|
||||
*/
|
||||
@NotBlank(message = "对话ID不能为空")
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* Coze聊天ID
|
||||
*/
|
||||
private String cozeChatId;
|
||||
|
||||
/**
|
||||
* Coze对话ID
|
||||
*/
|
||||
private String cozeConversationId;
|
||||
|
||||
/**
|
||||
* Bot ID
|
||||
*/
|
||||
private String botId;
|
||||
|
||||
/**
|
||||
* Workflow ID
|
||||
*/
|
||||
private String workflowId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 请求类型: chat/stream/retrieve/messages
|
||||
*/
|
||||
private String requestType;
|
||||
|
||||
/**
|
||||
* 请求URL
|
||||
*/
|
||||
private String requestUrl;
|
||||
|
||||
/**
|
||||
* 请求体
|
||||
*/
|
||||
private String requestBody;
|
||||
|
||||
/**
|
||||
* 请求头
|
||||
*/
|
||||
private String requestHeaders;
|
||||
|
||||
/**
|
||||
* 用户输入的消息内容
|
||||
*/
|
||||
private String userMessage;
|
||||
|
||||
/**
|
||||
* 用户消息类型: text/image/file
|
||||
*/
|
||||
private String userMessageType;
|
||||
|
||||
/**
|
||||
* AI回复的消息内容
|
||||
*/
|
||||
private String aiReply;
|
||||
|
||||
/**
|
||||
* AI回复类型: text/image/file
|
||||
*/
|
||||
private String aiReplyType;
|
||||
|
||||
/**
|
||||
* HTTP状态码
|
||||
*/
|
||||
private Integer responseStatus;
|
||||
|
||||
/**
|
||||
* 响应体
|
||||
*/
|
||||
private String responseBody;
|
||||
|
||||
/**
|
||||
* 响应头
|
||||
*/
|
||||
private String responseHeaders;
|
||||
|
||||
/**
|
||||
* 轮询次数
|
||||
*/
|
||||
private Integer pollCount;
|
||||
|
||||
/**
|
||||
* 轮询开始时间
|
||||
*/
|
||||
private String pollStartTime;
|
||||
|
||||
/**
|
||||
* 轮询结束时间
|
||||
*/
|
||||
private String pollEndTime;
|
||||
|
||||
/**
|
||||
* 最终状态: completed/failed/timeout
|
||||
*/
|
||||
private String finalStatus;
|
||||
|
||||
/**
|
||||
* 调用状态: pending/success/failed/timeout
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 耗时(毫秒)
|
||||
*/
|
||||
private Integer durationMs;
|
||||
|
||||
/**
|
||||
* 输入Token数
|
||||
*/
|
||||
private Integer promptTokens;
|
||||
|
||||
/**
|
||||
* 输出Token数
|
||||
*/
|
||||
private Integer completionTokens;
|
||||
|
||||
/**
|
||||
* 总Token数
|
||||
*/
|
||||
private Integer totalTokens;
|
||||
|
||||
/**
|
||||
* 费用
|
||||
*/
|
||||
private BigDecimal cost;
|
||||
|
||||
/**
|
||||
* 函数调用记录
|
||||
*/
|
||||
private String functionCalls;
|
||||
|
||||
/**
|
||||
* 函数调用结果
|
||||
*/
|
||||
private String functionResults;
|
||||
|
||||
/**
|
||||
* 错误代码
|
||||
*/
|
||||
private String errorCode;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
private String clientIp;
|
||||
|
||||
/**
|
||||
* 用户代理
|
||||
*/
|
||||
private String userAgent;
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String sessionId;
|
||||
|
||||
/**
|
||||
* 追踪ID
|
||||
*/
|
||||
private String traceId;
|
||||
|
||||
/**
|
||||
* 扩展元数据
|
||||
*/
|
||||
private String metadata;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.emotion.dto.request.coze;
|
||||
|
||||
import com.emotion.dto.request.PageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* Coze API调用记录分页请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CozeApiCallPageRequest extends PageRequest {
|
||||
|
||||
/**
|
||||
* 会话ID(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "会话ID格式不正确")
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "用户ID格式不正确")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* Bot ID(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "Bot ID格式不正确")
|
||||
private String botId;
|
||||
|
||||
/**
|
||||
* 状态(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\u4e00-\\u9fa5]{1,20}$", message = "状态格式不正确")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 请求类型(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,20}$", message = "请求类型格式不正确")
|
||||
private String requestType;
|
||||
|
||||
/**
|
||||
* 追踪ID(可选)
|
||||
*/
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "追踪ID格式不正确")
|
||||
private String traceId;
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
package com.emotion.dto.request.coze;
|
||||
|
||||
import com.emotion.dto.request.BaseRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Coze API调用记录更新请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CozeApiCallUpdateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@NotBlank(message = "ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 对话ID
|
||||
*/
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* Coze聊天ID
|
||||
*/
|
||||
private String cozeChatId;
|
||||
|
||||
/**
|
||||
* Coze对话ID
|
||||
*/
|
||||
private String cozeConversationId;
|
||||
|
||||
/**
|
||||
* Bot ID
|
||||
*/
|
||||
private String botId;
|
||||
|
||||
/**
|
||||
* Workflow ID
|
||||
*/
|
||||
private String workflowId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 请求类型: chat/stream/retrieve/messages
|
||||
*/
|
||||
private String requestType;
|
||||
|
||||
/**
|
||||
* 请求URL
|
||||
*/
|
||||
private String requestUrl;
|
||||
|
||||
/**
|
||||
* 请求体
|
||||
*/
|
||||
private String requestBody;
|
||||
|
||||
/**
|
||||
* 请求头
|
||||
*/
|
||||
private String requestHeaders;
|
||||
|
||||
/**
|
||||
* 用户输入的消息内容
|
||||
*/
|
||||
private String userMessage;
|
||||
|
||||
/**
|
||||
* 用户消息类型: text/image/file
|
||||
*/
|
||||
private String userMessageType;
|
||||
|
||||
/**
|
||||
* AI回复的消息内容
|
||||
*/
|
||||
private String aiReply;
|
||||
|
||||
/**
|
||||
* AI回复类型: text/image/file
|
||||
*/
|
||||
private String aiReplyType;
|
||||
|
||||
/**
|
||||
* HTTP状态码
|
||||
*/
|
||||
private Integer responseStatus;
|
||||
|
||||
/**
|
||||
* 响应体
|
||||
*/
|
||||
private String responseBody;
|
||||
|
||||
/**
|
||||
* 响应头
|
||||
*/
|
||||
private String responseHeaders;
|
||||
|
||||
/**
|
||||
* 轮询次数
|
||||
*/
|
||||
private Integer pollCount;
|
||||
|
||||
/**
|
||||
* 轮询开始时间
|
||||
*/
|
||||
private String pollStartTime;
|
||||
|
||||
/**
|
||||
* 轮询结束时间
|
||||
*/
|
||||
private String pollEndTime;
|
||||
|
||||
/**
|
||||
* 最终状态: completed/failed/timeout
|
||||
*/
|
||||
private String finalStatus;
|
||||
|
||||
/**
|
||||
* 调用状态: pending/success/failed/timeout
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 耗时(毫秒)
|
||||
*/
|
||||
private Integer durationMs;
|
||||
|
||||
/**
|
||||
* 输入Token数
|
||||
*/
|
||||
private Integer promptTokens;
|
||||
|
||||
/**
|
||||
* 输出Token数
|
||||
*/
|
||||
private Integer completionTokens;
|
||||
|
||||
/**
|
||||
* 总Token数
|
||||
*/
|
||||
private Integer totalTokens;
|
||||
|
||||
/**
|
||||
* 费用
|
||||
*/
|
||||
private BigDecimal cost;
|
||||
|
||||
/**
|
||||
* 函数调用记录
|
||||
*/
|
||||
private String functionCalls;
|
||||
|
||||
/**
|
||||
* 函数调用结果
|
||||
*/
|
||||
private String functionResults;
|
||||
|
||||
/**
|
||||
* 错误代码
|
||||
*/
|
||||
private String errorCode;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
private String clientIp;
|
||||
|
||||
/**
|
||||
* 用户代理
|
||||
*/
|
||||
private String userAgent;
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String sessionId;
|
||||
|
||||
/**
|
||||
* 追踪ID
|
||||
*/
|
||||
private String traceId;
|
||||
|
||||
/**
|
||||
* 扩展元数据
|
||||
*/
|
||||
private String metadata;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.emotion.dto.response;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 情绪分析响应类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EmotionAnalysisResponse extends BaseResponse {
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 主要情绪
|
||||
*/
|
||||
private String primaryEmotion;
|
||||
|
||||
/**
|
||||
* 情绪极性
|
||||
*/
|
||||
private String polarity;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
private Double intensity;
|
||||
|
||||
/**
|
||||
* 置信度
|
||||
*/
|
||||
private Double confidence;
|
||||
|
||||
/**
|
||||
* 情绪详情
|
||||
*/
|
||||
private String emotionDetails;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.emotion.dto.response;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 情绪记录响应类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EmotionRecordResponse extends BaseResponse {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 记录日期
|
||||
*/
|
||||
private LocalDate recordDate;
|
||||
|
||||
/**
|
||||
* 情绪类型
|
||||
*/
|
||||
private String emotionType;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
private BigDecimal intensity;
|
||||
|
||||
/**
|
||||
* 触发因素
|
||||
*/
|
||||
private String triggers;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private List<String> tags;
|
||||
|
||||
/**
|
||||
* 天气
|
||||
*/
|
||||
private String weather;
|
||||
|
||||
/**
|
||||
* 地点
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 活动
|
||||
*/
|
||||
private String activity;
|
||||
|
||||
/**
|
||||
* 相关人物
|
||||
*/
|
||||
private String people;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String notes;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.emotion.dto.response;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* WebSocket响应对象
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WebSocketResponse extends BaseResponse {
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 发送者ID
|
||||
*/
|
||||
private String senderId;
|
||||
|
||||
/**
|
||||
* 发送者类型
|
||||
*/
|
||||
private String senderType;
|
||||
|
||||
/**
|
||||
* 消息状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 扩展数据
|
||||
*/
|
||||
private Object data;
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.emotion.dto.response.achievement;
|
||||
|
||||
import com.emotion.dto.response.BaseResponse;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 成就响应类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AchievementResponse extends BaseResponse {
|
||||
|
||||
/**
|
||||
* 成就标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 分类
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
|
||||
/**
|
||||
* 稀有度
|
||||
*/
|
||||
private String rarity;
|
||||
|
||||
/**
|
||||
* 条件类型
|
||||
*/
|
||||
private String conditionType;
|
||||
|
||||
/**
|
||||
* 条件值
|
||||
*/
|
||||
private String conditionValue;
|
||||
|
||||
/**
|
||||
* 奖励
|
||||
*/
|
||||
private String rewards;
|
||||
|
||||
/**
|
||||
* 解锁时间
|
||||
*/
|
||||
private String unlockedTime;
|
||||
|
||||
/**
|
||||
* 进度
|
||||
*/
|
||||
private Double progress;
|
||||
|
||||
/**
|
||||
* 是否隐藏
|
||||
*/
|
||||
private Integer isHidden;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.emotion.dto.response.comment;
|
||||
|
||||
import com.emotion.dto.response.BaseResponse;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 评论响应
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommentResponse extends BaseResponse {
|
||||
|
||||
/**
|
||||
* 帖子ID
|
||||
*/
|
||||
private String postId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 回复的评论ID
|
||||
*/
|
||||
private String replyToId;
|
||||
|
||||
/**
|
||||
* 点赞数
|
||||
*/
|
||||
private Integer likes;
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.emotion.dto.response.community;
|
||||
|
||||
import com.emotion.dto.response.BaseResponse;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 社区帖子响应
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommunityPostResponse extends BaseResponse {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 地点ID
|
||||
*/
|
||||
private String locationId;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 帖子类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 图片列表
|
||||
*/
|
||||
private String images;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private String tags;
|
||||
|
||||
/**
|
||||
* 点赞数
|
||||
*/
|
||||
private Integer likes;
|
||||
|
||||
/**
|
||||
* 浏览数
|
||||
*/
|
||||
private Integer viewCount;
|
||||
|
||||
/**
|
||||
* 评论数
|
||||
*/
|
||||
private Integer commentCount;
|
||||
|
||||
/**
|
||||
* 是否私密
|
||||
*/
|
||||
private Integer isPrivate;
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package com.emotion.dto.response.coze;
|
||||
|
||||
import com.emotion.dto.response.BaseResponse;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Coze API调用记录响应
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CozeApiCallResponse extends BaseResponse {
|
||||
|
||||
/**
|
||||
* 对话ID
|
||||
*/
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* Coze聊天ID
|
||||
*/
|
||||
private String cozeChatId;
|
||||
|
||||
/**
|
||||
* Coze对话ID
|
||||
*/
|
||||
private String cozeConversationId;
|
||||
|
||||
/**
|
||||
* Bot ID
|
||||
*/
|
||||
private String botId;
|
||||
|
||||
/**
|
||||
* Workflow ID
|
||||
*/
|
||||
private String workflowId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 请求类型: chat/stream/retrieve/messages
|
||||
*/
|
||||
private String requestType;
|
||||
|
||||
/**
|
||||
* 请求URL
|
||||
*/
|
||||
private String requestUrl;
|
||||
|
||||
/**
|
||||
* 请求体
|
||||
*/
|
||||
private String requestBody;
|
||||
|
||||
/**
|
||||
* 请求头
|
||||
*/
|
||||
private String requestHeaders;
|
||||
|
||||
/**
|
||||
* 用户输入的消息内容
|
||||
*/
|
||||
private String userMessage;
|
||||
|
||||
/**
|
||||
* 用户消息类型: text/image/file
|
||||
*/
|
||||
private String userMessageType;
|
||||
|
||||
/**
|
||||
* AI回复的消息内容
|
||||
*/
|
||||
private String aiReply;
|
||||
|
||||
/**
|
||||
* AI回复类型: text/image/file
|
||||
*/
|
||||
private String aiReplyType;
|
||||
|
||||
/**
|
||||
* HTTP状态码
|
||||
*/
|
||||
private Integer responseStatus;
|
||||
|
||||
/**
|
||||
* 响应体
|
||||
*/
|
||||
private String responseBody;
|
||||
|
||||
/**
|
||||
* 响应头
|
||||
*/
|
||||
private String responseHeaders;
|
||||
|
||||
/**
|
||||
* 轮询次数
|
||||
*/
|
||||
private Integer pollCount;
|
||||
|
||||
/**
|
||||
* 轮询开始时间
|
||||
*/
|
||||
private String pollStartTime;
|
||||
|
||||
/**
|
||||
* 轮询结束时间
|
||||
*/
|
||||
private String pollEndTime;
|
||||
|
||||
/**
|
||||
* 最终状态: completed/failed/timeout
|
||||
*/
|
||||
private String finalStatus;
|
||||
|
||||
/**
|
||||
* 调用状态: pending/success/failed/timeout
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 耗时(毫秒)
|
||||
*/
|
||||
private Integer durationMs;
|
||||
|
||||
/**
|
||||
* 输入Token数
|
||||
*/
|
||||
private Integer promptTokens;
|
||||
|
||||
/**
|
||||
* 输出Token数
|
||||
*/
|
||||
private Integer completionTokens;
|
||||
|
||||
/**
|
||||
* 总Token数
|
||||
*/
|
||||
private Integer totalTokens;
|
||||
|
||||
/**
|
||||
* 费用
|
||||
*/
|
||||
private BigDecimal cost;
|
||||
|
||||
/**
|
||||
* 函数调用记录
|
||||
*/
|
||||
private String functionCalls;
|
||||
|
||||
/**
|
||||
* 函数调用结果
|
||||
*/
|
||||
private String functionResults;
|
||||
|
||||
/**
|
||||
* 错误代码
|
||||
*/
|
||||
private String errorCode;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
private String clientIp;
|
||||
|
||||
/**
|
||||
* 用户代理
|
||||
*/
|
||||
private String userAgent;
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String sessionId;
|
||||
|
||||
/**
|
||||
* 追踪ID
|
||||
*/
|
||||
private String traceId;
|
||||
|
||||
/**
|
||||
* 扩展元数据
|
||||
*/
|
||||
private String metadata;
|
||||
}
|
||||
@@ -35,14 +35,14 @@ public class ChatRequest {
|
||||
/**
|
||||
* 发送者类型
|
||||
*/
|
||||
@NotNull(message = "发送者类型不能为空")
|
||||
private SenderType senderType;
|
||||
@NotBlank(message = "发送者类型不能为空")
|
||||
private String senderType;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@NotNull(message = "消息类型不能为空")
|
||||
private MessageType messageType;
|
||||
@NotBlank(message = "消息类型不能为空")
|
||||
private String messageType;
|
||||
|
||||
/**
|
||||
* 会话ID(可选)
|
||||
@@ -53,45 +53,4 @@ public class ChatRequest {
|
||||
* 发送时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 发送者类型枚举
|
||||
*/
|
||||
public enum SenderType {
|
||||
USER("用户"),
|
||||
GUEST("访客"),
|
||||
AI("AI助手"),
|
||||
SYSTEM("系统");
|
||||
|
||||
private final String description;
|
||||
|
||||
SenderType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息类型枚举
|
||||
*/
|
||||
public enum MessageType {
|
||||
TEXT("文本消息"),
|
||||
IMAGE("图片消息"),
|
||||
FILE("文件消息"),
|
||||
SYSTEM("系统消息"),
|
||||
HEARTBEAT("心跳消息");
|
||||
|
||||
private final String description;
|
||||
|
||||
MessageType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class WebSocketMessage {
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private MessageType type;
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
@@ -47,12 +47,12 @@ public class WebSocketMessage {
|
||||
/**
|
||||
* 发送者类型
|
||||
*/
|
||||
private SenderType senderType;
|
||||
private String senderType;
|
||||
|
||||
/**
|
||||
* 消息状态
|
||||
*/
|
||||
private MessageStatus status;
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
@@ -63,68 +63,4 @@ public class WebSocketMessage {
|
||||
* 扩展数据
|
||||
*/
|
||||
private Object data;
|
||||
|
||||
/**
|
||||
* 消息类型枚举
|
||||
*/
|
||||
public enum MessageType {
|
||||
TEXT("文本消息"),
|
||||
TYPING("正在输入"),
|
||||
SYSTEM("系统消息"),
|
||||
ERROR("错误消息"),
|
||||
HEARTBEAT("心跳消息"),
|
||||
CONNECTION("连接消息"),
|
||||
AI_THINKING("AI思考中");
|
||||
|
||||
private final String description;
|
||||
|
||||
MessageType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送者类型枚举
|
||||
*/
|
||||
public enum SenderType {
|
||||
USER("用户"),
|
||||
GUEST("访客"),
|
||||
AI("AI助手"),
|
||||
SYSTEM("系统");
|
||||
|
||||
private final String description;
|
||||
|
||||
SenderType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息状态枚举
|
||||
*/
|
||||
public enum MessageStatus {
|
||||
SENDING("发送中"),
|
||||
SENT("已发送"),
|
||||
DELIVERED("已送达"),
|
||||
READ("已读"),
|
||||
FAILED("发送失败");
|
||||
|
||||
private final String description;
|
||||
|
||||
MessageStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.emotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.achievement.*;
|
||||
import com.emotion.dto.response.achievement.AchievementResponse;
|
||||
import com.emotion.entity.Achievement;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -12,14 +14,14 @@ import java.util.List;
|
||||
* 成就服务接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-07-23
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
public interface AchievementService extends IService<Achievement> {
|
||||
|
||||
/**
|
||||
* 分页查询成就
|
||||
*/
|
||||
IPage<Achievement> getPage(BasePageRequest request);
|
||||
IPage<Achievement> getPage(AchievementPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据分类查询成就
|
||||
@@ -87,32 +89,32 @@ public interface AchievementService extends IService<Achievement> {
|
||||
Long countByRarity(String rarity);
|
||||
|
||||
/**
|
||||
* 查询平均进度
|
||||
* 获取平均进度
|
||||
*/
|
||||
Double getAvgProgress();
|
||||
|
||||
/**
|
||||
* 查询指定分类的平均进度
|
||||
* 根据分类获取平均进度
|
||||
*/
|
||||
Double getAvgProgressByCategory(String category);
|
||||
|
||||
/**
|
||||
* 查询最近解锁的成就
|
||||
* 获取最近解锁的成就
|
||||
*/
|
||||
List<Achievement> getRecentlyUnlocked(Integer limit);
|
||||
|
||||
/**
|
||||
* 查询即将完成的成就(进度>80%)
|
||||
* 获取接近完成的成就
|
||||
*/
|
||||
List<Achievement> getNearCompletion();
|
||||
|
||||
/**
|
||||
* 查询稀有成就(稀有度为legendary或epic)
|
||||
* 获取稀有成就
|
||||
*/
|
||||
List<Achievement> getRareAchievements();
|
||||
|
||||
/**
|
||||
* 更新成就解锁状态
|
||||
* 解锁成就
|
||||
*/
|
||||
boolean unlockAchievement(String id, LocalDateTime unlockedTime);
|
||||
|
||||
@@ -122,12 +124,59 @@ public interface AchievementService extends IService<Achievement> {
|
||||
boolean updateProgress(String id, Double progress);
|
||||
|
||||
/**
|
||||
* 更新成就隐藏状态
|
||||
* 更新隐藏状态
|
||||
*/
|
||||
boolean updateHiddenStatus(String id, Integer isHidden);
|
||||
|
||||
/**
|
||||
* 查询推荐成就(基于分类和稀有度)
|
||||
* 获取推荐成就
|
||||
*/
|
||||
List<Achievement> getRecommendedAchievements(String category, String rarity, Integer limit);
|
||||
}
|
||||
|
||||
// 新增的Response相关方法
|
||||
|
||||
/**
|
||||
* 分页查询成就响应
|
||||
*/
|
||||
PageResult<AchievementResponse> getPageWithResponse(AchievementPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据ID获取成就响应
|
||||
*/
|
||||
AchievementResponse getAchievementResponseById(String id);
|
||||
|
||||
/**
|
||||
* 创建成就并返回响应
|
||||
*/
|
||||
AchievementResponse createAchievementWithResponse(AchievementCreateRequest request);
|
||||
|
||||
/**
|
||||
* 更新成就并返回响应
|
||||
*/
|
||||
AchievementResponse updateAchievementWithResponse(AchievementUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 根据分类查询成就响应
|
||||
*/
|
||||
List<AchievementResponse> getByCategoryWithResponse(String category);
|
||||
|
||||
/**
|
||||
* 根据稀有度查询成就响应
|
||||
*/
|
||||
List<AchievementResponse> getByRarityWithResponse(String rarity);
|
||||
|
||||
/**
|
||||
* 查询已解锁的成就响应
|
||||
*/
|
||||
List<AchievementResponse> getUnlockedAchievementsWithResponse();
|
||||
|
||||
/**
|
||||
* 查询未解锁的成就响应
|
||||
*/
|
||||
List<AchievementResponse> getLockedAchievementsWithResponse();
|
||||
|
||||
/**
|
||||
* 查询最近解锁的成就响应
|
||||
*/
|
||||
List<AchievementResponse> getRecentlyUnlockedWithResponse(Integer limit);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.emotion.service;
|
||||
|
||||
import com.emotion.dto.request.*;
|
||||
import com.emotion.dto.response.*;
|
||||
|
||||
/**
|
||||
* AI聊天服务接口
|
||||
@@ -10,85 +12,93 @@ package com.emotion.service;
|
||||
public interface AiChatService {
|
||||
|
||||
/**
|
||||
* 发送聊天消息(保存用户消息和AI回复)
|
||||
* @param conversationId 会话ID
|
||||
* @param message 用户消息内容
|
||||
* @param userId 用户ID
|
||||
* @return AI回复内容
|
||||
* 发送聊天消息
|
||||
*
|
||||
* @param request AI聊天请求
|
||||
* @return AI聊天响应
|
||||
*/
|
||||
String sendChatMessage(String conversationId, String message, String userId);
|
||||
AiChatResponse sendChatMessage(AiChatRequest request);
|
||||
|
||||
/**
|
||||
* 生成对话总结
|
||||
*
|
||||
* @param request AI总结请求
|
||||
* @return AI总结响应
|
||||
*/
|
||||
AiSummaryResponse generateConversationSummary(AiSummaryRequest request);
|
||||
|
||||
/**
|
||||
* 获取AI服务状态
|
||||
*
|
||||
* @return AI状态响应
|
||||
*/
|
||||
AiStatusResponse getServiceStatus();
|
||||
|
||||
/**
|
||||
* 获取聊天统计
|
||||
*
|
||||
* @param request 聊天统计请求
|
||||
* @return 聊天统计响应
|
||||
*/
|
||||
ChatStatsResponse getChatStats(ChatStatsRequest request);
|
||||
|
||||
/**
|
||||
* 访客聊天
|
||||
*
|
||||
* @param request 访客聊天请求
|
||||
* @param clientIp 客户端IP
|
||||
* @return 访客聊天响应
|
||||
*/
|
||||
GuestChatResponse guestChat(GuestChatRequest request, String clientIp);
|
||||
|
||||
/**
|
||||
* 获取访客用户信息
|
||||
*
|
||||
* @param clientIp 客户端IP
|
||||
* @return 访客用户信息响应
|
||||
*/
|
||||
GuestUserInfoResponse getGuestUserInfo(String clientIp);
|
||||
|
||||
/**
|
||||
* 创建对话
|
||||
*
|
||||
* @param request 对话创建请求
|
||||
* @param clientIp 客户端IP
|
||||
* @return 对话响应
|
||||
*/
|
||||
ConversationResponse createConversation(ConversationCreateRequest request, String clientIp);
|
||||
|
||||
/**
|
||||
* WebSocket方式发送聊天消息(只保存AI回复)
|
||||
*
|
||||
* @param conversationId 会话ID
|
||||
* @param message 用户消息内容
|
||||
* @param userId 用户ID
|
||||
* @param message 用户消息内容
|
||||
* @param userId 用户ID
|
||||
* @return AI回复内容
|
||||
*/
|
||||
String sendChatMessageForWebSocket(String conversationId, String message, String userId);
|
||||
|
||||
/**
|
||||
* WebSocket方式发送聊天消息(只保存AI回复,带messageId)
|
||||
*
|
||||
* @param conversationId 会话ID
|
||||
* @param messageId 用户消息ID
|
||||
* @param message 用户消息内容
|
||||
* @param userId 用户ID
|
||||
* @param messageId 用户消息ID
|
||||
* @param message 用户消息内容
|
||||
* @param userId 用户ID
|
||||
* @return AI回复内容
|
||||
*/
|
||||
String sendChatMessageForWebSocket(String conversationId, String messageId, String message, String userId);
|
||||
|
||||
/**
|
||||
* 生成对话总结
|
||||
* @param conversationId 会话ID
|
||||
* @param userId 用户ID
|
||||
* @return 总结内容
|
||||
*/
|
||||
String generateConversationSummary(String conversationId, String userId);
|
||||
|
||||
/**
|
||||
* 检查AI服务是否可用
|
||||
* @return 可用返回true,否则false
|
||||
*/
|
||||
boolean isServiceAvailable();
|
||||
|
||||
/**
|
||||
* 获取AI服务状态
|
||||
* @return "available" 或 "unavailable"
|
||||
*/
|
||||
String getServiceStatus();
|
||||
|
||||
/**
|
||||
* 发送消息到Coze AI(不保存消息,仅AI交互)
|
||||
*
|
||||
* @param conversationId 会话ID
|
||||
* @param userMessage 用户消息内容
|
||||
* @param userId 用户ID
|
||||
* @param userMessage 用户消息内容
|
||||
* @param userId 用户ID
|
||||
* @return AI回复内容
|
||||
*/
|
||||
String sendMessage(String conversationId, String userMessage, String userId);
|
||||
|
||||
/**
|
||||
* 访客聊天(不登录情况下)
|
||||
* @param message 用户消息内容
|
||||
* @param clientIp 客户端IP
|
||||
* @return 包含AI回复等信息的Map
|
||||
*/
|
||||
java.util.Map<String, Object> guestChat(String message, String clientIp);
|
||||
|
||||
/**
|
||||
* 创建新对话
|
||||
* @param userId 用户ID
|
||||
* @param title 对话标题
|
||||
* @return 包含对话信息的Map
|
||||
*/
|
||||
java.util.Map<String, Object> createConversation(String userId, String title);
|
||||
|
||||
/**
|
||||
* 获取访客用户信息
|
||||
* @param clientIp 客户端IP
|
||||
* @return 包含访客信息的Map
|
||||
*/
|
||||
java.util.Map<String, Object> getGuestUserInfo(String clientIp);
|
||||
|
||||
/**
|
||||
* 流式聊天(暂时降级为普通聊天)
|
||||
* @param conversationId 会话ID
|
||||
@@ -126,4 +136,4 @@ public interface AiChatService {
|
||||
* @return AI评论内容
|
||||
*/
|
||||
String sendSummaryMessage(String conversationId, String userMessage, String userId);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import com.emotion.dto.response.AuthResponse;
|
||||
import com.emotion.dto.response.CaptchaResponse;
|
||||
import com.emotion.dto.response.UserInfoResponse;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 认证服务接口
|
||||
*
|
||||
@@ -64,12 +66,12 @@ public interface AuthService {
|
||||
boolean logout(String userId, String token);
|
||||
|
||||
/**
|
||||
* 用户登出(通过令牌)
|
||||
* 用户登出(通过请求)
|
||||
*
|
||||
* @param token 访问令牌
|
||||
* @param request HTTP请求
|
||||
* @return 是否登出成功
|
||||
*/
|
||||
boolean logoutByToken(String token);
|
||||
boolean logoutByToken(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
@@ -79,6 +81,14 @@ public interface AuthService {
|
||||
*/
|
||||
AuthResponse refreshToken(String refreshToken);
|
||||
|
||||
/**
|
||||
* 验证访问令牌
|
||||
*
|
||||
* @param request HTTP请求
|
||||
* @return 是否有效
|
||||
*/
|
||||
boolean validateToken(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 验证访问令牌
|
||||
*
|
||||
@@ -126,4 +136,4 @@ public interface AuthService {
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByPhone(String phone);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.emotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.comment.CommentCreateRequest;
|
||||
import com.emotion.dto.request.comment.CommentUpdateRequest;
|
||||
import com.emotion.dto.request.comment.CommentPageRequest;
|
||||
import com.emotion.dto.response.comment.CommentResponse;
|
||||
import com.emotion.entity.Comment;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论服务接口
|
||||
*
|
||||
@@ -19,110 +19,25 @@ public interface CommentService extends IService<Comment> {
|
||||
/**
|
||||
* 分页查询评论
|
||||
*/
|
||||
IPage<Comment> getPage(BasePageRequest request);
|
||||
PageResult<CommentResponse> getPage(CommentPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据帖子ID分页查询评论
|
||||
* 根据ID获取评论响应
|
||||
*/
|
||||
IPage<Comment> getPageByPostId(BasePageRequest request, String postId);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询评论
|
||||
*/
|
||||
IPage<Comment> getPageByUserId(BasePageRequest request, String userId);
|
||||
|
||||
/**
|
||||
* 根据帖子ID查询所有评论
|
||||
*/
|
||||
List<Comment> getByPostId(String postId);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询所有评论
|
||||
*/
|
||||
List<Comment> getByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 根据回复的评论ID查询回复
|
||||
*/
|
||||
List<Comment> getRepliesByCommentId(String replyToId);
|
||||
|
||||
/**
|
||||
* 查询顶级评论(非回复的评论)
|
||||
*/
|
||||
List<Comment> getTopLevelCommentsByPostId(String postId);
|
||||
|
||||
/**
|
||||
* 根据点赞数范围查询评论
|
||||
*/
|
||||
List<Comment> getByLikesRange(Integer minLikes, Integer maxLikes);
|
||||
|
||||
/**
|
||||
* 根据时间范围查询评论
|
||||
*/
|
||||
List<Comment> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 统计帖子的评论数量
|
||||
*/
|
||||
Long countByPostId(String postId);
|
||||
|
||||
/**
|
||||
* 统计用户的评论数量
|
||||
*/
|
||||
Long countByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 统计评论的回复数量
|
||||
*/
|
||||
Long countRepliesByCommentId(String commentId);
|
||||
|
||||
/**
|
||||
* 统计帖子的顶级评论数量
|
||||
*/
|
||||
Long countTopLevelCommentsByPostId(String postId);
|
||||
|
||||
/**
|
||||
* 查询最受欢迎的评论(按点赞数排序)
|
||||
*/
|
||||
List<Comment> getMostLikedCommentsByPostId(String postId, Integer limit);
|
||||
|
||||
/**
|
||||
* 查询最新的评论
|
||||
*/
|
||||
List<Comment> getLatestCommentsByPostId(String postId, Integer limit);
|
||||
|
||||
/**
|
||||
* 查询用户最近的评论
|
||||
*/
|
||||
List<Comment> getRecentByUserId(String userId, Integer limit);
|
||||
|
||||
/**
|
||||
* 查询热门评论(按点赞数和回复数综合排序)
|
||||
*/
|
||||
List<Comment> getPopularCommentsByPostId(String postId, Integer limit);
|
||||
|
||||
/**
|
||||
* 根据关键词搜索评论内容
|
||||
*/
|
||||
List<Comment> searchByKeyword(String keyword);
|
||||
|
||||
/**
|
||||
* 根据帖子ID和关键词搜索评论
|
||||
*/
|
||||
List<Comment> searchByPostIdAndKeyword(String postId, String keyword);
|
||||
|
||||
/**
|
||||
* 查询用户在指定帖子下的评论
|
||||
*/
|
||||
List<Comment> getByPostIdAndUserId(String postId, String userId);
|
||||
|
||||
/**
|
||||
* 更新评论点赞数
|
||||
*/
|
||||
boolean updateLikes(String id, Integer increment);
|
||||
|
||||
CommentResponse getById(String id);
|
||||
|
||||
/**
|
||||
* 创建评论
|
||||
*/
|
||||
Comment createComment(String postId, String userId, String content, String replyToId);
|
||||
}
|
||||
CommentResponse create(CommentCreateRequest request);
|
||||
|
||||
/**
|
||||
* 更新评论
|
||||
*/
|
||||
CommentResponse update(CommentUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*/
|
||||
boolean delete(String id);
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.emotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.community.CommunityPostCreateRequest;
|
||||
import com.emotion.dto.request.community.CommunityPostUpdateRequest;
|
||||
import com.emotion.dto.request.community.CommunityPostPageRequest;
|
||||
import com.emotion.dto.response.community.CommunityPostResponse;
|
||||
import com.emotion.entity.CommunityPost;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社区帖子服务接口
|
||||
*
|
||||
@@ -19,141 +19,25 @@ public interface CommunityPostService extends IService<CommunityPost> {
|
||||
/**
|
||||
* 分页查询帖子
|
||||
*/
|
||||
IPage<CommunityPost> getPage(BasePageRequest request);
|
||||
PageResult<CommunityPostResponse> getPage(CommunityPostPageRequest request);
|
||||
|
||||
/**
|
||||
* 分页查询公开帖子
|
||||
* 根据ID获取帖子响应
|
||||
*/
|
||||
IPage<CommunityPost> getPublicPostsPage(BasePageRequest request);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询帖子
|
||||
*/
|
||||
IPage<CommunityPost> getPageByUserId(BasePageRequest request, String userId);
|
||||
|
||||
/**
|
||||
* 根据地点ID查询帖子
|
||||
*/
|
||||
List<CommunityPost> getByLocationId(String locationId);
|
||||
|
||||
/**
|
||||
* 根据帖子类型查询帖子
|
||||
*/
|
||||
List<CommunityPost> getByType(String type);
|
||||
|
||||
/**
|
||||
* 查询用户的私密帖子
|
||||
*/
|
||||
List<CommunityPost> getPrivatePostsByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 根据点赞数范围查询帖子
|
||||
*/
|
||||
List<CommunityPost> getByLikesRange(Integer minLikes, Integer maxLikes);
|
||||
|
||||
/**
|
||||
* 根据浏览数范围查询帖子
|
||||
*/
|
||||
List<CommunityPost> getByViewRange(Integer minViews, Integer maxViews);
|
||||
|
||||
/**
|
||||
* 根据评论数范围查询帖子
|
||||
*/
|
||||
List<CommunityPost> getByCommentRange(Integer minComments, Integer maxComments);
|
||||
|
||||
/**
|
||||
* 根据时间范围查询帖子
|
||||
*/
|
||||
List<CommunityPost> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 统计用户的帖子数量
|
||||
*/
|
||||
Long countByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 统计用户的公开帖子数量
|
||||
*/
|
||||
Long countPublicPostsByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 统计用户的私密帖子数量
|
||||
*/
|
||||
Long countPrivatePostsByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 统计指定类型的帖子数量
|
||||
*/
|
||||
Long countByType(String type);
|
||||
|
||||
/**
|
||||
* 统计地点的帖子数量
|
||||
*/
|
||||
Long countByLocationId(String locationId);
|
||||
|
||||
/**
|
||||
* 查询最受欢迎的帖子(按点赞数排序)
|
||||
*/
|
||||
List<CommunityPost> getMostLikedPosts(Integer limit);
|
||||
|
||||
/**
|
||||
* 查询最热门的帖子(按浏览数排序)
|
||||
*/
|
||||
List<CommunityPost> getMostViewedPosts(Integer limit);
|
||||
|
||||
/**
|
||||
* 查询最新的帖子
|
||||
*/
|
||||
List<CommunityPost> getLatestPosts(Integer limit);
|
||||
|
||||
/**
|
||||
* 查询热门帖子(综合点赞、浏览、评论)
|
||||
*/
|
||||
List<CommunityPost> getPopularPosts(Integer limit);
|
||||
|
||||
/**
|
||||
* 根据标签搜索帖子
|
||||
*/
|
||||
List<CommunityPost> getByTag(String tag);
|
||||
|
||||
/**
|
||||
* 根据关键词搜索帖子
|
||||
*/
|
||||
List<CommunityPost> searchByKeyword(String keyword);
|
||||
|
||||
/**
|
||||
* 查询用户最近的帖子
|
||||
*/
|
||||
List<CommunityPost> getRecentByUserId(String userId, Integer limit);
|
||||
|
||||
/**
|
||||
* 更新帖子点赞数
|
||||
*/
|
||||
boolean updateLikes(String id, Integer increment);
|
||||
|
||||
/**
|
||||
* 更新帖子浏览数
|
||||
*/
|
||||
boolean incrementViewCount(String id);
|
||||
|
||||
/**
|
||||
* 更新帖子评论数
|
||||
*/
|
||||
boolean updateCommentCount(String id, Integer increment);
|
||||
|
||||
/**
|
||||
* 更新帖子隐私状态
|
||||
*/
|
||||
boolean updatePrivacyStatus(String id, Integer isPrivate);
|
||||
|
||||
/**
|
||||
* 查询推荐帖子(基于类型和地点)
|
||||
*/
|
||||
List<CommunityPost> getRecommendedPosts(String type, String locationId, Integer limit);
|
||||
|
||||
CommunityPostResponse getById(String id);
|
||||
|
||||
/**
|
||||
* 创建帖子
|
||||
*/
|
||||
CommunityPost createPost(String userId, String title, String content, String type,
|
||||
String locationId, String tags, Integer isPrivate);
|
||||
}
|
||||
CommunityPostResponse create(CommunityPostCreateRequest request);
|
||||
|
||||
/**
|
||||
* 更新帖子
|
||||
*/
|
||||
CommunityPostResponse update(CommunityPostUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 删除帖子
|
||||
*/
|
||||
boolean delete(String id);
|
||||
}
|
||||
@@ -2,9 +2,13 @@ package com.emotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.ConversationCreateRequest;
|
||||
import com.emotion.dto.request.ConversationPageRequest;
|
||||
import com.emotion.dto.response.ConversationResponse;
|
||||
import com.emotion.entity.Conversation;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -19,12 +23,12 @@ public interface ConversationService extends IService<Conversation> {
|
||||
/**
|
||||
* 分页查询会话
|
||||
*/
|
||||
IPage<Conversation> getPage(BasePageRequest request);
|
||||
IPage<Conversation> getPage(ConversationPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询会话
|
||||
*/
|
||||
IPage<Conversation> getPageByUserId(BasePageRequest request, String userId);
|
||||
IPage<Conversation> getPageByUserId(ConversationPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询会话列表
|
||||
@@ -85,4 +89,60 @@ public interface ConversationService extends IService<Conversation> {
|
||||
* 结束会话
|
||||
*/
|
||||
boolean endConversation(String conversationId);
|
||||
|
||||
/**
|
||||
* 分页查询会话响应
|
||||
*/
|
||||
PageResult<ConversationResponse> getPageWithResponse(ConversationPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询会话响应
|
||||
*/
|
||||
PageResult<ConversationResponse> getPageByUserIdWithResponse(ConversationPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据ID获取会话响应
|
||||
*/
|
||||
ConversationResponse getConversationResponseById(String id);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询会话响应列表
|
||||
*/
|
||||
List<ConversationResponse> getByUserIdWithResponse(String userId);
|
||||
|
||||
/**
|
||||
* 获取活跃会话响应列表
|
||||
*/
|
||||
List<ConversationResponse> getActiveConversationsWithResponse();
|
||||
|
||||
/**
|
||||
* 获取归档会话响应列表
|
||||
*/
|
||||
List<ConversationResponse> getArchivedConversationsWithResponse();
|
||||
|
||||
/**
|
||||
* 归档对话
|
||||
*/
|
||||
boolean archiveConversation(String id);
|
||||
|
||||
/**
|
||||
* 激活对话
|
||||
*/
|
||||
boolean activateConversation(String id);
|
||||
|
||||
/**
|
||||
* 创建会话并返回响应对象
|
||||
*/
|
||||
ConversationResponse createConversationWithResponse(ConversationCreateRequest request,
|
||||
HttpServletRequest httpRequest);
|
||||
|
||||
/**
|
||||
* 更新会话并返回响应对象
|
||||
*/
|
||||
ConversationResponse updateConversationWithResponse(ConversationCreateRequest request);
|
||||
|
||||
/**
|
||||
* 更新会话状态
|
||||
*/
|
||||
boolean updateConversationStatus(String id, String status);
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.emotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.coze.CozeApiCallPageRequest;
|
||||
import com.emotion.dto.request.coze.CozeApiCallCreateRequest;
|
||||
import com.emotion.dto.request.coze.CozeApiCallUpdateRequest;
|
||||
import com.emotion.dto.response.coze.CozeApiCallResponse;
|
||||
import com.emotion.entity.CozeApiCall;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -20,37 +23,27 @@ public interface CozeApiCallService extends IService<CozeApiCall> {
|
||||
/**
|
||||
* 分页查询API调用记录
|
||||
*/
|
||||
IPage<CozeApiCall> getPage(BasePageRequest request);
|
||||
PageResult<CozeApiCallResponse> getPage(CozeApiCallPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据会话ID分页查询API调用记录
|
||||
* 根据ID获取API调用记录
|
||||
*/
|
||||
IPage<CozeApiCall> getPageByConversationId(BasePageRequest request, String conversationId);
|
||||
|
||||
CozeApiCallResponse getById(String id);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询API调用记录
|
||||
* 创建API调用记录
|
||||
*/
|
||||
IPage<CozeApiCall> getPageByUserId(BasePageRequest request, String userId);
|
||||
|
||||
CozeApiCallResponse create(CozeApiCallCreateRequest request);
|
||||
|
||||
/**
|
||||
* 根据Bot ID查询API调用记录
|
||||
* 更新API调用记录
|
||||
*/
|
||||
List<CozeApiCall> getByBotId(String botId);
|
||||
|
||||
CozeApiCallResponse update(CozeApiCallUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 根据状态查询API调用记录
|
||||
* 删除API调用记录
|
||||
*/
|
||||
List<CozeApiCall> getByStatus(String status);
|
||||
|
||||
/**
|
||||
* 根据请求类型查询API调用记录
|
||||
*/
|
||||
List<CozeApiCall> getByRequestType(String requestType);
|
||||
|
||||
/**
|
||||
* 根据时间范围查询API调用记录
|
||||
*/
|
||||
List<CozeApiCall> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime);
|
||||
boolean delete(String id);
|
||||
|
||||
/**
|
||||
* 统计用户的API调用次数
|
||||
@@ -76,42 +69,4 @@ public interface CozeApiCallService extends IService<CozeApiCall> {
|
||||
* 统计用户的API调用费用
|
||||
*/
|
||||
BigDecimal sumCostByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 查询失败的API调用记录
|
||||
*/
|
||||
List<CozeApiCall> getFailedCalls();
|
||||
|
||||
/**
|
||||
* 查询超时的API调用记录
|
||||
*/
|
||||
List<CozeApiCall> getTimeoutCalls();
|
||||
|
||||
/**
|
||||
* 根据追踪ID查询API调用记录
|
||||
*/
|
||||
CozeApiCall getByTraceId(String traceId);
|
||||
|
||||
/**
|
||||
* 根据会话ID和请求类型查询API调用记录
|
||||
*/
|
||||
List<CozeApiCall> getByConversationIdAndRequestType(String conversationId, String requestType);
|
||||
|
||||
/**
|
||||
* 更新API调用状态
|
||||
*/
|
||||
boolean updateStatus(String id, String status, String finalStatus, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 更新API调用结果
|
||||
*/
|
||||
boolean updateResult(String id, Integer responseStatus, String responseBody, String aiReply,
|
||||
Integer totalTokens, BigDecimal cost, String status, String finalStatus,
|
||||
LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 创建API调用记录
|
||||
*/
|
||||
CozeApiCall createApiCall(String conversationId, String messageId, String userId,
|
||||
String requestType, String requestUrl, String requestBody);
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,12 @@ package com.emotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.DiaryCommentCreateRequest;
|
||||
import com.emotion.dto.request.DiaryCommentPageRequest;
|
||||
import com.emotion.dto.response.DiaryCommentResponse;
|
||||
import com.emotion.entity.DiaryComment;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -19,100 +21,28 @@ public interface DiaryCommentService extends IService<DiaryComment> {
|
||||
/**
|
||||
* 分页查询评论
|
||||
*/
|
||||
IPage<DiaryComment> getPage(BasePageRequest request);
|
||||
|
||||
PageResult<DiaryCommentResponse> getPageWithResponse(DiaryCommentPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据日记ID分页查询评论
|
||||
* 根据ID获取评论响应
|
||||
*/
|
||||
IPage<DiaryComment> getPageByDiaryId(String diaryId, BasePageRequest request);
|
||||
|
||||
DiaryCommentResponse getCommentResponseById(String id);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询评论
|
||||
* 获取评论树结构响应
|
||||
*/
|
||||
IPage<DiaryComment> getPageByUserId(String userId, BasePageRequest request);
|
||||
|
||||
List<DiaryCommentResponse> getCommentTreeWithResponse(String diaryId);
|
||||
|
||||
/**
|
||||
* 根据父评论ID查询回复列表
|
||||
* 创建评论并返回响应对象
|
||||
*/
|
||||
List<DiaryComment> getRepliesByParentId(String parentCommentId);
|
||||
|
||||
DiaryCommentResponse createCommentWithResponse(DiaryCommentCreateRequest request);
|
||||
|
||||
/**
|
||||
* 根据评论类型查询评论列表
|
||||
* 更新评论并返回响应对象
|
||||
*/
|
||||
List<DiaryComment> getByCommentType(String commentType);
|
||||
|
||||
/**
|
||||
* 根据日记ID和评论类型查询评论列表
|
||||
*/
|
||||
List<DiaryComment> getByDiaryIdAndCommentType(String diaryId, String commentType);
|
||||
|
||||
/**
|
||||
* 增加点赞数
|
||||
*/
|
||||
boolean incrementLikeCount(String commentId);
|
||||
|
||||
/**
|
||||
* 减少点赞数
|
||||
*/
|
||||
boolean decrementLikeCount(String commentId);
|
||||
|
||||
/**
|
||||
* 增加回复数
|
||||
*/
|
||||
boolean incrementReplyCount(String commentId);
|
||||
|
||||
/**
|
||||
* 减少回复数
|
||||
*/
|
||||
boolean decrementReplyCount(String commentId);
|
||||
|
||||
/**
|
||||
* 更新最后回复时间
|
||||
*/
|
||||
boolean updateLastReplyTime(String commentId);
|
||||
|
||||
/**
|
||||
* 设置置顶状态
|
||||
*/
|
||||
boolean setTop(String commentId, Integer isTop);
|
||||
|
||||
/**
|
||||
* 统计日记评论数量
|
||||
*/
|
||||
Long countByDiaryId(String diaryId);
|
||||
|
||||
/**
|
||||
* 统计用户评论数量
|
||||
*/
|
||||
Long countByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 统计指定类型的评论数量
|
||||
*/
|
||||
Long countByCommentType(String commentType);
|
||||
|
||||
/**
|
||||
* 统计回复数量
|
||||
*/
|
||||
Long countReplies(String parentCommentId);
|
||||
|
||||
/**
|
||||
* 创建评论
|
||||
*/
|
||||
DiaryComment createComment(String diaryId, String userId, String content, List<String> images,
|
||||
String parentCommentId, Integer isAnonymous);
|
||||
|
||||
/**
|
||||
* 创建AI评论
|
||||
*/
|
||||
DiaryComment createAiComment(String diaryId, String content, String aiCommentSource,
|
||||
BigDecimal emotionScore, BigDecimal sentimentScore);
|
||||
|
||||
/**
|
||||
* 更新评论
|
||||
*/
|
||||
boolean updateComment(String commentId, String content, List<String> images);
|
||||
|
||||
DiaryCommentResponse updateCommentWithResponse(DiaryCommentCreateRequest request);
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*/
|
||||
@@ -129,7 +59,17 @@ public interface DiaryCommentService extends IService<DiaryComment> {
|
||||
boolean restoreComment(String commentId);
|
||||
|
||||
/**
|
||||
* 获取评论树结构
|
||||
* 增加点赞数
|
||||
*/
|
||||
List<DiaryComment> getCommentTree(String diaryId);
|
||||
}
|
||||
boolean incrementLikeCount(String commentId);
|
||||
|
||||
/**
|
||||
* 减少点赞数
|
||||
*/
|
||||
boolean decrementLikeCount(String commentId);
|
||||
|
||||
/**
|
||||
* 设置置顶状态
|
||||
*/
|
||||
boolean setTop(String commentId, Integer isTop);
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.emotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.DiaryPostCreateRequest;
|
||||
import com.emotion.dto.request.DiaryPostPageRequest;
|
||||
import com.emotion.dto.request.DiaryPostUpdateRequest;
|
||||
import com.emotion.dto.response.DiaryPostResponse;
|
||||
import com.emotion.entity.DiaryPost;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -19,47 +22,37 @@ public interface DiaryPostService extends IService<DiaryPost> {
|
||||
/**
|
||||
* 分页查询日记
|
||||
*/
|
||||
IPage<DiaryPost> getPage(BasePageRequest request);
|
||||
PageResult<DiaryPostResponse> getPageWithResponse(DiaryPostPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据ID获取日记响应
|
||||
*/
|
||||
DiaryPostResponse getDiaryPostResponseById(String id);
|
||||
|
||||
/**
|
||||
* 创建日记并返回响应对象
|
||||
*/
|
||||
DiaryPostResponse createDiaryPostWithResponse(DiaryPostCreateRequest request);
|
||||
|
||||
/**
|
||||
* 更新日记并返回响应对象
|
||||
*/
|
||||
DiaryPostResponse updateDiaryPostWithResponse(DiaryPostUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 删除日记
|
||||
*/
|
||||
boolean deleteDiaryPost(String diaryId);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询日记
|
||||
* 软删除日记
|
||||
*/
|
||||
IPage<DiaryPost> getPageByUserId(String userId, BasePageRequest request);
|
||||
boolean softDeleteDiaryPost(String diaryId);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询公开日记
|
||||
* 恢复日记
|
||||
*/
|
||||
IPage<DiaryPost> getPublicPageByUserId(String userId, BasePageRequest request);
|
||||
|
||||
/**
|
||||
* 查询精选日记
|
||||
*/
|
||||
IPage<DiaryPost> getFeaturedPage(BasePageRequest request);
|
||||
|
||||
/**
|
||||
* 根据状态查询日记列表
|
||||
*/
|
||||
List<DiaryPost> getByStatus(String status);
|
||||
|
||||
/**
|
||||
* 根据用户ID和状态查询日记列表
|
||||
*/
|
||||
List<DiaryPost> getByUserIdAndStatus(String userId, String status);
|
||||
|
||||
/**
|
||||
* 根据心情状态查询日记列表
|
||||
*/
|
||||
List<DiaryPost> getByMood(String mood);
|
||||
|
||||
/**
|
||||
* 根据标签查询日记列表
|
||||
*/
|
||||
List<DiaryPost> getByTags(List<String> tags);
|
||||
|
||||
/**
|
||||
* 根据地点查询日记列表
|
||||
*/
|
||||
List<DiaryPost> getByLocation(String location);
|
||||
boolean restoreDiaryPost(String diaryId);
|
||||
|
||||
/**
|
||||
* 增加浏览数
|
||||
@@ -120,47 +113,9 @@ public interface DiaryPostService extends IService<DiaryPost> {
|
||||
* 统计精选日记数量
|
||||
*/
|
||||
Long countFeatured();
|
||||
|
||||
/**
|
||||
* 统计指定状态的日记数量
|
||||
*/
|
||||
Long countByStatus(String status);
|
||||
|
||||
/**
|
||||
* 创建日记
|
||||
*/
|
||||
DiaryPost createDiaryPost(com.emotion.dto.request.DiaryPostCreateRequest request);
|
||||
|
||||
/**
|
||||
* 更新日记
|
||||
*/
|
||||
boolean updateDiaryPost(String diaryId, com.emotion.dto.request.DiaryPostUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 删除日记
|
||||
*/
|
||||
boolean deleteDiaryPost(String diaryId);
|
||||
|
||||
/**
|
||||
* 软删除日记
|
||||
*/
|
||||
boolean softDeleteDiaryPost(String diaryId);
|
||||
|
||||
/**
|
||||
* 恢复日记
|
||||
*/
|
||||
boolean restoreDiaryPost(String diaryId);
|
||||
|
||||
/**
|
||||
* 添加AI评论
|
||||
*/
|
||||
boolean addAiComment(String diaryId, String aiComment, Object aiEmotionAnalysis,
|
||||
BigDecimal aiSentimentScore, List<String> aiKeywords, String aiSuggestions);
|
||||
|
||||
|
||||
/**
|
||||
* 发表日记并生成AI评论
|
||||
* @param request 日记创建请求
|
||||
* @return 日记响应对象,包含AI评论
|
||||
*/
|
||||
com.emotion.dto.response.DiaryPostResponse publishDiaryWithAiComment(com.emotion.dto.request.DiaryPostCreateRequest request);
|
||||
}
|
||||
DiaryPostResponse publishDiaryWithAiComment(DiaryPostCreateRequest request);
|
||||
}
|
||||
@@ -3,6 +3,11 @@ package com.emotion.service;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.EmotionAnalysisCreateRequest;
|
||||
import com.emotion.dto.request.EmotionAnalysisPageRequest;
|
||||
import com.emotion.dto.request.EmotionAnalysisUpdateRequest;
|
||||
import com.emotion.dto.response.EmotionAnalysisResponse;
|
||||
import com.emotion.entity.EmotionAnalysis;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -96,4 +101,67 @@ public interface EmotionAnalysisService extends IService<EmotionAnalysis> {
|
||||
*/
|
||||
EmotionAnalysis createEmotionAnalysis(String messageId, String userId, String primaryEmotion,
|
||||
String polarity, Double intensity, Double confidence);
|
||||
}
|
||||
|
||||
// 新增的方法
|
||||
|
||||
/**
|
||||
* 分页查询情绪分析记录响应
|
||||
*/
|
||||
PageResult<EmotionAnalysisResponse> getPageWithResponse(EmotionAnalysisPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询情绪分析记录响应
|
||||
*/
|
||||
PageResult<EmotionAnalysisResponse> getPageByUserIdWithResponse(String userId, EmotionAnalysisPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据ID获取情绪分析记录响应
|
||||
*/
|
||||
EmotionAnalysisResponse getEmotionAnalysisResponseById(String id);
|
||||
|
||||
/**
|
||||
* 根据消息ID获取情绪分析记录响应
|
||||
*/
|
||||
EmotionAnalysisResponse getEmotionAnalysisResponseByMessageId(String messageId);
|
||||
|
||||
/**
|
||||
* 创建情绪分析记录并返回响应
|
||||
*/
|
||||
EmotionAnalysisResponse createEmotionAnalysisWithResponse(EmotionAnalysisCreateRequest request);
|
||||
|
||||
/**
|
||||
* 更新情绪分析记录并返回响应
|
||||
*/
|
||||
EmotionAnalysisResponse updateEmotionAnalysisWithResponse(EmotionAnalysisUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 删除情绪分析记录
|
||||
*/
|
||||
boolean deleteEmotionAnalysis(String id);
|
||||
|
||||
/**
|
||||
* 根据主要情绪查询分析记录响应
|
||||
*/
|
||||
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByPrimaryEmotion(String primaryEmotion);
|
||||
|
||||
/**
|
||||
* 根据情绪极性查询分析记录响应
|
||||
*/
|
||||
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByPolarity(String polarity);
|
||||
|
||||
/**
|
||||
* 根据用户ID和情绪类型查询分析记录响应
|
||||
*/
|
||||
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByUserIdAndEmotion(String userId, String primaryEmotion);
|
||||
|
||||
/**
|
||||
* 根据时间范围查询用户情绪分析记录响应
|
||||
*/
|
||||
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByUserIdAndTimeRange(String userId,
|
||||
LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 查询用户最近的情绪分析记录响应
|
||||
*/
|
||||
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesRecentByUserId(String userId, Integer limit);
|
||||
}
|
||||
@@ -3,10 +3,16 @@ package com.emotion.service;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.EmotionRecordCreateRequest;
|
||||
import com.emotion.dto.request.EmotionRecordPageRequest;
|
||||
import com.emotion.dto.request.EmotionRecordUpdateRequest;
|
||||
import com.emotion.dto.response.EmotionRecordResponse;
|
||||
import com.emotion.entity.EmotionRecord;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 情绪记录服务接口
|
||||
@@ -111,4 +117,46 @@ public interface EmotionRecordService extends IService<EmotionRecord> {
|
||||
*/
|
||||
EmotionRecord createEmotionRecord(String userId, String emotionType, Double intensity,
|
||||
String trigger, String location, String notes);
|
||||
}
|
||||
|
||||
// 新增的方法
|
||||
|
||||
/**
|
||||
* 分页查询情绪记录响应
|
||||
*/
|
||||
PageResult<EmotionRecordResponse> getPageWithResponse(EmotionRecordPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询情绪记录响应
|
||||
*/
|
||||
PageResult<EmotionRecordResponse> getPageByUserIdWithResponse(String userId, EmotionRecordPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据ID获取情绪记录响应
|
||||
*/
|
||||
EmotionRecordResponse getEmotionRecordResponseById(String id);
|
||||
|
||||
/**
|
||||
* 创建情绪记录并返回响应
|
||||
*/
|
||||
EmotionRecordResponse createEmotionRecordWithResponse(EmotionRecordCreateRequest request);
|
||||
|
||||
/**
|
||||
* 更新情绪记录并返回响应
|
||||
*/
|
||||
EmotionRecordResponse updateEmotionRecordWithResponse(EmotionRecordUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 删除情绪记录
|
||||
*/
|
||||
boolean deleteEmotionRecord(String id);
|
||||
|
||||
/**
|
||||
* 获取用户情绪统计
|
||||
*/
|
||||
Map<String, Object> getEmotionStats(String userId, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 查询用户最近的情绪记录并返回响应
|
||||
*/
|
||||
List<EmotionRecordResponse> getRecentByUserIdWithResponse(String userId, Integer limit);
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.emotion.service;
|
||||
|
||||
import com.emotion.dto.response.UserInfoResponse;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 令牌服务接口
|
||||
*
|
||||
@@ -13,24 +15,24 @@ public interface TokenService {
|
||||
/**
|
||||
* 从请求中提取并验证令牌,获取用户信息
|
||||
*
|
||||
* @param token 访问令牌
|
||||
* @param request HTTP请求
|
||||
* @return 用户信息响应
|
||||
*/
|
||||
UserInfoResponse getUserInfoByToken(String token);
|
||||
UserInfoResponse getUserInfoByToken(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 从请求中提取并验证令牌,获取用户名
|
||||
*
|
||||
* @param token 访问令牌
|
||||
* @param request HTTP请求
|
||||
* @return 用户名
|
||||
*/
|
||||
String getUsernameByToken(String token);
|
||||
String getUsernameByToken(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 验证令牌并返回用户ID
|
||||
*
|
||||
* @param token 访问令牌
|
||||
* @param request HTTP请求
|
||||
* @return 用户ID
|
||||
*/
|
||||
String validateTokenAndGetUserId(String token);
|
||||
}
|
||||
String validateTokenAndGetUserId(HttpServletRequest request);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.emotion.service;
|
||||
|
||||
import com.emotion.dto.websocket.ChatRequest;
|
||||
import com.emotion.dto.request.WebSocketRequest;
|
||||
import com.emotion.dto.websocket.ConnectRequest;
|
||||
|
||||
import java.security.Principal;
|
||||
@@ -9,32 +9,48 @@ import java.security.Principal;
|
||||
* WebSocket服务接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-07-25
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
public interface WebSocketService {
|
||||
|
||||
/**
|
||||
* 处理聊天消息
|
||||
*
|
||||
* @param request WebSocket请求对象
|
||||
* @param sessionId 会话ID
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
void handleChatMessage(ChatRequest request, String sessionId, Principal principal);
|
||||
void handleChatMessage(WebSocketRequest request, String sessionId, Principal principal);
|
||||
|
||||
/**
|
||||
* 处理用户连接
|
||||
*
|
||||
* @param request 连接请求对象
|
||||
* @param sessionId 会话ID
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
void handleUserConnect(ConnectRequest request, String sessionId, Principal principal);
|
||||
|
||||
/**
|
||||
* 处理用户断开连接
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
void handleUserDisconnect(String sessionId, Principal principal);
|
||||
|
||||
/**
|
||||
* 处理心跳消息
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
void handleHeartbeat(String sessionId, Principal principal);
|
||||
|
||||
/**
|
||||
* 获取在线用户数量
|
||||
*
|
||||
* @return 在线用户数量
|
||||
*/
|
||||
int getOnlineUserCount();
|
||||
}
|
||||
}
|
||||
@@ -5,27 +5,41 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.achievement.AchievementCreateRequest;
|
||||
import com.emotion.dto.request.achievement.AchievementPageRequest;
|
||||
import com.emotion.dto.request.achievement.AchievementUpdateRequest;
|
||||
import com.emotion.dto.response.achievement.AchievementResponse;
|
||||
import com.emotion.entity.Achievement;
|
||||
import com.emotion.mapper.AchievementMapper;
|
||||
import com.emotion.service.AchievementService;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 成就服务实现类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-07-23
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Service
|
||||
public class AchievementServiceImpl extends ServiceImpl<AchievementMapper, Achievement> implements AchievementService {
|
||||
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<Achievement> getPage(BasePageRequest request) {
|
||||
public IPage<Achievement> getPage(AchievementPageRequest request) {
|
||||
Page<Achievement> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
@@ -35,6 +49,30 @@ public class AchievementServiceImpl extends ServiceImpl<AchievementMapper, Achie
|
||||
.or().like(Achievement::getDescription, request.getKeyword()));
|
||||
}
|
||||
|
||||
// 分类筛选
|
||||
if (StringUtils.hasText(request.getCategory())) {
|
||||
wrapper.eq(Achievement::getCategory, request.getCategory());
|
||||
}
|
||||
|
||||
// 稀有度筛选
|
||||
if (StringUtils.hasText(request.getRarity())) {
|
||||
wrapper.eq(Achievement::getRarity, request.getRarity());
|
||||
}
|
||||
|
||||
// 解锁状态筛选
|
||||
if (request.getUnlocked() != null) {
|
||||
if (request.getUnlocked()) {
|
||||
wrapper.isNotNull(Achievement::getUnlockedTime);
|
||||
} else {
|
||||
wrapper.isNull(Achievement::getUnlockedTime);
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏状态筛选
|
||||
if (request.getHidden() != null) {
|
||||
wrapper.eq(Achievement::getIsHidden, request.getHidden() ? 1 : 0);
|
||||
}
|
||||
|
||||
wrapper.eq(Achievement::getIsDeleted, 0);
|
||||
|
||||
// 排序
|
||||
@@ -256,4 +294,147 @@ public class AchievementServiceImpl extends ServiceImpl<AchievementMapper, Achie
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增的Response相关方法实现
|
||||
|
||||
@Override
|
||||
public PageResult<AchievementResponse> getPageWithResponse(AchievementPageRequest request) {
|
||||
IPage<Achievement> page = getPage(request);
|
||||
List<AchievementResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
PageResult<AchievementResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AchievementResponse getAchievementResponseById(String id) {
|
||||
Achievement achievement = this.getById(id);
|
||||
if (achievement == null) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(achievement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AchievementResponse createAchievementWithResponse(AchievementCreateRequest request) {
|
||||
Achievement achievement = new Achievement();
|
||||
org.springframework.beans.BeanUtils.copyProperties(request, achievement);
|
||||
achievement.setId(snowflakeIdGenerator.nextIdAsString());
|
||||
|
||||
boolean saved = this.save(achievement);
|
||||
if (!saved) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(achievement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AchievementResponse updateAchievementWithResponse(AchievementUpdateRequest request) {
|
||||
Achievement achievement = this.getById(request.getId());
|
||||
if (achievement == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 更新非空字段
|
||||
if (request.getTitle() != null) {
|
||||
achievement.setTitle(request.getTitle());
|
||||
}
|
||||
if (request.getDescription() != null) {
|
||||
achievement.setDescription(request.getDescription());
|
||||
}
|
||||
if (request.getCategory() != null) {
|
||||
achievement.setCategory(request.getCategory());
|
||||
}
|
||||
if (request.getIcon() != null) {
|
||||
achievement.setIcon(request.getIcon());
|
||||
}
|
||||
if (request.getRarity() != null) {
|
||||
achievement.setRarity(request.getRarity());
|
||||
}
|
||||
if (request.getConditionType() != null) {
|
||||
achievement.setConditionType(request.getConditionType());
|
||||
}
|
||||
if (request.getConditionValue() != null) {
|
||||
achievement.setConditionValue(request.getConditionValue());
|
||||
}
|
||||
if (request.getRewards() != null) {
|
||||
achievement.setRewards(request.getRewards());
|
||||
}
|
||||
if (request.getIsHidden() != null) {
|
||||
achievement.setIsHidden(request.getIsHidden());
|
||||
}
|
||||
|
||||
achievement.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
boolean updated = this.updateById(achievement);
|
||||
if (!updated) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(achievement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AchievementResponse> getByCategoryWithResponse(String category) {
|
||||
List<Achievement> achievements = getByCategory(category);
|
||||
return achievements.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AchievementResponse> getByRarityWithResponse(String rarity) {
|
||||
List<Achievement> achievements = getByRarity(rarity);
|
||||
return achievements.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AchievementResponse> getUnlockedAchievementsWithResponse() {
|
||||
List<Achievement> achievements = getUnlockedAchievements();
|
||||
return achievements.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AchievementResponse> getLockedAchievementsWithResponse() {
|
||||
List<Achievement> achievements = getLockedAchievements();
|
||||
return achievements.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AchievementResponse> getRecentlyUnlockedWithResponse(Integer limit) {
|
||||
List<Achievement> achievements = getRecentlyUnlocked(limit);
|
||||
return achievements.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private AchievementResponse convertToResponse(Achievement achievement) {
|
||||
AchievementResponse response = new AchievementResponse();
|
||||
org.springframework.beans.BeanUtils.copyProperties(achievement, response);
|
||||
response.setId(achievement.getId());
|
||||
if (achievement.getCreateTime() != null) {
|
||||
response.setCreateTime(achievement.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (achievement.getUpdateTime() != null) {
|
||||
response.setUpdateTime(achievement.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (achievement.getUnlockedTime() != null) {
|
||||
response.setUnlockedTime(achievement.getUnlockedTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ import com.emotion.exception.TokenException;
|
||||
import com.emotion.service.AuthService;
|
||||
import com.emotion.service.UserService;
|
||||
import com.emotion.util.JwtUtil;
|
||||
import com.emotion.util.TokenUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -22,6 +23,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -55,6 +57,9 @@ public class AuthServiceImpl implements AuthService {
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Autowired
|
||||
private TokenUtil tokenUtil;
|
||||
|
||||
private static final String CAPTCHA_PREFIX = "captcha:";
|
||||
private static final String TOKEN_PREFIX = "token:";
|
||||
private static final String REFRESH_TOKEN_PREFIX = "refresh_token:";
|
||||
@@ -239,7 +244,8 @@ public class AuthServiceImpl implements AuthService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean logoutByToken(String token) {
|
||||
public boolean logoutByToken(HttpServletRequest request) {
|
||||
String token = tokenUtil.extractToken(request);
|
||||
String userId = validateTokenAndGetUserId(token);
|
||||
return logout(userId, token);
|
||||
}
|
||||
@@ -248,7 +254,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
* 验证令牌并获取用户ID
|
||||
*/
|
||||
private String validateTokenAndGetUserId(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
if (!tokenUtil.isValidToken(token)) {
|
||||
throw new TokenException("未提供访问令牌");
|
||||
}
|
||||
|
||||
@@ -293,9 +299,15 @@ public class AuthServiceImpl implements AuthService {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateToken(HttpServletRequest request) {
|
||||
String token = tokenUtil.extractToken(request);
|
||||
return validateToken(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateToken(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
if (!tokenUtil.isValidToken(token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -314,7 +326,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
|
||||
@Override
|
||||
public String getUserIdFromToken(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
if (!tokenUtil.isValidToken(token)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -336,7 +348,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
|
||||
@Override
|
||||
public String getUsernameFromToken(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
if (!tokenUtil.isValidToken(token)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -498,4 +510,4 @@ public class AuthServiceImpl implements AuthService {
|
||||
User user = userService.getByPhone(phone);
|
||||
return user != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,27 @@
|
||||
package com.emotion.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.comment.CommentCreateRequest;
|
||||
import com.emotion.dto.request.comment.CommentUpdateRequest;
|
||||
import com.emotion.dto.request.comment.CommentPageRequest;
|
||||
import com.emotion.dto.response.comment.CommentResponse;
|
||||
import com.emotion.entity.Comment;
|
||||
import com.emotion.mapper.CommentMapper;
|
||||
import com.emotion.service.CommentService;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 评论服务实现类
|
||||
@@ -23,219 +32,125 @@ import java.util.List;
|
||||
@Service
|
||||
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {
|
||||
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<Comment> getPage(BasePageRequest request) {
|
||||
public PageResult<CommentResponse> getPage(CommentPageRequest request) {
|
||||
Page<Comment> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 根据请求参数构建查询条件
|
||||
if (StringUtils.hasText(request.getPostId())) {
|
||||
wrapper.eq(Comment::getPostId, request.getPostId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getUserId())) {
|
||||
wrapper.eq(Comment::getUserId, request.getUserId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getKeyword())) {
|
||||
wrapper.like(Comment::getContent, request.getKeyword());
|
||||
}
|
||||
|
||||
|
||||
wrapper.eq(Comment::getIsDeleted, 0).orderByDesc(Comment::getCreateTime);
|
||||
return this.page(page, wrapper);
|
||||
page = this.page(page, wrapper);
|
||||
|
||||
return convertPageToPageResult(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<Comment> getPageByPostId(BasePageRequest request, String postId) {
|
||||
Page<Comment> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime);
|
||||
return this.page(page, wrapper);
|
||||
public CommentResponse getById(String id) {
|
||||
Comment comment = this.getBaseMapper().selectById(id);
|
||||
if (comment == null) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<Comment> getPageByUserId(BasePageRequest request, String userId) {
|
||||
Page<Comment> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getUserId, userId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime);
|
||||
return this.page(page, wrapper);
|
||||
public CommentResponse create(CommentCreateRequest request) {
|
||||
Comment comment = new Comment();
|
||||
comment.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
|
||||
comment.setPostId(request.getPostId());
|
||||
|
||||
// 从上下文中获取当前用户ID
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
if (currentUserId != null) {
|
||||
comment.setUserId(currentUserId);
|
||||
} else if (request.getUserId() != null) {
|
||||
// 如果上下文中没有用户ID,则使用请求中的用户ID(向后兼容)
|
||||
comment.setUserId(request.getUserId());
|
||||
} else {
|
||||
throw new IllegalArgumentException("用户ID不能为空");
|
||||
}
|
||||
|
||||
comment.setContent(request.getContent());
|
||||
comment.setReplyToId(request.getReplyToId());
|
||||
comment.setLikes(0);
|
||||
|
||||
this.save(comment);
|
||||
return convertToResponse(comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getByPostId(String postId) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByAsc(Comment::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
public CommentResponse update(CommentUpdateRequest request) {
|
||||
Comment comment = this.getBaseMapper().selectById(request.getId());
|
||||
if (comment == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 只更新非空字段
|
||||
if (StringUtils.hasText(request.getContent())) {
|
||||
comment.setContent(request.getContent());
|
||||
}
|
||||
|
||||
this.updateById(comment);
|
||||
return convertToResponse(comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getByUserId(String userId) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getUserId, userId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getRepliesByCommentId(String replyToId) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getReplyToId, replyToId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByAsc(Comment::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getTopLevelCommentsByPostId(String postId) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.isNull(Comment::getReplyToId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByAsc(Comment::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getByLikesRange(Integer minLikes, Integer maxLikes) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.between(Comment::getLikes, minLikes, maxLikes)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getLikes);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.between(Comment::getCreateTime, startTime, endTime)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByPostId(String postId) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.eq(Comment::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByUserId(String userId) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getUserId, userId)
|
||||
.eq(Comment::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countRepliesByCommentId(String commentId) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getReplyToId, commentId)
|
||||
.eq(Comment::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countTopLevelCommentsByPostId(String postId) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.isNull(Comment::getReplyToId)
|
||||
.eq(Comment::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getMostLikedCommentsByPostId(String postId, Integer limit) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getLikes)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getLatestCommentsByPostId(String postId, Integer limit) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getRecentByUserId(String userId, Integer limit) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getUserId, userId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getPopularCommentsByPostId(String postId, Integer limit) {
|
||||
// 这里需要自定义SQL查询,暂时返回最新评论
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> searchByKeyword(String keyword) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.like(Comment::getContent, keyword)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> searchByPostIdAndKeyword(String postId, String keyword) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.like(Comment::getContent, keyword)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Comment> getByPostIdAndUserId(String postId, String userId) {
|
||||
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Comment::getPostId, postId)
|
||||
.eq(Comment::getUserId, userId)
|
||||
.eq(Comment::getIsDeleted, 0)
|
||||
.orderByDesc(Comment::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateLikes(String id, Integer increment) {
|
||||
Comment comment = this.getById(id);
|
||||
public boolean delete(String id) {
|
||||
Comment comment = this.getBaseMapper().selectById(id);
|
||||
if (comment == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer newLikes = comment.getLikes() + increment;
|
||||
comment.setLikes(newLikes);
|
||||
// 逻辑删除
|
||||
comment.setIsDeleted(1);
|
||||
return this.updateById(comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comment createComment(String postId, String userId, String content, String replyToId) {
|
||||
Comment comment = new Comment();
|
||||
comment.setPostId(postId);
|
||||
comment.setUserId(userId);
|
||||
comment.setContent(content);
|
||||
comment.setReplyToId(replyToId);
|
||||
comment.setLikes(0);
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private CommentResponse convertToResponse(Comment comment) {
|
||||
CommentResponse response = new CommentResponse();
|
||||
BeanUtils.copyProperties(comment, response);
|
||||
response.setId(comment.getId());
|
||||
if (comment.getCreateTime() != null) {
|
||||
response.setCreateTime(comment.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (comment.getUpdateTime() != null) {
|
||||
response.setUpdateTime(comment.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
this.save(comment);
|
||||
return comment;
|
||||
/**
|
||||
* 转换分页对象为PageResult对象
|
||||
*/
|
||||
private PageResult<CommentResponse> convertPageToPageResult(Page<Comment> page) {
|
||||
PageResult<CommentResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList()));
|
||||
return pageResult;
|
||||
}
|
||||
}
|
||||
+130
-269
@@ -1,18 +1,26 @@
|
||||
package com.emotion.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.community.CommunityPostCreateRequest;
|
||||
import com.emotion.dto.request.community.CommunityPostUpdateRequest;
|
||||
import com.emotion.dto.request.community.CommunityPostPageRequest;
|
||||
import com.emotion.dto.response.community.CommunityPostResponse;
|
||||
import com.emotion.entity.CommunityPost;
|
||||
import com.emotion.mapper.CommunityPostMapper;
|
||||
import com.emotion.service.CommunityPostService;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 社区帖子服务实现类
|
||||
@@ -23,296 +31,149 @@ import java.util.List;
|
||||
@Service
|
||||
public class CommunityPostServiceImpl extends ServiceImpl<CommunityPostMapper, CommunityPost> implements CommunityPostService {
|
||||
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<CommunityPost> getPage(BasePageRequest request) {
|
||||
public PageResult<CommunityPostResponse> getPage(CommunityPostPageRequest request) {
|
||||
Page<CommunityPost> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 根据请求参数构建查询条件
|
||||
if (StringUtils.hasText(request.getUserId())) {
|
||||
wrapper.eq(CommunityPost::getUserId, request.getUserId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getType())) {
|
||||
wrapper.eq(CommunityPost::getType, request.getType());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getLocationId())) {
|
||||
wrapper.eq(CommunityPost::getLocationId, request.getLocationId());
|
||||
}
|
||||
|
||||
if (request.getPublicOnly() != null && request.getPublicOnly()) {
|
||||
wrapper.eq(CommunityPost::getIsPrivate, 0);
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getKeyword())) {
|
||||
wrapper.and(w -> w.like(CommunityPost::getTitle, request.getKeyword())
|
||||
.or().like(CommunityPost::getContent, request.getKeyword()));
|
||||
}
|
||||
|
||||
|
||||
wrapper.eq(CommunityPost::getIsDeleted, 0).orderByDesc(CommunityPost::getCreateTime);
|
||||
return this.page(page, wrapper);
|
||||
page = this.page(page, wrapper);
|
||||
|
||||
return convertPageToResponse(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<CommunityPost> getPublicPostsPage(BasePageRequest request) {
|
||||
Page<CommunityPost> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
if (StringUtils.hasText(request.getKeyword())) {
|
||||
wrapper.and(w -> w.like(CommunityPost::getTitle, request.getKeyword())
|
||||
.or().like(CommunityPost::getContent, request.getKeyword()));
|
||||
}
|
||||
|
||||
wrapper.eq(CommunityPost::getIsPrivate, 0)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime);
|
||||
return this.page(page, wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<CommunityPost> getPageByUserId(BasePageRequest request, String userId) {
|
||||
Page<CommunityPost> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getUserId, userId)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime);
|
||||
return this.page(page, wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getByLocationId(String locationId) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getLocationId, locationId)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getByType(String type) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getType, type)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getPrivatePostsByUserId(String userId) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getUserId, userId)
|
||||
.eq(CommunityPost::getIsPrivate, 1)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getByLikesRange(Integer minLikes, Integer maxLikes) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.between(CommunityPost::getLikes, minLikes, maxLikes)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getLikes);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getByViewRange(Integer minViews, Integer maxViews) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.between(CommunityPost::getViewCount, minViews, maxViews)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getViewCount);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getByCommentRange(Integer minComments, Integer maxComments) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.between(CommunityPost::getCommentCount, minComments, maxComments)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCommentCount);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.between(CommunityPost::getCreateTime, startTime, endTime)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByUserId(String userId) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getUserId, userId)
|
||||
.eq(CommunityPost::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countPublicPostsByUserId(String userId) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getUserId, userId)
|
||||
.eq(CommunityPost::getIsPrivate, 0)
|
||||
.eq(CommunityPost::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countPrivatePostsByUserId(String userId) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getUserId, userId)
|
||||
.eq(CommunityPost::getIsPrivate, 1)
|
||||
.eq(CommunityPost::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByType(String type) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getType, type)
|
||||
.eq(CommunityPost::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByLocationId(String locationId) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getLocationId, locationId)
|
||||
.eq(CommunityPost::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getMostLikedPosts(Integer limit) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getLikes)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getMostViewedPosts(Integer limit) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getViewCount)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getLatestPosts(Integer limit) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getPopularPosts(Integer limit) {
|
||||
// 这里需要自定义SQL查询,暂时返回最新帖子
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getByTag(String tag) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.like(CommunityPost::getTags, tag)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> searchByKeyword(String keyword) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.and(w -> w.like(CommunityPost::getTitle, keyword)
|
||||
.or().like(CommunityPost::getContent, keyword))
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getRecentByUserId(String userId, Integer limit) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getUserId, userId)
|
||||
.eq(CommunityPost::getIsDeleted, 0)
|
||||
.orderByDesc(CommunityPost::getCreateTime)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateLikes(String id, Integer increment) {
|
||||
CommunityPost post = this.getById(id);
|
||||
public CommunityPostResponse getById(String id) {
|
||||
CommunityPost post = this.getBaseMapper().selectById(id);
|
||||
if (post == null) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
// 增加浏览数
|
||||
Integer viewCount = post.getViewCount() == null ? 1 : post.getViewCount() + 1;
|
||||
post.setViewCount(viewCount);
|
||||
this.updateById(post);
|
||||
|
||||
Integer newLikes = post.getLikes() + increment;
|
||||
post.setLikes(newLikes);
|
||||
return this.updateById(post);
|
||||
return convertToResponse(post);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incrementViewCount(String id) {
|
||||
CommunityPost post = this.getById(id);
|
||||
if (post == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer newViewCount = post.getViewCount() + 1;
|
||||
post.setViewCount(newViewCount);
|
||||
return this.updateById(post);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateCommentCount(String id, Integer increment) {
|
||||
CommunityPost post = this.getById(id);
|
||||
if (post == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer newCommentCount = post.getCommentCount() + increment;
|
||||
post.setCommentCount(newCommentCount);
|
||||
return this.updateById(post);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePrivacyStatus(String id, Integer isPrivate) {
|
||||
public CommunityPostResponse create(CommunityPostCreateRequest request) {
|
||||
CommunityPost post = new CommunityPost();
|
||||
post.setId(id);
|
||||
post.setIsPrivate(isPrivate);
|
||||
return this.updateById(post);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommunityPost> getRecommendedPosts(String type, String locationId, Integer limit) {
|
||||
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CommunityPost::getIsDeleted, 0);
|
||||
|
||||
if (StringUtils.hasText(type)) {
|
||||
wrapper.eq(CommunityPost::getType, type);
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(locationId)) {
|
||||
wrapper.eq(CommunityPost::getLocationId, locationId);
|
||||
}
|
||||
|
||||
wrapper.orderByDesc(CommunityPost::getCreateTime)
|
||||
.last("LIMIT " + limit);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommunityPost createPost(String userId, String title, String content, String type,
|
||||
String locationId, String tags, Integer isPrivate) {
|
||||
CommunityPost post = new CommunityPost();
|
||||
post.setUserId(userId);
|
||||
post.setTitle(title);
|
||||
post.setContent(content);
|
||||
post.setType(type);
|
||||
post.setLocationId(locationId);
|
||||
post.setTags(tags);
|
||||
post.setIsPrivate(isPrivate);
|
||||
post.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
|
||||
post.setUserId(request.getUserId());
|
||||
post.setTitle(request.getTitle());
|
||||
post.setContent(request.getContent());
|
||||
post.setType(request.getType());
|
||||
post.setLocationId(request.getLocationId());
|
||||
post.setTags(request.getTags());
|
||||
post.setIsPrivate(request.getIsPrivate() != null ? request.getIsPrivate() : 0); // 默认公开
|
||||
post.setLikes(0);
|
||||
post.setViewCount(0);
|
||||
post.setCommentCount(0);
|
||||
|
||||
this.save(post);
|
||||
return post;
|
||||
return convertToResponse(post);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommunityPostResponse update(CommunityPostUpdateRequest request) {
|
||||
CommunityPost post = this.getBaseMapper().selectById(request.getId());
|
||||
if (post == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 只更新非空字段
|
||||
if (request.getTitle() != null) {
|
||||
post.setTitle(request.getTitle());
|
||||
}
|
||||
if (request.getContent() != null) {
|
||||
post.setContent(request.getContent());
|
||||
}
|
||||
if (request.getType() != null) {
|
||||
post.setType(request.getType());
|
||||
}
|
||||
if (request.getLocationId() != null) {
|
||||
post.setLocationId(request.getLocationId());
|
||||
}
|
||||
if (request.getTags() != null) {
|
||||
post.setTags(request.getTags());
|
||||
}
|
||||
if (request.getIsPrivate() != null) {
|
||||
post.setIsPrivate(request.getIsPrivate());
|
||||
}
|
||||
|
||||
this.updateById(post);
|
||||
return convertToResponse(post);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(String id) {
|
||||
CommunityPost post = this.getBaseMapper().selectById(id);
|
||||
if (post == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 逻辑删除
|
||||
post.setIsDeleted(1);
|
||||
return this.updateById(post);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private CommunityPostResponse convertToResponse(CommunityPost post) {
|
||||
CommunityPostResponse response = new CommunityPostResponse();
|
||||
BeanUtils.copyProperties(post, response);
|
||||
response.setId(post.getId());
|
||||
if (post.getCreateTime() != null) {
|
||||
response.setCreateTime(post.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (post.getUpdateTime() != null) {
|
||||
response.setUpdateTime(post.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换分页对象为响应对象
|
||||
*/
|
||||
private PageResult<CommunityPostResponse> convertPageToResponse(Page<CommunityPost> page) {
|
||||
PageResult<CommunityPostResponse> responsePage = new PageResult<>();
|
||||
responsePage.setCurrent(page.getCurrent());
|
||||
responsePage.setSize(page.getSize());
|
||||
responsePage.setTotal(page.getTotal());
|
||||
responsePage.setPages(page.getPages());
|
||||
responsePage.setRecords(
|
||||
page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList()));
|
||||
return responsePage;
|
||||
}
|
||||
}
|
||||
+192
-4
@@ -4,15 +4,25 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.ConversationCreateRequest;
|
||||
import com.emotion.dto.request.ConversationPageRequest;
|
||||
import com.emotion.dto.response.ConversationResponse;
|
||||
import com.emotion.entity.Conversation;
|
||||
import com.emotion.mapper.ConversationMapper;
|
||||
import com.emotion.service.ConversationService;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 会话服务实现类
|
||||
@@ -23,11 +33,29 @@ import java.util.List;
|
||||
@Service
|
||||
public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Conversation> implements ConversationService {
|
||||
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<Conversation> getPage(BasePageRequest request) {
|
||||
public IPage<Conversation> getPage(ConversationPageRequest request) {
|
||||
Page<Conversation> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<Conversation> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 根据请求参数构建查询条件
|
||||
if (StringUtils.hasText(request.getUserId())) {
|
||||
wrapper.eq(Conversation::getUserId, request.getUserId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getStatus())) {
|
||||
wrapper.eq(Conversation::getConversationStatus, request.getStatus());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getType())) {
|
||||
wrapper.eq(Conversation::getType, request.getType());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getKeyword())) {
|
||||
wrapper.and(w -> w.like(Conversation::getTitle, request.getKeyword())
|
||||
.or().like(Conversation::getSummary, request.getKeyword()));
|
||||
@@ -38,10 +66,16 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Con
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<Conversation> getPageByUserId(BasePageRequest request, String userId) {
|
||||
public IPage<Conversation> getPageByUserId(ConversationPageRequest request) {
|
||||
Page<Conversation> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<Conversation> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Conversation::getUserId, userId)
|
||||
|
||||
// 必须提供userId参数
|
||||
if (!StringUtils.hasText(request.getUserId())) {
|
||||
throw new IllegalArgumentException("userId不能为空");
|
||||
}
|
||||
|
||||
wrapper.eq(Conversation::getUserId, request.getUserId())
|
||||
.eq(Conversation::getIsDeleted, 0)
|
||||
.orderByDesc(Conversation::getCreateTime);
|
||||
return this.page(page, wrapper);
|
||||
@@ -148,6 +182,7 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Con
|
||||
@Override
|
||||
public Conversation createConversation(String userId, String title, String cozeConversationId) {
|
||||
Conversation conversation = new Conversation();
|
||||
conversation.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
|
||||
conversation.setUserId(userId);
|
||||
conversation.setTitle(title);
|
||||
conversation.setCozeConversationId(cozeConversationId);
|
||||
@@ -172,4 +207,157 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Con
|
||||
conversation.setEndTime(LocalDateTime.now());
|
||||
return this.updateById(conversation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<ConversationResponse> getPageWithResponse(ConversationPageRequest request) {
|
||||
IPage<Conversation> page = this.getPage(request);
|
||||
return convertPageToResponse(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<ConversationResponse> getPageByUserIdWithResponse(ConversationPageRequest request) {
|
||||
IPage<Conversation> page = this.getPageByUserId(request);
|
||||
return convertPageToResponse(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConversationResponse getConversationResponseById(String id) {
|
||||
Conversation conversation = this.getById(id);
|
||||
if (conversation == null) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(conversation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConversationResponse> getByUserIdWithResponse(String userId) {
|
||||
List<Conversation> conversations = this.getByUserId(userId);
|
||||
return conversations.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConversationResponse> getActiveConversationsWithResponse() {
|
||||
// 实现获取活跃对话的逻辑
|
||||
LambdaQueryWrapper<Conversation> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Conversation::getConversationStatus, "active")
|
||||
.eq(Conversation::getIsDeleted, 0)
|
||||
.orderByDesc(Conversation::getLastActiveTime);
|
||||
List<Conversation> conversations = this.list(wrapper);
|
||||
return conversations.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConversationResponse> getArchivedConversationsWithResponse() {
|
||||
// 实现获取归档对话的逻辑
|
||||
LambdaQueryWrapper<Conversation> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Conversation::getConversationStatus, "archived")
|
||||
.eq(Conversation::getIsDeleted, 0)
|
||||
.orderByDesc(Conversation::getCreateTime);
|
||||
List<Conversation> conversations = this.list(wrapper);
|
||||
return conversations.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean archiveConversation(String id) {
|
||||
Conversation conversation = new Conversation();
|
||||
conversation.setId(id);
|
||||
conversation.setConversationStatus("archived");
|
||||
return this.updateById(conversation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateConversation(String id) {
|
||||
Conversation conversation = new Conversation();
|
||||
conversation.setId(id);
|
||||
conversation.setConversationStatus("active");
|
||||
return this.updateById(conversation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConversationResponse createConversationWithResponse(ConversationCreateRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
// 获取客户端IP地址
|
||||
String clientIp = UserContextUtils.getClientIpAddress(httpRequest);
|
||||
|
||||
Conversation conversation = this.createConversation(
|
||||
request.getUserId(),
|
||||
request.getTitle(),
|
||||
null // cozeConversationId
|
||||
);
|
||||
|
||||
// 设置客户端IP
|
||||
conversation.setClientIp(clientIp);
|
||||
this.updateById(conversation);
|
||||
|
||||
return convertToResponse(conversation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConversationResponse updateConversationWithResponse(ConversationCreateRequest request) {
|
||||
Conversation conversation = this.getById(request.getId()); // 修复:使用ID而不是userId
|
||||
if (conversation == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if (request.getTitle() != null) {
|
||||
conversation.setTitle(request.getTitle());
|
||||
}
|
||||
if (request.getType() != null) {
|
||||
conversation.setType(request.getType());
|
||||
}
|
||||
|
||||
this.updateById(conversation);
|
||||
return convertToResponse(conversation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateConversationStatus(String id, String status) {
|
||||
Conversation conversation = new Conversation();
|
||||
conversation.setId(id);
|
||||
conversation.setConversationStatus(status);
|
||||
return this.updateById(conversation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private ConversationResponse convertToResponse(Conversation conversation) {
|
||||
ConversationResponse response = new ConversationResponse();
|
||||
BeanUtils.copyProperties(conversation, response);
|
||||
response.setId(conversation.getId());
|
||||
response.setStatus(conversation.getConversationStatus());
|
||||
if (conversation.getCreateTime() != null) {
|
||||
response.setCreateTime(conversation.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (conversation.getUpdateTime() != null) {
|
||||
response.setUpdateTime(conversation.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (conversation.getLastActiveTime() != null) {
|
||||
response.setLastMessageTime(conversation.getLastActiveTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换分页对象为响应对象
|
||||
*/
|
||||
private PageResult<ConversationResponse> convertPageToResponse(IPage<Conversation> page) {
|
||||
PageResult<ConversationResponse> responsePage = new PageResult<>();
|
||||
responsePage.setCurrent(page.getCurrent());
|
||||
responsePage.setSize(page.getSize());
|
||||
responsePage.setTotal(page.getTotal());
|
||||
responsePage.setPages(page.getPages());
|
||||
responsePage.setRecords(
|
||||
page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList()));
|
||||
return responsePage;
|
||||
}
|
||||
}
|
||||
+259
-146
@@ -1,20 +1,27 @@
|
||||
package com.emotion.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.coze.CozeApiCallPageRequest;
|
||||
import com.emotion.dto.request.coze.CozeApiCallCreateRequest;
|
||||
import com.emotion.dto.request.coze.CozeApiCallUpdateRequest;
|
||||
import com.emotion.dto.response.coze.CozeApiCallResponse;
|
||||
import com.emotion.entity.CozeApiCall;
|
||||
import com.emotion.mapper.CozeApiCallMapper;
|
||||
import com.emotion.service.CozeApiCallService;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Coze API调用记录服务实现类
|
||||
@@ -25,15 +32,46 @@ import java.util.List;
|
||||
@Service
|
||||
public class CozeApiCallServiceImpl extends ServiceImpl<CozeApiCallMapper, CozeApiCall> implements CozeApiCallService {
|
||||
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<CozeApiCall> getPage(BasePageRequest request) {
|
||||
public PageResult<CozeApiCallResponse> getPage(CozeApiCallPageRequest request) {
|
||||
Page<CozeApiCall> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 根据请求参数构建查询条件
|
||||
if (StringUtils.hasText(request.getConversationId())) {
|
||||
wrapper.eq(CozeApiCall::getConversationId, request.getConversationId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getUserId())) {
|
||||
wrapper.eq(CozeApiCall::getUserId, request.getUserId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getBotId())) {
|
||||
wrapper.eq(CozeApiCall::getBotId, request.getBotId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getStatus())) {
|
||||
wrapper.eq(CozeApiCall::getStatus, request.getStatus());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getRequestType())) {
|
||||
wrapper.eq(CozeApiCall::getRequestType, request.getRequestType());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getTraceId())) {
|
||||
wrapper.eq(CozeApiCall::getTraceId, request.getTraceId());
|
||||
}
|
||||
|
||||
// 关键词搜索
|
||||
if (StringUtils.hasText(request.getKeyword())) {
|
||||
wrapper.like(CozeApiCall::getRequestUrl, request.getKeyword())
|
||||
.or().like(CozeApiCall::getAiReply, request.getKeyword());
|
||||
wrapper.and(w -> w.like(CozeApiCall::getRequestUrl, request.getKeyword())
|
||||
.or().like(CozeApiCall::getAiReply, request.getKeyword())
|
||||
.or().like(CozeApiCall::getUserMessage, request.getKeyword()));
|
||||
}
|
||||
|
||||
wrapper.eq(CozeApiCall::getIsDeleted, 0);
|
||||
@@ -49,77 +87,193 @@ public class CozeApiCallServiceImpl extends ServiceImpl<CozeApiCallMapper, CozeA
|
||||
wrapper.orderByDesc(CozeApiCall::getStartTime);
|
||||
}
|
||||
|
||||
return this.page(page, wrapper);
|
||||
page = this.page(page, wrapper);
|
||||
return convertPageToPageResult(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<CozeApiCall> getPageByConversationId(BasePageRequest request, String conversationId) {
|
||||
Page<CozeApiCall> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CozeApiCall::getConversationId, conversationId)
|
||||
.eq(CozeApiCall::getIsDeleted, 0);
|
||||
public CozeApiCallResponse getById(String id) {
|
||||
CozeApiCall apiCall = this.getBaseMapper().selectById(id);
|
||||
if (apiCall == null || apiCall.getIsDeleted() == 1) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(apiCall);
|
||||
}
|
||||
|
||||
// 关键词搜索
|
||||
if (StringUtils.hasText(request.getKeyword())) {
|
||||
wrapper.like(CozeApiCall::getRequestUrl, request.getKeyword())
|
||||
.or().like(CozeApiCall::getAiReply, request.getKeyword());
|
||||
@Override
|
||||
public CozeApiCallResponse create(CozeApiCallCreateRequest request) {
|
||||
CozeApiCall apiCall = new CozeApiCall();
|
||||
apiCall.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
|
||||
|
||||
// 复制属性
|
||||
apiCall.setConversationId(request.getConversationId());
|
||||
apiCall.setMessageId(request.getMessageId());
|
||||
apiCall.setCozeChatId(request.getCozeChatId());
|
||||
apiCall.setCozeConversationId(request.getCozeConversationId());
|
||||
apiCall.setBotId(request.getBotId());
|
||||
apiCall.setWorkflowId(request.getWorkflowId());
|
||||
apiCall.setUserId(request.getUserId());
|
||||
apiCall.setRequestType(request.getRequestType());
|
||||
apiCall.setRequestUrl(request.getRequestUrl());
|
||||
apiCall.setRequestBody(request.getRequestBody());
|
||||
apiCall.setRequestHeaders(request.getRequestHeaders());
|
||||
apiCall.setUserMessage(request.getUserMessage());
|
||||
apiCall.setUserMessageType(request.getUserMessageType());
|
||||
apiCall.setAiReply(request.getAiReply());
|
||||
apiCall.setAiReplyType(request.getAiReplyType());
|
||||
apiCall.setResponseStatus(request.getResponseStatus());
|
||||
apiCall.setResponseBody(request.getResponseBody());
|
||||
apiCall.setResponseHeaders(request.getResponseHeaders());
|
||||
apiCall.setPollCount(request.getPollCount());
|
||||
apiCall.setFinalStatus(request.getFinalStatus());
|
||||
apiCall.setStatus(request.getStatus());
|
||||
apiCall.setDurationMs(request.getDurationMs());
|
||||
apiCall.setPromptTokens(request.getPromptTokens());
|
||||
apiCall.setCompletionTokens(request.getCompletionTokens());
|
||||
apiCall.setTotalTokens(request.getTotalTokens());
|
||||
apiCall.setCost(request.getCost());
|
||||
apiCall.setFunctionCalls(request.getFunctionCalls());
|
||||
apiCall.setFunctionResults(request.getFunctionResults());
|
||||
apiCall.setErrorCode(request.getErrorCode());
|
||||
apiCall.setErrorMessage(request.getErrorMessage());
|
||||
apiCall.setClientIp(request.getClientIp());
|
||||
apiCall.setUserAgent(request.getUserAgent());
|
||||
apiCall.setSessionId(request.getSessionId());
|
||||
apiCall.setTraceId(request.getTraceId());
|
||||
apiCall.setMetadata(request.getMetadata());
|
||||
|
||||
this.save(apiCall);
|
||||
return convertToResponse(apiCall);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CozeApiCallResponse update(CozeApiCallUpdateRequest request) {
|
||||
CozeApiCall apiCall = this.getBaseMapper().selectById(request.getId());
|
||||
if (apiCall == null || apiCall.getIsDeleted() == 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
wrapper.orderByDesc(CozeApiCall::getStartTime);
|
||||
return this.page(page, wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<CozeApiCall> getPageByUserId(BasePageRequest request, String userId) {
|
||||
Page<CozeApiCall> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CozeApiCall::getUserId, userId)
|
||||
.eq(CozeApiCall::getIsDeleted, 0);
|
||||
|
||||
// 关键词搜索
|
||||
if (StringUtils.hasText(request.getKeyword())) {
|
||||
wrapper.like(CozeApiCall::getRequestUrl, request.getKeyword())
|
||||
.or().like(CozeApiCall::getAiReply, request.getKeyword());
|
||||
// 只更新非空字段
|
||||
if (request.getConversationId() != null) {
|
||||
apiCall.setConversationId(request.getConversationId());
|
||||
}
|
||||
if (request.getMessageId() != null) {
|
||||
apiCall.setMessageId(request.getMessageId());
|
||||
}
|
||||
if (request.getCozeChatId() != null) {
|
||||
apiCall.setCozeChatId(request.getCozeChatId());
|
||||
}
|
||||
if (request.getCozeConversationId() != null) {
|
||||
apiCall.setCozeConversationId(request.getCozeConversationId());
|
||||
}
|
||||
if (request.getBotId() != null) {
|
||||
apiCall.setBotId(request.getBotId());
|
||||
}
|
||||
if (request.getWorkflowId() != null) {
|
||||
apiCall.setWorkflowId(request.getWorkflowId());
|
||||
}
|
||||
if (request.getUserId() != null) {
|
||||
apiCall.setUserId(request.getUserId());
|
||||
}
|
||||
if (request.getRequestType() != null) {
|
||||
apiCall.setRequestType(request.getRequestType());
|
||||
}
|
||||
if (request.getRequestUrl() != null) {
|
||||
apiCall.setRequestUrl(request.getRequestUrl());
|
||||
}
|
||||
if (request.getRequestBody() != null) {
|
||||
apiCall.setRequestBody(request.getRequestBody());
|
||||
}
|
||||
if (request.getRequestHeaders() != null) {
|
||||
apiCall.setRequestHeaders(request.getRequestHeaders());
|
||||
}
|
||||
if (request.getUserMessage() != null) {
|
||||
apiCall.setUserMessage(request.getUserMessage());
|
||||
}
|
||||
if (request.getUserMessageType() != null) {
|
||||
apiCall.setUserMessageType(request.getUserMessageType());
|
||||
}
|
||||
if (request.getAiReply() != null) {
|
||||
apiCall.setAiReply(request.getAiReply());
|
||||
}
|
||||
if (request.getAiReplyType() != null) {
|
||||
apiCall.setAiReplyType(request.getAiReplyType());
|
||||
}
|
||||
if (request.getResponseStatus() != null) {
|
||||
apiCall.setResponseStatus(request.getResponseStatus());
|
||||
}
|
||||
if (request.getResponseBody() != null) {
|
||||
apiCall.setResponseBody(request.getResponseBody());
|
||||
}
|
||||
if (request.getResponseHeaders() != null) {
|
||||
apiCall.setResponseHeaders(request.getResponseHeaders());
|
||||
}
|
||||
if (request.getPollCount() != null) {
|
||||
apiCall.setPollCount(request.getPollCount());
|
||||
}
|
||||
if (request.getFinalStatus() != null) {
|
||||
apiCall.setFinalStatus(request.getFinalStatus());
|
||||
}
|
||||
if (request.getStatus() != null) {
|
||||
apiCall.setStatus(request.getStatus());
|
||||
}
|
||||
if (request.getDurationMs() != null) {
|
||||
apiCall.setDurationMs(request.getDurationMs());
|
||||
}
|
||||
if (request.getPromptTokens() != null) {
|
||||
apiCall.setPromptTokens(request.getPromptTokens());
|
||||
}
|
||||
if (request.getCompletionTokens() != null) {
|
||||
apiCall.setCompletionTokens(request.getCompletionTokens());
|
||||
}
|
||||
if (request.getTotalTokens() != null) {
|
||||
apiCall.setTotalTokens(request.getTotalTokens());
|
||||
}
|
||||
if (request.getCost() != null) {
|
||||
apiCall.setCost(request.getCost());
|
||||
}
|
||||
if (request.getFunctionCalls() != null) {
|
||||
apiCall.setFunctionCalls(request.getFunctionCalls());
|
||||
}
|
||||
if (request.getFunctionResults() != null) {
|
||||
apiCall.setFunctionResults(request.getFunctionResults());
|
||||
}
|
||||
if (request.getErrorCode() != null) {
|
||||
apiCall.setErrorCode(request.getErrorCode());
|
||||
}
|
||||
if (request.getErrorMessage() != null) {
|
||||
apiCall.setErrorMessage(request.getErrorMessage());
|
||||
}
|
||||
if (request.getClientIp() != null) {
|
||||
apiCall.setClientIp(request.getClientIp());
|
||||
}
|
||||
if (request.getUserAgent() != null) {
|
||||
apiCall.setUserAgent(request.getUserAgent());
|
||||
}
|
||||
if (request.getSessionId() != null) {
|
||||
apiCall.setSessionId(request.getSessionId());
|
||||
}
|
||||
if (request.getTraceId() != null) {
|
||||
apiCall.setTraceId(request.getTraceId());
|
||||
}
|
||||
if (request.getMetadata() != null) {
|
||||
apiCall.setMetadata(request.getMetadata());
|
||||
}
|
||||
|
||||
wrapper.orderByDesc(CozeApiCall::getStartTime);
|
||||
return this.page(page, wrapper);
|
||||
this.updateById(apiCall);
|
||||
return convertToResponse(apiCall);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CozeApiCall> getByBotId(String botId) {
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CozeApiCall::getBotId, botId)
|
||||
.eq(CozeApiCall::getIsDeleted, 0)
|
||||
.orderByDesc(CozeApiCall::getStartTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
public boolean delete(String id) {
|
||||
CozeApiCall apiCall = this.getBaseMapper().selectById(id);
|
||||
if (apiCall == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CozeApiCall> getByStatus(String status) {
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CozeApiCall::getStatus, status)
|
||||
.eq(CozeApiCall::getIsDeleted, 0)
|
||||
.orderByDesc(CozeApiCall::getStartTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CozeApiCall> getByRequestType(String requestType) {
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CozeApiCall::getRequestType, requestType)
|
||||
.eq(CozeApiCall::getIsDeleted, 0)
|
||||
.orderByDesc(CozeApiCall::getStartTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CozeApiCall> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.between(CozeApiCall::getStartTime, startTime, endTime)
|
||||
.eq(CozeApiCall::getIsDeleted, 0)
|
||||
.orderByDesc(CozeApiCall::getStartTime);
|
||||
return this.list(wrapper);
|
||||
// 逻辑删除
|
||||
apiCall.setIsDeleted(1);
|
||||
return this.updateById(apiCall);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -167,87 +321,46 @@ public class CozeApiCallServiceImpl extends ServiceImpl<CozeApiCallMapper, CozeA
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CozeApiCall> getFailedCalls() {
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.and(w -> w.eq(CozeApiCall::getStatus, "failed").or().eq(CozeApiCall::getFinalStatus, "failed"))
|
||||
.eq(CozeApiCall::getIsDeleted, 0)
|
||||
.orderByDesc(CozeApiCall::getStartTime);
|
||||
return this.list(wrapper);
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private CozeApiCallResponse convertToResponse(CozeApiCall apiCall) {
|
||||
CozeApiCallResponse response = new CozeApiCallResponse();
|
||||
BeanUtils.copyProperties(apiCall, response);
|
||||
response.setId(apiCall.getId());
|
||||
if (apiCall.getCreateTime() != null) {
|
||||
response.setCreateTime(apiCall.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (apiCall.getUpdateTime() != null) {
|
||||
response.setUpdateTime(apiCall.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (apiCall.getStartTime() != null) {
|
||||
response.setStartTime(apiCall.getStartTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (apiCall.getEndTime() != null) {
|
||||
response.setEndTime(apiCall.getEndTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (apiCall.getPollStartTime() != null) {
|
||||
response.setPollStartTime(apiCall.getPollStartTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (apiCall.getPollEndTime() != null) {
|
||||
response.setPollEndTime(apiCall.getPollEndTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CozeApiCall> getTimeoutCalls() {
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.and(w -> w.eq(CozeApiCall::getStatus, "timeout").or().eq(CozeApiCall::getFinalStatus, "timeout"))
|
||||
.eq(CozeApiCall::getIsDeleted, 0)
|
||||
.orderByDesc(CozeApiCall::getStartTime);
|
||||
return this.list(wrapper);
|
||||
/**
|
||||
* 转换分页对象为PageResult对象
|
||||
*/
|
||||
private PageResult<CozeApiCallResponse> convertPageToPageResult(Page<CozeApiCall> page) {
|
||||
PageResult<CozeApiCallResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList()));
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CozeApiCall getByTraceId(String traceId) {
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CozeApiCall::getTraceId, traceId)
|
||||
.eq(CozeApiCall::getIsDeleted, 0);
|
||||
return this.getOne(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CozeApiCall> getByConversationIdAndRequestType(String conversationId, String requestType) {
|
||||
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CozeApiCall::getConversationId, conversationId)
|
||||
.eq(CozeApiCall::getRequestType, requestType)
|
||||
.eq(CozeApiCall::getIsDeleted, 0)
|
||||
.orderByDesc(CozeApiCall::getStartTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateStatus(String id, String status, String finalStatus, LocalDateTime endTime) {
|
||||
LambdaUpdateWrapper<CozeApiCall> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(CozeApiCall::getId, id)
|
||||
.set(CozeApiCall::getStatus, status)
|
||||
.set(CozeApiCall::getFinalStatus, finalStatus)
|
||||
.set(CozeApiCall::getEndTime, endTime)
|
||||
.set(CozeApiCall::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateResult(String id, Integer responseStatus, String responseBody, String aiReply,
|
||||
Integer totalTokens, BigDecimal cost, String status, String finalStatus,
|
||||
LocalDateTime endTime) {
|
||||
LambdaUpdateWrapper<CozeApiCall> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(CozeApiCall::getId, id)
|
||||
.set(CozeApiCall::getResponseStatus, responseStatus)
|
||||
.set(CozeApiCall::getResponseBody, responseBody)
|
||||
.set(CozeApiCall::getAiReply, aiReply)
|
||||
.set(CozeApiCall::getTotalTokens, totalTokens)
|
||||
.set(CozeApiCall::getCost, cost)
|
||||
.set(CozeApiCall::getStatus, status)
|
||||
.set(CozeApiCall::getFinalStatus, finalStatus)
|
||||
.set(CozeApiCall::getEndTime, endTime)
|
||||
.set(CozeApiCall::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CozeApiCall createApiCall(String conversationId, String messageId, String userId,
|
||||
String requestType, String requestUrl, String requestBody) {
|
||||
CozeApiCall apiCall = CozeApiCall.builder()
|
||||
.conversationId(conversationId)
|
||||
.messageId(messageId)
|
||||
.userId(userId)
|
||||
.requestType(requestType)
|
||||
.requestUrl(requestUrl)
|
||||
.requestBody(requestBody)
|
||||
.status("pending")
|
||||
.startTime(LocalDateTime.now())
|
||||
.createBy(userId) // 设置创建人为当前用户
|
||||
.updateBy(userId) // 设置更新人为当前用户
|
||||
.build();
|
||||
this.save(apiCall);
|
||||
return apiCall;
|
||||
}
|
||||
}
|
||||
}
|
||||
+214
-182
@@ -5,18 +5,24 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.DiaryCommentCreateRequest;
|
||||
import com.emotion.dto.request.DiaryCommentPageRequest;
|
||||
import com.emotion.dto.response.DiaryCommentResponse;
|
||||
import com.emotion.entity.DiaryComment;
|
||||
import com.emotion.mapper.DiaryCommentMapper;
|
||||
import com.emotion.service.DiaryCommentService;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -29,207 +35,105 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, DiaryComment> implements DiaryCommentService {
|
||||
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<DiaryComment> getPage(BasePageRequest request) {
|
||||
public PageResult<DiaryCommentResponse> getPageWithResponse(DiaryCommentPageRequest request) {
|
||||
Page<DiaryComment> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryComment::getIsTop)
|
||||
|
||||
// 基础查询条件
|
||||
wrapper.eq(DiaryComment::getIsDeleted, 0);
|
||||
|
||||
// 根据请求参数添加查询条件
|
||||
if (StringUtils.hasText(request.getDiaryId())) {
|
||||
wrapper.eq(DiaryComment::getDiaryId, request.getDiaryId());
|
||||
}
|
||||
if (StringUtils.hasText(request.getUserId())) {
|
||||
wrapper.eq(DiaryComment::getUserId, request.getUserId());
|
||||
}
|
||||
if (StringUtils.hasText(request.getCommentType())) {
|
||||
wrapper.eq(DiaryComment::getCommentType, request.getCommentType());
|
||||
}
|
||||
if (request.getTopLevelOnly() != null && request.getTopLevelOnly()) {
|
||||
wrapper.isNull(DiaryComment::getParentCommentId);
|
||||
}
|
||||
|
||||
// 排序
|
||||
wrapper.orderByDesc(DiaryComment::getIsTop)
|
||||
.orderByDesc(DiaryComment::getPublishTime);
|
||||
return this.page(page, wrapper);
|
||||
|
||||
IPage<DiaryComment> resultPage = this.page(page, wrapper);
|
||||
return convertPageToResponse(resultPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<DiaryComment> getPageByDiaryId(String diaryId, BasePageRequest request) {
|
||||
Page<DiaryComment> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getDiaryId, diaryId)
|
||||
.eq(DiaryComment::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryComment::getIsTop)
|
||||
.orderByDesc(DiaryComment::getPublishTime);
|
||||
return this.page(page, wrapper);
|
||||
public DiaryCommentResponse getCommentResponseById(String id) {
|
||||
DiaryComment comment = this.getById(id);
|
||||
if (comment == null) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<DiaryComment> getPageByUserId(String userId, BasePageRequest request) {
|
||||
Page<DiaryComment> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getUserId, userId)
|
||||
.eq(DiaryComment::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryComment::getPublishTime);
|
||||
return this.page(page, wrapper);
|
||||
public List<DiaryCommentResponse> getCommentTreeWithResponse(String diaryId) {
|
||||
List<DiaryComment> comments = this.getCommentTree(diaryId);
|
||||
return comments.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiaryComment> getRepliesByParentId(String parentCommentId) {
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getParentCommentId, parentCommentId)
|
||||
.eq(DiaryComment::getIsDeleted, 0)
|
||||
.orderByAsc(DiaryComment::getPublishTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiaryComment> getByCommentType(String commentType) {
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getCommentType, commentType)
|
||||
.eq(DiaryComment::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryComment::getPublishTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiaryComment> getByDiaryIdAndCommentType(String diaryId, String commentType) {
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getDiaryId, diaryId)
|
||||
.eq(DiaryComment::getCommentType, commentType)
|
||||
.eq(DiaryComment::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryComment::getPublishTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incrementLikeCount(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.setSql("like_count = like_count + 1");
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean decrementLikeCount(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.setSql("like_count = GREATEST(like_count - 1, 0)");
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incrementReplyCount(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.setSql("reply_count = reply_count + 1");
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean decrementReplyCount(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.setSql("reply_count = GREATEST(reply_count - 1, 0)");
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateLastReplyTime(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.set(DiaryComment::getLastReplyTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTop(String commentId, Integer isTop) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.set(DiaryComment::getIsTop, isTop);
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByDiaryId(String diaryId) {
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getDiaryId, diaryId)
|
||||
.eq(DiaryComment::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByUserId(String userId) {
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getUserId, userId)
|
||||
.eq(DiaryComment::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByCommentType(String commentType) {
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getCommentType, commentType)
|
||||
.eq(DiaryComment::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countReplies(String parentCommentId) {
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getParentCommentId, parentCommentId)
|
||||
.eq(DiaryComment::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiaryComment createComment(String diaryId, String userId, String content, List<String> images,
|
||||
String parentCommentId, Integer isAnonymous) {
|
||||
public DiaryCommentResponse createCommentWithResponse(DiaryCommentCreateRequest request) {
|
||||
DiaryComment comment = DiaryComment.builder()
|
||||
.diaryId(diaryId)
|
||||
.userId(userId)
|
||||
.content(content)
|
||||
.images(convertListToJson(images))
|
||||
.parentCommentId(parentCommentId)
|
||||
.id(snowflakeIdGenerator.nextIdAsString()) // 使用雪花算法生成ID
|
||||
.diaryId(request.getDiaryId())
|
||||
.userId(request.getUserId())
|
||||
.content(request.getContent())
|
||||
.images(convertListToJson(request.getImages()))
|
||||
.parentCommentId(request.getParentCommentId())
|
||||
.commentType("user")
|
||||
.likeCount(0)
|
||||
.replyCount(0)
|
||||
.isAnonymous(isAnonymous)
|
||||
.isAnonymous(request.getIsAnonymous())
|
||||
.isTop(0)
|
||||
.status("published")
|
||||
.publishTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
|
||||
this.save(comment);
|
||||
|
||||
|
||||
// 如果有父评论,更新父评论的回复数
|
||||
if (StringUtils.hasText(parentCommentId)) {
|
||||
this.incrementReplyCount(parentCommentId);
|
||||
this.updateLastReplyTime(parentCommentId);
|
||||
if (StringUtils.hasText(request.getParentCommentId())) {
|
||||
this.incrementReplyCount(request.getParentCommentId());
|
||||
this.updateLastReplyTime(request.getParentCommentId());
|
||||
}
|
||||
|
||||
return comment;
|
||||
|
||||
return convertToResponse(comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiaryComment createAiComment(String diaryId, String content, String aiCommentSource,
|
||||
BigDecimal emotionScore, BigDecimal sentimentScore) {
|
||||
DiaryComment comment = DiaryComment.builder()
|
||||
.diaryId(diaryId)
|
||||
.userId("system") // AI评论使用system用户ID
|
||||
.content(content)
|
||||
.commentType("ai")
|
||||
.aiCommentSource(aiCommentSource)
|
||||
.likeCount(0)
|
||||
.replyCount(0)
|
||||
.isAnonymous(0)
|
||||
.isTop(0)
|
||||
.status("published")
|
||||
.publishTime(LocalDateTime.now())
|
||||
.emotionScore(emotionScore)
|
||||
.sentimentScore(sentimentScore)
|
||||
.build();
|
||||
|
||||
this.save(comment);
|
||||
return comment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateComment(String commentId, String content, List<String> images) {
|
||||
public DiaryCommentResponse updateCommentWithResponse(DiaryCommentCreateRequest request) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.set(StringUtils.hasText(content), DiaryComment::getContent, content)
|
||||
.set(images != null, DiaryComment::getImages, convertListToJson(images));
|
||||
return this.update(wrapper);
|
||||
wrapper.eq(DiaryComment::getId, request.getId())
|
||||
.set(StringUtils.hasText(request.getContent()), DiaryComment::getContent, request.getContent())
|
||||
.set(request.getImages() != null, DiaryComment::getImages, convertListToJson(request.getImages()))
|
||||
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
|
||||
|
||||
boolean updated = this.update(wrapper);
|
||||
if (!updated) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DiaryComment updatedComment = this.getById(request.getId());
|
||||
return updatedComment != null ? convertToResponse(updatedComment) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -241,7 +145,8 @@ public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, Dia
|
||||
public boolean softDeleteComment(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.set(DiaryComment::getIsDeleted, 1);
|
||||
.set(DiaryComment::getIsDeleted, 1)
|
||||
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -249,22 +154,52 @@ public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, Dia
|
||||
public boolean restoreComment(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.set(DiaryComment::getIsDeleted, 0);
|
||||
.set(DiaryComment::getIsDeleted, 0)
|
||||
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiaryComment> getCommentTree(String diaryId) {
|
||||
public boolean incrementLikeCount(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.setSql("like_count = like_count + 1")
|
||||
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean decrementLikeCount(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.setSql("like_count = GREATEST(like_count - 1, 0)")
|
||||
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTop(String commentId, Integer isTop) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.set(DiaryComment::getIsTop, isTop)
|
||||
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评论树结构
|
||||
*/
|
||||
private List<DiaryComment> getCommentTree(String diaryId) {
|
||||
// 获取所有顶级评论(没有父评论的评论)
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getDiaryId, diaryId)
|
||||
.isNull(DiaryComment::getParentCommentId)
|
||||
.eq(DiaryComment::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryComment::getIsTop)
|
||||
.orderByDesc(DiaryComment::getPublishTime);
|
||||
|
||||
.isNull(DiaryComment::getParentCommentId)
|
||||
.eq(DiaryComment::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryComment::getIsTop)
|
||||
.orderByDesc(DiaryComment::getPublishTime);
|
||||
|
||||
List<DiaryComment> topComments = this.list(wrapper);
|
||||
|
||||
|
||||
// 为每个顶级评论加载回复
|
||||
return topComments.stream()
|
||||
.peek(comment -> {
|
||||
@@ -274,6 +209,103 @@ public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, Dia
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据父评论ID查询回复列表
|
||||
*/
|
||||
private List<DiaryComment> getRepliesByParentId(String parentCommentId) {
|
||||
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryComment::getParentCommentId, parentCommentId)
|
||||
.eq(DiaryComment::getIsDeleted, 0)
|
||||
.orderByAsc(DiaryComment::getPublishTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加回复数
|
||||
*/
|
||||
private boolean incrementReplyCount(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.setSql("reply_count = reply_count + 1")
|
||||
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少回复数
|
||||
*/
|
||||
private boolean decrementReplyCount(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.setSql("reply_count = GREATEST(reply_count - 1, 0)")
|
||||
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新最后回复时间
|
||||
*/
|
||||
private boolean updateLastReplyTime(String commentId) {
|
||||
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryComment::getId, commentId)
|
||||
.set(DiaryComment::getLastReplyTime, LocalDateTime.now())
|
||||
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private DiaryCommentResponse convertToResponse(DiaryComment comment) {
|
||||
DiaryCommentResponse response = new DiaryCommentResponse();
|
||||
BeanUtils.copyProperties(comment, response);
|
||||
|
||||
// 转换时间格式
|
||||
if (comment.getPublishTime() != null) {
|
||||
response.setPublishTime(comment.getPublishTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (comment.getLastReplyTime() != null) {
|
||||
response.setLastReplyTime(comment.getLastReplyTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (comment.getCreateTime() != null) {
|
||||
response.setCreateTime(comment.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (comment.getUpdateTime() != null) {
|
||||
response.setUpdateTime(comment.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
|
||||
// 转换JSON字段
|
||||
try {
|
||||
if (comment.getImages() != null) {
|
||||
response.setImages(objectMapper.readValue(comment.getImages(), new TypeReference<List<String>>() {
|
||||
}));
|
||||
}
|
||||
if (comment.getMetadata() != null) {
|
||||
response.setMetadata(objectMapper.readValue(comment.getMetadata(), Object.class));
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
// 忽略JSON解析错误
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换分页对象为响应对象
|
||||
*/
|
||||
private PageResult<DiaryCommentResponse> convertPageToResponse(IPage<DiaryComment> page) {
|
||||
PageResult<DiaryCommentResponse> responsePage = new PageResult<>();
|
||||
responsePage.setCurrent(page.getCurrent());
|
||||
responsePage.setSize(page.getSize());
|
||||
responsePage.setTotal(page.getTotal());
|
||||
responsePage.setPages(page.getPages());
|
||||
responsePage.setRecords(
|
||||
page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList()));
|
||||
return responsePage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将List转换为JSON字符串
|
||||
*/
|
||||
@@ -287,4 +319,4 @@ public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, Dia
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,24 +5,30 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.DiaryPostCreateRequest;
|
||||
import com.emotion.dto.request.DiaryPostPageRequest;
|
||||
import com.emotion.dto.request.DiaryPostUpdateRequest;
|
||||
import com.emotion.dto.response.DiaryPostResponse;
|
||||
import com.emotion.entity.DiaryPost;
|
||||
import com.emotion.mapper.DiaryPostMapper;
|
||||
import com.emotion.service.DiaryPostService;
|
||||
import com.emotion.service.DiaryCommentService;
|
||||
import com.emotion.service.AiChatService;
|
||||
import com.emotion.dto.request.DiaryPostCreateRequest;
|
||||
import com.emotion.dto.response.DiaryPostResponse;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户日记服务实现类
|
||||
@@ -39,112 +45,153 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
private DiaryCommentService diaryCommentService;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<DiaryPost> getPage(BasePageRequest request) {
|
||||
public PageResult<DiaryPostResponse> getPageWithResponse(DiaryPostPageRequest request) {
|
||||
Page<DiaryPost> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryPost::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryPost::getPriority)
|
||||
|
||||
// 基础查询条件
|
||||
wrapper.eq(DiaryPost::getIsDeleted, 0);
|
||||
|
||||
// 根据请求参数添加查询条件
|
||||
if (StringUtils.hasText(request.getUserId())) {
|
||||
wrapper.eq(DiaryPost::getUserId, request.getUserId());
|
||||
}
|
||||
if (Boolean.TRUE.equals(request.getPublicOnly())) {
|
||||
wrapper.eq(DiaryPost::getIsPublic, 1)
|
||||
.eq(DiaryPost::getStatus, "published");
|
||||
}
|
||||
if (Boolean.TRUE.equals(request.getFeaturedOnly())) {
|
||||
wrapper.eq(DiaryPost::getFeatured, 1)
|
||||
.eq(DiaryPost::getIsPublic, 1)
|
||||
.eq(DiaryPost::getStatus, "published");
|
||||
}
|
||||
if (StringUtils.hasText(request.getMood())) {
|
||||
wrapper.eq(DiaryPost::getMood, request.getMood());
|
||||
}
|
||||
if (StringUtils.hasText(request.getTag())) {
|
||||
// 标签查询可能需要特殊处理,因为tags字段是JSON格式
|
||||
wrapper.like(DiaryPost::getTags, request.getTag());
|
||||
}
|
||||
|
||||
// 排序
|
||||
wrapper.orderByDesc(DiaryPost::getPriority)
|
||||
.orderByDesc(DiaryPost::getPublishTime);
|
||||
return this.page(page, wrapper);
|
||||
|
||||
IPage<DiaryPost> resultPage = this.page(page, wrapper);
|
||||
List<DiaryPostResponse> responses = resultPage.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return createPageResult(resultPage, responses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<DiaryPost> getPageByUserId(String userId, BasePageRequest request) {
|
||||
Page<DiaryPost> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryPost::getUserId, userId)
|
||||
.eq(DiaryPost::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryPost::getPriority)
|
||||
.orderByDesc(DiaryPost::getPublishTime);
|
||||
return this.page(page, wrapper);
|
||||
public DiaryPostResponse getDiaryPostResponseById(String id) {
|
||||
DiaryPost diaryPost = this.getById(id);
|
||||
if (diaryPost == null) {
|
||||
return null;
|
||||
}
|
||||
// 增加浏览数
|
||||
incrementViewCount(id);
|
||||
return convertToResponse(diaryPost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<DiaryPost> getPublicPageByUserId(String userId, BasePageRequest request) {
|
||||
Page<DiaryPost> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryPost::getUserId, userId)
|
||||
.eq(DiaryPost::getIsPublic, 1)
|
||||
.eq(DiaryPost::getStatus, "published")
|
||||
.eq(DiaryPost::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryPost::getPriority)
|
||||
.orderByDesc(DiaryPost::getPublishTime);
|
||||
return this.page(page, wrapper);
|
||||
public DiaryPostResponse createDiaryPostWithResponse(DiaryPostCreateRequest request) {
|
||||
// 处理标题:如果为空,则设置为null或生成默认标题
|
||||
String title = request.getTitle();
|
||||
if (title == null || title.trim().isEmpty()) {
|
||||
// 可以选择设置为null,或者生成一个默认标题
|
||||
// 这里我们设置为null,让数据库使用默认值
|
||||
title = null;
|
||||
}
|
||||
|
||||
DiaryPost diaryPost = DiaryPost.builder()
|
||||
.id(snowflakeIdGenerator.nextIdAsString()) // 使用雪花算法生成ID
|
||||
.userId(request.getUserId())
|
||||
.title(title)
|
||||
.content(request.getContent())
|
||||
.images(convertListToJson(request.getImages()))
|
||||
.videos(convertListToJson(request.getVideos()))
|
||||
.location(request.getLocation())
|
||||
.weather(request.getWeather())
|
||||
.mood(request.getMood())
|
||||
.tags(convertListToJson(request.getTags()))
|
||||
.isPublic(request.getIsPublic())
|
||||
.isAnonymous(request.getIsAnonymous())
|
||||
.viewCount(0)
|
||||
.likeCount(0)
|
||||
.commentCount(0)
|
||||
.shareCount(0)
|
||||
.publishTime(LocalDateTime.now())
|
||||
.status("published")
|
||||
.priority(0)
|
||||
.featured(0)
|
||||
.build();
|
||||
|
||||
this.save(diaryPost);
|
||||
return convertToResponse(diaryPost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<DiaryPost> getFeaturedPage(BasePageRequest request) {
|
||||
Page<DiaryPost> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryPost::getFeatured, 1)
|
||||
.eq(DiaryPost::getIsPublic, 1)
|
||||
.eq(DiaryPost::getStatus, "published")
|
||||
.eq(DiaryPost::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryPost::getPriority)
|
||||
.orderByDesc(DiaryPost::getPublishTime);
|
||||
return this.page(page, wrapper);
|
||||
public DiaryPostResponse updateDiaryPostWithResponse(DiaryPostUpdateRequest request) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, request.getId())
|
||||
.set(StringUtils.hasText(request.getTitle()), DiaryPost::getTitle, request.getTitle())
|
||||
.set(StringUtils.hasText(request.getContent()), DiaryPost::getContent, request.getContent())
|
||||
.set(request.getImages() != null, DiaryPost::getImages, convertListToJson(request.getImages()))
|
||||
.set(request.getVideos() != null, DiaryPost::getVideos, convertListToJson(request.getVideos()))
|
||||
.set(StringUtils.hasText(request.getLocation()), DiaryPost::getLocation, request.getLocation())
|
||||
.set(StringUtils.hasText(request.getWeather()), DiaryPost::getWeather, request.getWeather())
|
||||
.set(StringUtils.hasText(request.getMood()), DiaryPost::getMood, request.getMood())
|
||||
.set(request.getTags() != null, DiaryPost::getTags, convertListToJson(request.getTags()))
|
||||
.set(request.getIsPublic() != null, DiaryPost::getIsPublic, request.getIsPublic())
|
||||
.set(request.getIsAnonymous() != null, DiaryPost::getIsAnonymous, request.getIsAnonymous())
|
||||
.set(StringUtils.hasText(request.getStatus()), DiaryPost::getStatus, request.getStatus())
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
|
||||
boolean updated = this.update(wrapper);
|
||||
if (!updated) {
|
||||
return null;
|
||||
}
|
||||
DiaryPost updatedDiaryPost = this.getById(request.getId());
|
||||
return convertToResponse(updatedDiaryPost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiaryPost> getByStatus(String status) {
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryPost::getStatus, status)
|
||||
.eq(DiaryPost::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryPost::getPublishTime);
|
||||
return this.list(wrapper);
|
||||
public boolean deleteDiaryPost(String diaryId) {
|
||||
return this.removeById(diaryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiaryPost> getByUserIdAndStatus(String userId, String status) {
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryPost::getUserId, userId)
|
||||
.eq(DiaryPost::getStatus, status)
|
||||
.eq(DiaryPost::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryPost::getPublishTime);
|
||||
return this.list(wrapper);
|
||||
public boolean softDeleteDiaryPost(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(DiaryPost::getIsDeleted, 1)
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiaryPost> getByMood(String mood) {
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryPost::getMood, mood)
|
||||
.eq(DiaryPost::getIsPublic, 1)
|
||||
.eq(DiaryPost::getStatus, "published")
|
||||
.eq(DiaryPost::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryPost::getPublishTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiaryPost> getByTags(List<String> tags) {
|
||||
// 这里需要根据实际需求实现标签查询逻辑
|
||||
// 由于tags字段是JSON格式,可能需要使用数据库的JSON查询功能
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryPost::getIsPublic, 1)
|
||||
.eq(DiaryPost::getStatus, "published")
|
||||
.eq(DiaryPost::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryPost::getPublishTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiaryPost> getByLocation(String location) {
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.like(DiaryPost::getLocation, location)
|
||||
.eq(DiaryPost::getIsPublic, 1)
|
||||
.eq(DiaryPost::getStatus, "published")
|
||||
.eq(DiaryPost::getIsDeleted, 0)
|
||||
.orderByDesc(DiaryPost::getPublishTime);
|
||||
return this.list(wrapper);
|
||||
public boolean restoreDiaryPost(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(DiaryPost::getIsDeleted, 0)
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incrementViewCount(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.setSql("view_count = view_count + 1");
|
||||
.setSql("view_count = view_count + 1")
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -152,7 +199,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
public boolean incrementLikeCount(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.setSql("like_count = like_count + 1");
|
||||
.setSql("like_count = like_count + 1")
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -160,7 +208,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
public boolean decrementLikeCount(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.setSql("like_count = GREATEST(like_count - 1, 0)");
|
||||
.setSql("like_count = GREATEST(like_count - 1, 0)")
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -168,7 +217,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
public boolean incrementCommentCount(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.setSql("comment_count = comment_count + 1");
|
||||
.setSql("comment_count = comment_count + 1")
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -176,7 +226,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
public boolean decrementCommentCount(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.setSql("comment_count = GREATEST(comment_count - 1, 0)");
|
||||
.setSql("comment_count = GREATEST(comment_count - 1, 0)")
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -184,7 +235,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
public boolean incrementShareCount(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.setSql("share_count = share_count + 1");
|
||||
.setSql("share_count = share_count + 1")
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -192,7 +244,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
public boolean updateLastCommentTime(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(DiaryPost::getLastCommentTime, LocalDateTime.now());
|
||||
.set(DiaryPost::getLastCommentTime, LocalDateTime.now())
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -200,7 +253,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
public boolean setFeatured(String diaryId, Integer featured) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(DiaryPost::getFeatured, featured);
|
||||
.set(DiaryPost::getFeatured, featured)
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -208,7 +262,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
public boolean setPriority(String diaryId, Integer priority) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(DiaryPost::getPriority, priority);
|
||||
.set(DiaryPost::getPriority, priority)
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@@ -239,26 +294,12 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByStatus(String status) {
|
||||
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(DiaryPost::getStatus, status)
|
||||
.eq(DiaryPost::getIsDeleted, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiaryPost createDiaryPost(com.emotion.dto.request.DiaryPostCreateRequest request) {
|
||||
// 处理标题:如果为空,则设置为null或生成默认标题
|
||||
String title = request.getTitle();
|
||||
if (title == null || title.trim().isEmpty()) {
|
||||
// 可以选择设置为null,或者生成一个默认标题
|
||||
// 这里我们设置为null,让数据库使用默认值
|
||||
title = null;
|
||||
}
|
||||
|
||||
public DiaryPostResponse publishDiaryWithAiComment(DiaryPostCreateRequest request) {
|
||||
// 1. 保存日记
|
||||
DiaryPost diaryPost = DiaryPost.builder()
|
||||
.id(snowflakeIdGenerator.nextIdAsString()) // 使用雪花算法生成ID
|
||||
.userId(request.getUserId())
|
||||
.title(title)
|
||||
.title(request.getTitle())
|
||||
.content(request.getContent())
|
||||
.images(convertListToJson(request.getImages()))
|
||||
.videos(convertListToJson(request.getVideos()))
|
||||
@@ -277,69 +318,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
.priority(0)
|
||||
.featured(0)
|
||||
.build();
|
||||
|
||||
this.save(diaryPost);
|
||||
return diaryPost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateDiaryPost(String diaryId, com.emotion.dto.request.DiaryPostUpdateRequest request) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(StringUtils.hasText(request.getTitle()), DiaryPost::getTitle, request.getTitle())
|
||||
.set(StringUtils.hasText(request.getContent()), DiaryPost::getContent, request.getContent())
|
||||
.set(request.getImages() != null, DiaryPost::getImages, convertListToJson(request.getImages()))
|
||||
.set(request.getVideos() != null, DiaryPost::getVideos, convertListToJson(request.getVideos()))
|
||||
.set(StringUtils.hasText(request.getLocation()), DiaryPost::getLocation, request.getLocation())
|
||||
.set(StringUtils.hasText(request.getWeather()), DiaryPost::getWeather, request.getWeather())
|
||||
.set(StringUtils.hasText(request.getMood()), DiaryPost::getMood, request.getMood())
|
||||
.set(request.getTags() != null, DiaryPost::getTags, convertListToJson(request.getTags()))
|
||||
.set(request.getIsPublic() != null, DiaryPost::getIsPublic, request.getIsPublic())
|
||||
.set(request.getIsAnonymous() != null, DiaryPost::getIsAnonymous, request.getIsAnonymous())
|
||||
.set(StringUtils.hasText(request.getStatus()), DiaryPost::getStatus, request.getStatus());
|
||||
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteDiaryPost(String diaryId) {
|
||||
return this.removeById(diaryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean softDeleteDiaryPost(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(DiaryPost::getIsDeleted, 1);
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restoreDiaryPost(String diaryId) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(DiaryPost::getIsDeleted, 0);
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAiComment(String diaryId, String aiComment, Object aiEmotionAnalysis,
|
||||
BigDecimal aiSentimentScore, List<String> aiKeywords, String aiSuggestions) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(DiaryPost::getAiComment, aiComment)
|
||||
.set(DiaryPost::getAiCommentTime, LocalDateTime.now())
|
||||
.set(DiaryPost::getAiEmotionAnalysis, convertObjectToJson(aiEmotionAnalysis))
|
||||
.set(DiaryPost::getAiSentimentScore, aiSentimentScore)
|
||||
.set(DiaryPost::getAiKeywords, convertListToJson(aiKeywords))
|
||||
.set(DiaryPost::getAiSuggestions, aiSuggestions);
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.emotion.dto.response.DiaryPostResponse publishDiaryWithAiComment(com.emotion.dto.request.DiaryPostCreateRequest request) {
|
||||
// 1. 保存日记
|
||||
DiaryPost diaryPost = createDiaryPost(request);
|
||||
// 2. 生成AI评论
|
||||
String aiComment = null;
|
||||
try {
|
||||
@@ -350,13 +330,14 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
}
|
||||
// 3. 写入AI评论到diary_comment表
|
||||
if (aiComment != null) {
|
||||
diaryCommentService.createAiComment(
|
||||
diaryPost.getId(),
|
||||
aiComment,
|
||||
"diary_ai_summary",
|
||||
null,
|
||||
null
|
||||
);
|
||||
// 使用createCommentWithResponse方法创建AI评论
|
||||
com.emotion.dto.request.DiaryCommentCreateRequest commentRequest = new com.emotion.dto.request.DiaryCommentCreateRequest();
|
||||
commentRequest.setDiaryId(diaryPost.getId());
|
||||
commentRequest.setUserId("system"); // AI评论使用system用户ID
|
||||
commentRequest.setContent(aiComment);
|
||||
commentRequest.setIsAnonymous(0);
|
||||
diaryCommentService.createCommentWithResponse(commentRequest);
|
||||
|
||||
addAiComment(
|
||||
diaryPost.getId(),
|
||||
aiComment,
|
||||
@@ -367,53 +348,37 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
);
|
||||
}
|
||||
// 4. 返回日记详情(含AI评论)
|
||||
com.emotion.dto.response.DiaryPostResponse response = new com.emotion.dto.response.DiaryPostResponse();
|
||||
org.springframework.beans.BeanUtils.copyProperties(diaryPost, response);
|
||||
// 查询AI评论
|
||||
List<com.emotion.entity.DiaryComment> aiComments = diaryCommentService.getByDiaryIdAndCommentType(diaryPost.getId(), "ai");
|
||||
if (!aiComments.isEmpty()) {
|
||||
response.setAiComment(aiComments.get(0).getContent());
|
||||
}
|
||||
// 转换时间格式
|
||||
if (diaryPost.getPublishTime() != null) {
|
||||
response.setPublishTime(diaryPost.getPublishTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
}
|
||||
if (diaryPost.getLastCommentTime() != null) {
|
||||
response.setLastCommentTime(diaryPost.getLastCommentTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
}
|
||||
if (diaryPost.getAiCommentTime() != null) {
|
||||
response.setAiCommentTime(diaryPost.getAiCommentTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
}
|
||||
if (diaryPost.getCreateTime() != null) {
|
||||
response.setCreateTime(diaryPost.getCreateTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
}
|
||||
if (diaryPost.getUpdateTime() != null) {
|
||||
response.setUpdateTime(diaryPost.getUpdateTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
}
|
||||
// 转换JSON字段
|
||||
try {
|
||||
if (diaryPost.getImages() != null) {
|
||||
response.setImages(objectMapper.readValue(diaryPost.getImages(), new com.fasterxml.jackson.core.type.TypeReference<List<String>>() {}));
|
||||
}
|
||||
if (diaryPost.getVideos() != null) {
|
||||
response.setVideos(objectMapper.readValue(diaryPost.getVideos(), new com.fasterxml.jackson.core.type.TypeReference<List<String>>() {}));
|
||||
}
|
||||
if (diaryPost.getTags() != null) {
|
||||
response.setTags(objectMapper.readValue(diaryPost.getTags(), new com.fasterxml.jackson.core.type.TypeReference<List<String>>() {}));
|
||||
}
|
||||
if (diaryPost.getAiKeywords() != null) {
|
||||
response.setAiKeywords(objectMapper.readValue(diaryPost.getAiKeywords(), new com.fasterxml.jackson.core.type.TypeReference<List<String>>() {}));
|
||||
}
|
||||
if (diaryPost.getAiEmotionAnalysis() != null) {
|
||||
response.setAiEmotionAnalysis(objectMapper.readValue(diaryPost.getAiEmotionAnalysis(), Object.class));
|
||||
}
|
||||
if (diaryPost.getMetadata() != null) {
|
||||
response.setMetadata(objectMapper.readValue(diaryPost.getMetadata(), Object.class));
|
||||
}
|
||||
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
|
||||
// 忽略JSON解析错误
|
||||
}
|
||||
return response;
|
||||
return convertToResponse(diaryPost);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加AI评论
|
||||
*/
|
||||
private boolean addAiComment(String diaryId, String aiComment, Object aiEmotionAnalysis,
|
||||
BigDecimal aiSentimentScore, List<String> aiKeywords, String aiSuggestions) {
|
||||
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(DiaryPost::getId, diaryId)
|
||||
.set(DiaryPost::getAiComment, aiComment)
|
||||
.set(DiaryPost::getAiCommentTime, LocalDateTime.now())
|
||||
.set(DiaryPost::getAiEmotionAnalysis, convertObjectToJson(aiEmotionAnalysis))
|
||||
.set(DiaryPost::getAiSentimentScore, aiSentimentScore)
|
||||
.set(DiaryPost::getAiKeywords, convertListToJson(aiKeywords))
|
||||
.set(DiaryPost::getAiSuggestions, aiSuggestions)
|
||||
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建PageResult对象
|
||||
*/
|
||||
private <T> PageResult<T> createPageResult(IPage<?> page, List<T> records) {
|
||||
PageResult<T> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(records);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,4 +408,60 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换实体为响应DTO
|
||||
*/
|
||||
private DiaryPostResponse convertToResponse(DiaryPost diaryPost) {
|
||||
DiaryPostResponse response = new DiaryPostResponse();
|
||||
BeanUtils.copyProperties(diaryPost, response);
|
||||
|
||||
// 转换时间格式
|
||||
if (diaryPost.getPublishTime() != null) {
|
||||
response.setPublishTime(diaryPost.getPublishTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (diaryPost.getLastCommentTime() != null) {
|
||||
response.setLastCommentTime(diaryPost.getLastCommentTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (diaryPost.getAiCommentTime() != null) {
|
||||
response.setAiCommentTime(diaryPost.getAiCommentTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (diaryPost.getCreateTime() != null) {
|
||||
response.setCreateTime(diaryPost.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (diaryPost.getUpdateTime() != null) {
|
||||
response.setUpdateTime(diaryPost.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
|
||||
// 转换JSON字段
|
||||
try {
|
||||
if (diaryPost.getImages() != null) {
|
||||
response.setImages(objectMapper.readValue(diaryPost.getImages(), new TypeReference<List<String>>() {
|
||||
}));
|
||||
}
|
||||
if (diaryPost.getVideos() != null) {
|
||||
response.setVideos(objectMapper.readValue(diaryPost.getVideos(), new TypeReference<List<String>>() {
|
||||
}));
|
||||
}
|
||||
if (diaryPost.getTags() != null) {
|
||||
response.setTags(objectMapper.readValue(diaryPost.getTags(), new TypeReference<List<String>>() {
|
||||
}));
|
||||
}
|
||||
if (diaryPost.getAiKeywords() != null) {
|
||||
response.setAiKeywords(
|
||||
objectMapper.readValue(diaryPost.getAiKeywords(), new TypeReference<List<String>>() {
|
||||
}));
|
||||
}
|
||||
if (diaryPost.getAiEmotionAnalysis() != null) {
|
||||
response.setAiEmotionAnalysis(objectMapper.readValue(diaryPost.getAiEmotionAnalysis(), Object.class));
|
||||
}
|
||||
if (diaryPost.getMetadata() != null) {
|
||||
response.setMetadata(objectMapper.readValue(diaryPost.getMetadata(), Object.class));
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
// 忽略JSON解析错误
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+174
@@ -5,15 +5,25 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.EmotionAnalysisCreateRequest;
|
||||
import com.emotion.dto.request.EmotionAnalysisPageRequest;
|
||||
import com.emotion.dto.request.EmotionAnalysisUpdateRequest;
|
||||
import com.emotion.dto.response.EmotionAnalysisResponse;
|
||||
import com.emotion.entity.EmotionAnalysis;
|
||||
import com.emotion.mapper.EmotionAnalysisMapper;
|
||||
import com.emotion.service.EmotionAnalysisService;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 情绪分析服务实现类
|
||||
@@ -24,6 +34,11 @@ import java.util.List;
|
||||
@Service
|
||||
public class EmotionAnalysisServiceImpl extends ServiceImpl<EmotionAnalysisMapper, EmotionAnalysis> implements EmotionAnalysisService {
|
||||
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<EmotionAnalysis> getPage(BasePageRequest request) {
|
||||
Page<EmotionAnalysis> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
@@ -173,6 +188,7 @@ public class EmotionAnalysisServiceImpl extends ServiceImpl<EmotionAnalysisMappe
|
||||
public EmotionAnalysis createEmotionAnalysis(String messageId, String userId, String primaryEmotion,
|
||||
String polarity, Double intensity, Double confidence) {
|
||||
EmotionAnalysis analysis = new EmotionAnalysis();
|
||||
analysis.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
|
||||
analysis.setMessageId(messageId);
|
||||
analysis.setCreateBy(userId);
|
||||
analysis.setPrimaryEmotion(primaryEmotion);
|
||||
@@ -183,4 +199,162 @@ public class EmotionAnalysisServiceImpl extends ServiceImpl<EmotionAnalysisMappe
|
||||
this.save(analysis);
|
||||
return analysis;
|
||||
}
|
||||
|
||||
// 新增的方法实现
|
||||
|
||||
@Override
|
||||
public PageResult<EmotionAnalysisResponse> getPageWithResponse(EmotionAnalysisPageRequest request) {
|
||||
IPage<EmotionAnalysis> page = getPage(request);
|
||||
List<EmotionAnalysisResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return createPageResult(page, responses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<EmotionAnalysisResponse> getPageByUserIdWithResponse(String userId,
|
||||
EmotionAnalysisPageRequest request) {
|
||||
IPage<EmotionAnalysis> page = getPageByUserId(request, userId);
|
||||
List<EmotionAnalysisResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
return createPageResult(page, responses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmotionAnalysisResponse getEmotionAnalysisResponseById(String id) {
|
||||
EmotionAnalysis analysis = this.getById(id);
|
||||
if (analysis == null) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(analysis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmotionAnalysisResponse getEmotionAnalysisResponseByMessageId(String messageId) {
|
||||
EmotionAnalysis analysis = getByMessageId(messageId);
|
||||
if (analysis == null) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(analysis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmotionAnalysisResponse createEmotionAnalysisWithResponse(EmotionAnalysisCreateRequest request) {
|
||||
EmotionAnalysis analysis = createEmotionAnalysis(
|
||||
request.getMessageId(),
|
||||
request.getUserId(),
|
||||
request.getPrimaryEmotion(),
|
||||
request.getPolarity(),
|
||||
request.getIntensity(),
|
||||
request.getConfidence());
|
||||
return convertToResponse(analysis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmotionAnalysisResponse updateEmotionAnalysisWithResponse(EmotionAnalysisUpdateRequest request) {
|
||||
EmotionAnalysis analysis = this.getById(request.getId());
|
||||
if (analysis == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if (request.getMessageId() != null) {
|
||||
analysis.setMessageId(request.getMessageId());
|
||||
}
|
||||
if (request.getUserId() != null) {
|
||||
analysis.setCreateBy(request.getUserId());
|
||||
}
|
||||
if (request.getPrimaryEmotion() != null) {
|
||||
analysis.setPrimaryEmotion(request.getPrimaryEmotion());
|
||||
}
|
||||
if (request.getPolarity() != null) {
|
||||
analysis.setPolarity(request.getPolarity());
|
||||
}
|
||||
if (request.getIntensity() != null) {
|
||||
analysis.setIntensity(BigDecimal.valueOf(request.getIntensity()));
|
||||
}
|
||||
if (request.getConfidence() != null) {
|
||||
analysis.setConfidence(BigDecimal.valueOf(request.getConfidence()));
|
||||
}
|
||||
|
||||
this.updateById(analysis);
|
||||
return convertToResponse(analysis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteEmotionAnalysis(String id) {
|
||||
return this.removeById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByPrimaryEmotion(String primaryEmotion) {
|
||||
List<EmotionAnalysis> analyses = getByPrimaryEmotion(primaryEmotion);
|
||||
return analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByPolarity(String polarity) {
|
||||
List<EmotionAnalysis> analyses = getByPolarity(polarity);
|
||||
return analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByUserIdAndEmotion(String userId,
|
||||
String primaryEmotion) {
|
||||
List<EmotionAnalysis> analyses = getByUserIdAndEmotion(userId, primaryEmotion);
|
||||
return analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByUserIdAndTimeRange(String userId,
|
||||
LocalDateTime startTime, LocalDateTime endTime) {
|
||||
List<EmotionAnalysis> analyses = getByUserIdAndTimeRange(userId, startTime, endTime);
|
||||
return analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesRecentByUserId(String userId, Integer limit) {
|
||||
List<EmotionAnalysis> analyses = getRecentByUserId(userId, limit);
|
||||
return analyses.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建PageResult对象
|
||||
*/
|
||||
private <T> PageResult<T> createPageResult(IPage<?> page, List<T> records) {
|
||||
PageResult<T> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(records);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private EmotionAnalysisResponse convertToResponse(EmotionAnalysis analysis) {
|
||||
EmotionAnalysisResponse response = new EmotionAnalysisResponse();
|
||||
BeanUtils.copyProperties(analysis, response);
|
||||
response.setId(analysis.getId());
|
||||
if (analysis.getCreateTime() != null) {
|
||||
response.setCreateTime(analysis.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (analysis.getUpdateTime() != null) {
|
||||
response.setUpdateTime(analysis.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+282
-6
@@ -5,17 +5,29 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.request.EmotionRecordCreateRequest;
|
||||
import com.emotion.dto.request.EmotionRecordPageRequest;
|
||||
import com.emotion.dto.request.EmotionRecordUpdateRequest;
|
||||
import com.emotion.dto.response.EmotionRecordResponse;
|
||||
import com.emotion.entity.EmotionRecord;
|
||||
import com.emotion.mapper.EmotionRecordMapper;
|
||||
import com.emotion.service.EmotionRecordService;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 情绪记录服务实现类
|
||||
@@ -26,6 +38,11 @@ import java.util.Collections;
|
||||
@Service
|
||||
public class EmotionRecordServiceImpl extends ServiceImpl<EmotionRecordMapper, EmotionRecord> implements EmotionRecordService {
|
||||
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<EmotionRecord> getPage(BasePageRequest request) {
|
||||
Page<EmotionRecord> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
@@ -134,20 +151,59 @@ public class EmotionRecordServiceImpl extends ServiceImpl<EmotionRecordMapper, E
|
||||
|
||||
@Override
|
||||
public Double getAvgIntensityByUserId(String userId) {
|
||||
// 这里需要自定义SQL查询平均值,暂时返回0
|
||||
return 0.0;
|
||||
LambdaQueryWrapper<EmotionRecord> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(EmotionRecord::getUserId, userId)
|
||||
.eq(EmotionRecord::getIsDeleted, 0);
|
||||
|
||||
List<EmotionRecord> records = this.list(wrapper);
|
||||
if (records.isEmpty()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double sum = records.stream()
|
||||
.mapToDouble(record -> record.getIntensity().doubleValue())
|
||||
.sum();
|
||||
|
||||
return sum / records.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAvgIntensityByUserIdAndTimeRange(String userId, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
// 这里需要自定义SQL查询平均值,暂时返回0
|
||||
return 0.0;
|
||||
LambdaQueryWrapper<EmotionRecord> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(EmotionRecord::getUserId, userId)
|
||||
.between(EmotionRecord::getCreateTime, startTime, endTime)
|
||||
.eq(EmotionRecord::getIsDeleted, 0);
|
||||
|
||||
List<EmotionRecord> records = this.list(wrapper);
|
||||
if (records.isEmpty()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double sum = records.stream()
|
||||
.mapToDouble(record -> record.getIntensity().doubleValue())
|
||||
.sum();
|
||||
|
||||
return sum / records.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMostFrequentEmotionByUserId(String userId) {
|
||||
// 这里需要自定义SQL查询最常见的情绪类型,暂时返回null
|
||||
return null;
|
||||
LambdaQueryWrapper<EmotionRecord> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(EmotionRecord::getUserId, userId)
|
||||
.eq(EmotionRecord::getIsDeleted, 0);
|
||||
|
||||
List<EmotionRecord> records = this.list(wrapper);
|
||||
if (records.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return records.stream()
|
||||
.collect(Collectors.groupingBy(EmotionRecord::getEmotionType, Collectors.counting()))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.max(Map.Entry.comparingByValue())
|
||||
.map(Map.Entry::getKey)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,6 +237,7 @@ public class EmotionRecordServiceImpl extends ServiceImpl<EmotionRecordMapper, E
|
||||
public EmotionRecord createEmotionRecord(String userId, String emotionType, Double intensity,
|
||||
String trigger, String location, String notes) {
|
||||
EmotionRecord record = new EmotionRecord();
|
||||
record.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
|
||||
record.setUserId(userId);
|
||||
record.setEmotionType(emotionType);
|
||||
record.setIntensity(BigDecimal.valueOf(intensity));
|
||||
@@ -204,4 +261,223 @@ public class EmotionRecordServiceImpl extends ServiceImpl<EmotionRecordMapper, E
|
||||
|
||||
return this.page(page, wrapper);
|
||||
}
|
||||
|
||||
// 新增的方法实现
|
||||
|
||||
@Override
|
||||
public PageResult<EmotionRecordResponse> getPageWithResponse(EmotionRecordPageRequest request) {
|
||||
IPage<EmotionRecord> page = getPage(request);
|
||||
List<EmotionRecordResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
PageResult<EmotionRecordResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<EmotionRecordResponse> getPageByUserIdWithResponse(String userId, EmotionRecordPageRequest request) {
|
||||
IPage<EmotionRecord> page = getPageByUserId(request, userId);
|
||||
List<EmotionRecordResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
PageResult<EmotionRecordResponse> pageResult = new PageResult<>();
|
||||
pageResult.setCurrent(page.getCurrent());
|
||||
pageResult.setSize(page.getSize());
|
||||
pageResult.setTotal(page.getTotal());
|
||||
pageResult.setPages(page.getPages());
|
||||
pageResult.setRecords(responses);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmotionRecordResponse getEmotionRecordResponseById(String id) {
|
||||
EmotionRecord record = this.getById(id);
|
||||
if (record == null) {
|
||||
return null;
|
||||
}
|
||||
return convertToResponse(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmotionRecordResponse createEmotionRecordWithResponse(EmotionRecordCreateRequest request) {
|
||||
EmotionRecord record = new EmotionRecord();
|
||||
record.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
|
||||
record.setUserId(request.getUserId());
|
||||
record.setRecordDate(request.getRecordDate());
|
||||
record.setEmotionType(request.getEmotionType());
|
||||
record.setIntensity(request.getIntensity());
|
||||
record.setTriggers(request.getTriggers());
|
||||
record.setDescription(request.getDescription());
|
||||
// 注意:tags字段在实体类中是String类型,需要转换
|
||||
record.setTags(request.getTags() != null ? request.getTags().toString() : null);
|
||||
record.setWeather(request.getWeather());
|
||||
record.setLocation(request.getLocation());
|
||||
record.setActivity(request.getActivity());
|
||||
record.setPeople(request.getPeople());
|
||||
record.setNotes(request.getNotes());
|
||||
|
||||
this.save(record);
|
||||
return convertToResponse(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmotionRecordResponse updateEmotionRecordWithResponse(EmotionRecordUpdateRequest request) {
|
||||
EmotionRecord record = this.getById(request.getId());
|
||||
if (record == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if (request.getRecordDate() != null) {
|
||||
record.setRecordDate(request.getRecordDate());
|
||||
}
|
||||
if (request.getEmotionType() != null) {
|
||||
record.setEmotionType(request.getEmotionType());
|
||||
}
|
||||
if (request.getIntensity() != null) {
|
||||
record.setIntensity(request.getIntensity());
|
||||
}
|
||||
if (request.getTriggers() != null) {
|
||||
record.setTriggers(request.getTriggers());
|
||||
}
|
||||
if (request.getDescription() != null) {
|
||||
record.setDescription(request.getDescription());
|
||||
}
|
||||
// 注意:tags字段在实体类中是String类型,需要转换
|
||||
if (request.getTags() != null) {
|
||||
record.setTags(request.getTags().toString());
|
||||
}
|
||||
if (request.getWeather() != null) {
|
||||
record.setWeather(request.getWeather());
|
||||
}
|
||||
if (request.getLocation() != null) {
|
||||
record.setLocation(request.getLocation());
|
||||
}
|
||||
if (request.getActivity() != null) {
|
||||
record.setActivity(request.getActivity());
|
||||
}
|
||||
if (request.getPeople() != null) {
|
||||
record.setPeople(request.getPeople());
|
||||
}
|
||||
if (request.getNotes() != null) {
|
||||
record.setNotes(request.getNotes());
|
||||
}
|
||||
|
||||
this.updateById(record);
|
||||
return convertToResponse(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteEmotionRecord(String id) {
|
||||
return this.removeById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getEmotionStats(String userId, String startDate, String endDate) {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<EmotionRecord> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(EmotionRecord::getUserId, userId)
|
||||
.eq(EmotionRecord::getIsDeleted, 0);
|
||||
|
||||
// 如果提供了日期范围,则添加时间条件
|
||||
if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
|
||||
try {
|
||||
LocalDateTime start = LocalDate.parse(startDate).atStartOfDay();
|
||||
LocalDateTime end = LocalDate.parse(endDate).atTime(23, 59, 59);
|
||||
wrapper.between(EmotionRecord::getCreateTime, start, end);
|
||||
} catch (Exception e) {
|
||||
// 如果日期解析失败,忽略日期条件
|
||||
}
|
||||
}
|
||||
|
||||
// 查询用户的所有情绪记录
|
||||
List<EmotionRecord> records = this.list(wrapper);
|
||||
|
||||
if (records.isEmpty()) {
|
||||
stats.put("emotionDistribution", new HashMap<>());
|
||||
stats.put("totalRecords", 0);
|
||||
stats.put("averageIntensity", 0.0);
|
||||
stats.put("mostFrequentEmotion", null);
|
||||
stats.put("emotionTrend", "no_data");
|
||||
return stats;
|
||||
}
|
||||
|
||||
// 情绪类型分布
|
||||
Map<String, Long> emotionDistribution = records.stream()
|
||||
.collect(Collectors.groupingBy(EmotionRecord::getEmotionType, Collectors.counting()));
|
||||
|
||||
stats.put("emotionDistribution", emotionDistribution);
|
||||
stats.put("totalRecords", records.size());
|
||||
|
||||
// 平均情绪强度
|
||||
double avgIntensity = records.stream()
|
||||
.mapToDouble(record -> record.getIntensity().doubleValue())
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
stats.put("averageIntensity", avgIntensity);
|
||||
|
||||
// 最常见的情绪类型
|
||||
String mostFrequentEmotion = emotionDistribution.entrySet().stream()
|
||||
.max(Map.Entry.comparingByValue())
|
||||
.map(Map.Entry::getKey)
|
||||
.orElse(null);
|
||||
stats.put("mostFrequentEmotion", mostFrequentEmotion);
|
||||
|
||||
// 情绪趋势(简单实现:比较前半段和后半段的平均强度)
|
||||
if (records.size() > 1) {
|
||||
int mid = records.size() / 2;
|
||||
double firstHalfAvg = records.subList(0, mid).stream()
|
||||
.mapToDouble(record -> record.getIntensity().doubleValue())
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
|
||||
double secondHalfAvg = records.subList(mid, records.size()).stream()
|
||||
.mapToDouble(record -> record.getIntensity().doubleValue())
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
|
||||
if (secondHalfAvg > firstHalfAvg) {
|
||||
stats.put("emotionTrend", "improving");
|
||||
} else if (secondHalfAvg < firstHalfAvg) {
|
||||
stats.put("emotionTrend", "declining");
|
||||
} else {
|
||||
stats.put("emotionTrend", "stable");
|
||||
}
|
||||
} else {
|
||||
stats.put("emotionTrend", "insufficient_data");
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmotionRecordResponse> getRecentByUserIdWithResponse(String userId, Integer limit) {
|
||||
List<EmotionRecord> records = getRecentByUserId(userId, limit);
|
||||
return records.stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应对象
|
||||
*/
|
||||
private EmotionRecordResponse convertToResponse(EmotionRecord record) {
|
||||
EmotionRecordResponse response = new EmotionRecordResponse();
|
||||
BeanUtils.copyProperties(record, response);
|
||||
response.setId(record.getId());
|
||||
if (record.getCreateTime() != null) {
|
||||
response.setCreateTime(record.getCreateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
if (record.getUpdateTime() != null) {
|
||||
response.setUpdateTime(record.getUpdateTime().format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,13 @@ import com.emotion.dto.response.UserInfoResponse;
|
||||
import com.emotion.exception.TokenException;
|
||||
import com.emotion.service.AuthService;
|
||||
import com.emotion.service.TokenService;
|
||||
import com.emotion.util.TokenUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 令牌服务实现类
|
||||
*
|
||||
@@ -20,15 +23,19 @@ public class TokenServiceImpl implements TokenService {
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
@Autowired
|
||||
private TokenUtil tokenUtil;
|
||||
|
||||
@Override
|
||||
public UserInfoResponse getUserInfoByToken(String token) {
|
||||
String userId = validateTokenAndGetUserId(token);
|
||||
public UserInfoResponse getUserInfoByToken(HttpServletRequest request) {
|
||||
String userId = validateTokenAndGetUserId(request);
|
||||
return authService.getCurrentUserInfo(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsernameByToken(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
public String getUsernameByToken(HttpServletRequest request) {
|
||||
String token = tokenUtil.extractToken(request);
|
||||
if (!tokenUtil.isValidToken(token)) {
|
||||
throw new TokenException("未提供访问令牌");
|
||||
}
|
||||
|
||||
@@ -45,8 +52,9 @@ public class TokenServiceImpl implements TokenService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String validateTokenAndGetUserId(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
public String validateTokenAndGetUserId(HttpServletRequest request) {
|
||||
String token = tokenUtil.extractToken(request);
|
||||
if (!tokenUtil.isValidToken(token)) {
|
||||
throw new TokenException("未提供访问令牌");
|
||||
}
|
||||
|
||||
@@ -61,4 +69,4 @@ public class TokenServiceImpl implements TokenService {
|
||||
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.emotion.service.impl;
|
||||
|
||||
import com.emotion.dto.request.WebSocketRequest;
|
||||
import com.emotion.dto.websocket.ChatRequest;
|
||||
import com.emotion.dto.websocket.ConnectRequest;
|
||||
import com.emotion.dto.websocket.WebSocketMessage;
|
||||
@@ -13,6 +14,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -48,64 +50,54 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
* 处理聊天消息
|
||||
*/
|
||||
@Override
|
||||
public void handleChatMessage(ChatRequest request, String sessionId, Principal principal) {
|
||||
public void handleChatMessage(WebSocketRequest webSocketRequest, String sessionId, Principal principal) {
|
||||
try {
|
||||
log.info("处理聊天消息: request={}, sessionId={}, principal={}", request, sessionId, principal);
|
||||
log.info("处理聊天消息: request={}, sessionId={}, principal={}", webSocketRequest, sessionId, principal);
|
||||
|
||||
// 验证请求参数
|
||||
if (request.getContent() == null || request.getContent().trim().isEmpty()) {
|
||||
sendErrorMessage(request.getSenderId(), "消息内容不能为空");
|
||||
if (webSocketRequest.getContent() == null || webSocketRequest.getContent().trim().isEmpty()) {
|
||||
sendErrorMessage(getUserId(principal, sessionId), "消息内容不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 确定用户身份和类型
|
||||
String userId = request.getSenderId();
|
||||
WebSocketMessage.SenderType senderType = WebSocketMessage.SenderType.GUEST;
|
||||
// 设置默认值
|
||||
setWebSocketRequestDefaults(webSocketRequest, principal, sessionId);
|
||||
|
||||
if (principal != null) {
|
||||
userId = principal.getName();
|
||||
// 如果用户ID不是以guest_开头,说明是认证用户
|
||||
if (!userId.startsWith("guest_")) {
|
||||
senderType = WebSocketMessage.SenderType.USER;
|
||||
}
|
||||
}
|
||||
log.info("确定用户身份: userId={}, senderType={}", webSocketRequest.getSenderId(),
|
||||
webSocketRequest.getSenderType());
|
||||
|
||||
// 更新请求中的用户信息
|
||||
request.setSenderId(userId);
|
||||
request.setSenderType(senderType == WebSocketMessage.SenderType.USER ? ChatRequest.SenderType.USER
|
||||
: ChatRequest.SenderType.GUEST);
|
||||
|
||||
log.info("确定用户身份: userId={}, senderType={}", userId, senderType);
|
||||
// 转换请求对象
|
||||
ChatRequest chatRequest = convertToChatRequest(webSocketRequest);
|
||||
|
||||
// 构建用户消息
|
||||
WebSocketMessage userMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.conversationId(request.getConversationId())
|
||||
.type(WebSocketMessage.MessageType.TEXT)
|
||||
.content(request.getContent())
|
||||
.senderId(userId)
|
||||
.senderType(senderType)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.conversationId(chatRequest.getConversationId())
|
||||
.type("TEXT")
|
||||
.content(chatRequest.getContent())
|
||||
.senderId(chatRequest.getSenderId())
|
||||
.senderType(getSenderType(chatRequest.getSenderType()))
|
||||
.status("SENT")
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
// 发送用户消息到会话
|
||||
if (request.getConversationId() != null) {
|
||||
messagingTemplate.convertAndSend("/topic/conversation/" + request.getConversationId(), userMessage);
|
||||
if (chatRequest.getConversationId() != null) {
|
||||
messagingTemplate.convertAndSend("/topic/conversation/" + chatRequest.getConversationId(), userMessage);
|
||||
}
|
||||
|
||||
// 发送给用户私有队列
|
||||
messagingTemplate.convertAndSendToUser(request.getSenderId(), "/queue/messages", userMessage);
|
||||
messagingTemplate.convertAndSendToUser(chatRequest.getSenderId(), "/queue/messages", userMessage);
|
||||
|
||||
// 发送AI思考状态
|
||||
sendAiThinkingMessage(request.getSenderId(), request.getConversationId());
|
||||
sendAiThinkingMessage(chatRequest.getSenderId(), chatRequest.getConversationId());
|
||||
|
||||
// 异步调用AI服务
|
||||
processAiResponse(request);
|
||||
processAiResponse(chatRequest);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理聊天消息失败", e);
|
||||
sendErrorMessage(request.getSenderId(), "消息处理失败,请稍后重试");
|
||||
sendErrorMessage(getUserId(principal, sessionId), "消息处理失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,39 +107,27 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
@Override
|
||||
public void handleUserConnect(ConnectRequest request, String sessionId, Principal principal) {
|
||||
try {
|
||||
String userId = request.getUserId();
|
||||
boolean isAuthenticated = false;
|
||||
|
||||
// 优先从Principal获取认证用户信息
|
||||
if (principal != null) {
|
||||
userId = principal.getName();
|
||||
// 检查是否是认证用户(不是访客)
|
||||
isAuthenticated = !userId.startsWith("guest_");
|
||||
}
|
||||
|
||||
// 如果还没有userId,生成访客ID
|
||||
if (userId == null) {
|
||||
userId = "guest_" + sessionId;
|
||||
}
|
||||
// 设置默认值
|
||||
setConnectRequestDefaults(request, principal, sessionId);
|
||||
|
||||
log.info("用户连接WebSocket: userId={}, sessionId={}, authenticated={}",
|
||||
userId, sessionId, isAuthenticated);
|
||||
request.getUserId(), sessionId, isUserAuthenticated(request.getUserId()));
|
||||
|
||||
// 记录在线用户
|
||||
onlineUsers.put(sessionId, userId);
|
||||
onlineUsers.put(sessionId, request.getUserId());
|
||||
|
||||
// 发送连接成功消息
|
||||
WebSocketMessage connectMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.type(WebSocketMessage.MessageType.CONNECTION)
|
||||
.type("CONNECTION")
|
||||
.content("连接成功")
|
||||
.senderId("system")
|
||||
.senderType(WebSocketMessage.SenderType.SYSTEM)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.senderType("SYSTEM")
|
||||
.status("SENT")
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
messagingTemplate.convertAndSendToUser(userId, "/queue/messages", connectMessage);
|
||||
messagingTemplate.convertAndSendToUser(request.getUserId(), "/queue/messages", connectMessage);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理用户连接失败", e);
|
||||
@@ -182,11 +162,11 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
// 发送心跳响应
|
||||
WebSocketMessage heartbeatMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.type(WebSocketMessage.MessageType.HEARTBEAT)
|
||||
.type("HEARTBEAT")
|
||||
.content("pong")
|
||||
.senderId("system")
|
||||
.senderType(WebSocketMessage.SenderType.SYSTEM)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.senderType("SYSTEM")
|
||||
.status("SENT")
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
@@ -206,11 +186,11 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
WebSocketMessage thinkingMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.conversationId(conversationId)
|
||||
.type(WebSocketMessage.MessageType.AI_THINKING)
|
||||
.type("AI_THINKING")
|
||||
.content("AI正在思考中...")
|
||||
.senderId("ai")
|
||||
.senderType(WebSocketMessage.SenderType.AI)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.senderType("AI")
|
||||
.status("SENT")
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
@@ -246,7 +226,7 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
userMessage.setUserId(userId);
|
||||
userMessage.setCreateBy(userId); // 设置创建人为当前用户
|
||||
userMessage
|
||||
.setUserType(request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest");
|
||||
.setUserType("USER".equals(request.getSenderType()) ? "registered" : "guest");
|
||||
userMessage.setContent(request.getContent());
|
||||
userMessage.setType("text");
|
||||
userMessage.setSender("user");
|
||||
@@ -281,11 +261,11 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
private void sendErrorMessage(String userId, String errorContent) {
|
||||
WebSocketMessage errorMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.type(WebSocketMessage.MessageType.ERROR)
|
||||
.type("ERROR")
|
||||
.content(errorContent)
|
||||
.senderId("system")
|
||||
.senderType(WebSocketMessage.SenderType.SYSTEM)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.senderType("SYSTEM")
|
||||
.status("SENT")
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
@@ -309,7 +289,7 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
|
||||
Conversation conversation = Conversation.builder()
|
||||
.userId(userId)
|
||||
.userType(request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest")
|
||||
.userType("USER".equals(request.getSenderType()) ? "registered" : "guest")
|
||||
.title("新对话")
|
||||
.type("chat")
|
||||
.conversationStatus("active")
|
||||
@@ -341,7 +321,7 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
// 如果会话不存在,创建一个
|
||||
conversation = Conversation.builder()
|
||||
.userId(userId)
|
||||
.userType(request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest")
|
||||
.userType("USER".equals(request.getSenderType()) ? "registered" : "guest")
|
||||
.title("对话")
|
||||
.type("chat")
|
||||
.conversationStatus("active")
|
||||
@@ -453,11 +433,11 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
WebSocketMessage fallbackMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.conversationId(conversationId)
|
||||
.type(WebSocketMessage.MessageType.TEXT)
|
||||
.type("TEXT")
|
||||
.content(aiReply)
|
||||
.senderId("ai")
|
||||
.senderType(WebSocketMessage.SenderType.AI)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.senderType("AI")
|
||||
.status("SENT")
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
@@ -488,11 +468,11 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
WebSocketMessage aiMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.conversationId(conversationId)
|
||||
.type(WebSocketMessage.MessageType.TEXT)
|
||||
.type("TEXT")
|
||||
.content(content)
|
||||
.senderId("ai")
|
||||
.senderType(WebSocketMessage.SenderType.AI)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.senderType("AI")
|
||||
.status("SENT")
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
@@ -534,4 +514,95 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
log.debug("没有换行符,返回原始内容");
|
||||
return new String[]{aiReply};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置WebSocket请求的默认值
|
||||
*/
|
||||
private void setWebSocketRequestDefaults(WebSocketRequest request, Principal principal, String sessionId) {
|
||||
// 如果请求中没有发送者ID,尝试从Principal获取
|
||||
if (request.getSenderId() == null && principal != null) {
|
||||
request.setSenderId(principal.getName());
|
||||
}
|
||||
|
||||
// 如果还是没有发送者ID,使用会话ID作为访客ID
|
||||
if (request.getSenderId() == null) {
|
||||
request.setSenderId("guest_" + sessionId);
|
||||
request.setSenderType("GUEST");
|
||||
}
|
||||
|
||||
// 设置时间戳
|
||||
if (request.getTimestamp() == null) {
|
||||
request.setTimestamp(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置连接请求的默认值
|
||||
*/
|
||||
private void setConnectRequestDefaults(ConnectRequest request, Principal principal, String sessionId) {
|
||||
// 优先从Principal获取认证用户信息
|
||||
if (principal != null) {
|
||||
request.setUserId(principal.getName());
|
||||
}
|
||||
|
||||
// 如果还没有userId,生成访客ID
|
||||
if (request.getUserId() == null) {
|
||||
request.setUserId("guest_" + sessionId);
|
||||
}
|
||||
|
||||
// 设置连接时间戳
|
||||
if (request.getTimestamp() == null) {
|
||||
request.setTimestamp(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户ID
|
||||
*/
|
||||
private String getUserId(Principal principal, String sessionId) {
|
||||
if (principal != null) {
|
||||
return principal.getName();
|
||||
}
|
||||
return "guest_" + sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否已认证
|
||||
*/
|
||||
private boolean isUserAuthenticated(String userId) {
|
||||
return userId != null && !userId.startsWith("guest_");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取发送者类型
|
||||
*/
|
||||
private String getSenderType(String senderType) {
|
||||
if ("USER".equals(senderType)) {
|
||||
return "USER";
|
||||
} else if ("GUEST".equals(senderType)) {
|
||||
return "GUEST";
|
||||
} else if ("AI".equals(senderType)) {
|
||||
return "AI";
|
||||
} else if ("SYSTEM".equals(senderType)) {
|
||||
return "SYSTEM";
|
||||
}
|
||||
return "USER";
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换WebSocketRequest到ChatRequest
|
||||
*
|
||||
* @param webSocketRequest WebSocket请求对象
|
||||
* @return ChatRequest对象
|
||||
*/
|
||||
private ChatRequest convertToChatRequest(WebSocketRequest webSocketRequest) {
|
||||
return ChatRequest.builder()
|
||||
.content(webSocketRequest.getContent())
|
||||
.senderId(webSocketRequest.getSenderId())
|
||||
.senderType(webSocketRequest.getSenderType())
|
||||
.messageType(webSocketRequest.getMessageType())
|
||||
.conversationId(webSocketRequest.getConversationId())
|
||||
.timestamp(webSocketRequest.getTimestamp())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.emotion.util;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 令牌工具类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Component
|
||||
public class TokenUtil {
|
||||
|
||||
/**
|
||||
* 从请求中提取访问令牌
|
||||
*
|
||||
* @param request HTTP请求
|
||||
* @return 访问令牌
|
||||
*/
|
||||
public String extractToken(HttpServletRequest request) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||
return authHeader.substring(7);
|
||||
}
|
||||
|
||||
// 也可以从请求参数中获取
|
||||
String tokenParam = request.getParameter("token");
|
||||
if (tokenParam != null && !tokenParam.trim().isEmpty()) {
|
||||
return tokenParam.trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证令牌是否有效
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 是否有效
|
||||
*/
|
||||
public boolean isValidToken(String token) {
|
||||
return StringUtils.hasText(token);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +1,94 @@
|
||||
package com.emotion.util;
|
||||
|
||||
import com.emotion.util.UserContextHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 用户上下文工具类
|
||||
* 提供便捷的用户上下文操作方法
|
||||
* 提供便捷的方法操作用户上下文信息
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-07-23
|
||||
* @date 2025-07-25
|
||||
*/
|
||||
@Slf4j
|
||||
public class UserContextUtils {
|
||||
|
||||
/**
|
||||
* 获取当前用户ID,如果为空则返回默认值
|
||||
* 获取当前用户ID
|
||||
*
|
||||
* @param defaultValue 默认值
|
||||
* @return 用户ID
|
||||
* @return 当前用户ID,如果未登录则返回null
|
||||
*/
|
||||
public static String getCurrentUserIdOrDefault(String defaultValue) {
|
||||
String userId = UserContextHolder.getCurrentUserId();
|
||||
return userId != null ? userId : defaultValue;
|
||||
public static String getCurrentUserId() {
|
||||
return UserContextHolder.getCurrentUserId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前用户ID,如果为空则返回"system"
|
||||
* 获取当前用户名
|
||||
*
|
||||
* @return 用户ID
|
||||
* @return 当前用户名,如果未登录则返回null
|
||||
*/
|
||||
public static String getCurrentUserIdOrSystem() {
|
||||
return getCurrentUserIdOrDefault("system");
|
||||
public static String getCurrentUsername() {
|
||||
return UserContextHolder.getCurrentUsername();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前用户名,如果为空则返回默认值
|
||||
* 获取当前用户类型
|
||||
*
|
||||
* @param defaultValue 默认值
|
||||
* @return 用户名
|
||||
* @return 当前用户类型,如果未登录则返回null
|
||||
*/
|
||||
public static String getCurrentUsernameOrDefault(String defaultValue) {
|
||||
String username = UserContextHolder.getCurrentUsername();
|
||||
return username != null ? username : defaultValue;
|
||||
public static String getCurrentUserType() {
|
||||
return UserContextHolder.getCurrentUserType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前用户名,如果为空则返回"guest"
|
||||
* 获取客户端IP
|
||||
*
|
||||
* @return 用户名
|
||||
* @return 客户端IP
|
||||
*/
|
||||
public static String getCurrentUsernameOrGuest() {
|
||||
return getCurrentUsernameOrDefault("guest");
|
||||
public static String getClientIp() {
|
||||
return UserContextHolder.getClientIp();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取请求ID
|
||||
*
|
||||
* @return 请求ID
|
||||
*/
|
||||
public static String getRequestId() {
|
||||
return UserContextHolder.getRequestId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户ID,如果未登录则抛出异常
|
||||
*
|
||||
* @return 当前用户ID
|
||||
* @throws IllegalStateException 如果用户未登录
|
||||
*/
|
||||
public static String requireCurrentUserId() {
|
||||
String userId = getCurrentUserId();
|
||||
if (userId == null || userId.trim().isEmpty()) {
|
||||
throw new IllegalStateException("用户未登录或认证失败");
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户名,如果未登录则抛出异常
|
||||
*
|
||||
* @return 当前用户名
|
||||
* @throws IllegalStateException 如果用户未登录
|
||||
*/
|
||||
public static String requireCurrentUsername() {
|
||||
String username = getCurrentUsername();
|
||||
if (username == null || username.trim().isEmpty()) {
|
||||
throw new IllegalStateException("用户未登录或认证失败");
|
||||
}
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户类型,如果为空则返回默认值
|
||||
*
|
||||
@@ -62,7 +99,7 @@ public class UserContextUtils {
|
||||
String userType = UserContextHolder.getCurrentUserType();
|
||||
return userType != null ? userType : defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前用户类型,如果为空则返回"GUEST"
|
||||
*
|
||||
@@ -71,7 +108,7 @@ public class UserContextUtils {
|
||||
public static String getCurrentUserTypeOrGuest() {
|
||||
return getCurrentUserTypeOrDefault("GUEST");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取客户端IP,如果为空则返回默认值
|
||||
*
|
||||
@@ -82,7 +119,7 @@ public class UserContextUtils {
|
||||
String clientIp = UserContextHolder.getClientIp();
|
||||
return clientIp != null ? clientIp : defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取客户端IP,如果为空则返回"unknown"
|
||||
*
|
||||
@@ -91,7 +128,7 @@ public class UserContextUtils {
|
||||
public static String getClientIpOrUnknown() {
|
||||
return getClientIpOrDefault("unknown");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取请求ID,如果为空则返回默认值
|
||||
*
|
||||
@@ -102,87 +139,27 @@ public class UserContextUtils {
|
||||
String requestId = UserContextHolder.getRequestId();
|
||||
return requestId != null ? requestId : defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取请求ID,如果为空则返回"unknown"
|
||||
* 获取请求ID,如果为空则返回随机UUID
|
||||
*
|
||||
* @return 请求ID
|
||||
*/
|
||||
public static String getRequestIdOrUnknown() {
|
||||
return getRequestIdOrDefault("unknown");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前用户是否为访客
|
||||
*
|
||||
* @return 是否为访客
|
||||
*/
|
||||
public static boolean isGuest() {
|
||||
String userId = UserContextHolder.getCurrentUserId();
|
||||
String userType = UserContextHolder.getCurrentUserType();
|
||||
|
||||
return userId == null ||
|
||||
userId.startsWith("guest_") ||
|
||||
"GUEST".equalsIgnoreCase(userType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前用户是否为系统用户
|
||||
*
|
||||
* @return 是否为系统用户
|
||||
*/
|
||||
public static boolean isSystem() {
|
||||
String userId = UserContextHolder.getCurrentUserId();
|
||||
String userType = UserContextHolder.getCurrentUserType();
|
||||
|
||||
return "system".equals(userId) ||
|
||||
"SYSTEM".equalsIgnoreCase(userType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前用户是否为注册用户
|
||||
*
|
||||
* @return 是否为注册用户
|
||||
*/
|
||||
public static boolean isRegisteredUser() {
|
||||
return !isGuest() && !isSystem();
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地执行需要用户上下文的操作
|
||||
* 如果没有用户上下文,会设置默认的系统上下文
|
||||
*
|
||||
* @param operation 操作
|
||||
*/
|
||||
public static void executeWithContext(Runnable operation) {
|
||||
boolean hasContext = UserContextHolder.hasUserContext();
|
||||
|
||||
try {
|
||||
// 如果没有用户上下文,设置默认的系统上下文
|
||||
if (!hasContext) {
|
||||
UserContextHolder.setUserContext("system", "system", "SYSTEM", "127.0.0.1", "system");
|
||||
log.debug("设置默认系统用户上下文");
|
||||
}
|
||||
|
||||
// 执行操作
|
||||
operation.run();
|
||||
|
||||
} finally {
|
||||
// 如果是我们设置的默认上下文,执行后清理
|
||||
if (!hasContext) {
|
||||
UserContextHolder.clear();
|
||||
log.debug("清理默认系统用户上下文");
|
||||
}
|
||||
public static String getRequestIdOrRandom() {
|
||||
String requestId = UserContextHolder.getRequestId();
|
||||
if (requestId != null) {
|
||||
return requestId;
|
||||
}
|
||||
return java.util.UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 临时设置用户上下文执行操作
|
||||
* 执行带有临时上下文的操作
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param username 用户名
|
||||
* @param userType 用户类型
|
||||
* @param operation 操作
|
||||
* @param userId 用户ID
|
||||
* @param username 用户名
|
||||
* @param userType 用户类型
|
||||
* @param operation 要执行的操作
|
||||
*/
|
||||
public static void executeWithTempContext(String userId, String username, String userType, Runnable operation) {
|
||||
// 保存当前上下文
|
||||
@@ -211,4 +188,59 @@ public class UserContextUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从HTTP请求中获取客户端真实IP地址
|
||||
*
|
||||
* @param request HTTP请求
|
||||
* @return 客户端IP地址
|
||||
*/
|
||||
public static String getClientIpAddress(HttpServletRequest request) {
|
||||
String ip = null;
|
||||
|
||||
// 1. 从X-Forwarded-For获取(经过代理的情况)
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
||||
// 多个IP的情况,取第一个
|
||||
int index = ip.indexOf(',');
|
||||
if (index != -1) {
|
||||
ip = ip.substring(0, index);
|
||||
}
|
||||
return ip.trim();
|
||||
}
|
||||
|
||||
// 2. 从X-Real-IP获取
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
||||
return ip.trim();
|
||||
}
|
||||
|
||||
// 3. 从Proxy-Client-IP获取
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
||||
return ip.trim();
|
||||
}
|
||||
|
||||
// 4. 从WL-Proxy-Client-IP获取
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
||||
return ip.trim();
|
||||
}
|
||||
|
||||
// 5. 从HTTP_CLIENT_IP获取
|
||||
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
||||
return ip.trim();
|
||||
}
|
||||
|
||||
// 6. 从HTTP_X_FORWARDED_FOR获取
|
||||
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
||||
return ip.trim();
|
||||
}
|
||||
|
||||
// 7. 最后从getRemoteAddr获取
|
||||
ip = request.getRemoteAddr();
|
||||
return StringUtils.hasText(ip) ? ip.trim() : "unknown";
|
||||
}
|
||||
}
|
||||
+35
-67
@@ -1,10 +1,12 @@
|
||||
package com.emotionmuseum.ai.controller;
|
||||
|
||||
import com.emotionmuseum.ai.request.*;
|
||||
import com.emotionmuseum.ai.response.*;
|
||||
import com.emotionmuseum.ai.dto.MessageListResponse;
|
||||
import com.emotionmuseum.ai.dto.GuestChatRequest;
|
||||
import com.emotionmuseum.ai.dto.GuestChatResponse;
|
||||
import com.emotionmuseum.ai.dto.GuestUserInfo;
|
||||
import com.emotionmuseum.ai.dto.MessageListResponse;
|
||||
import com.emotionmuseum.ai.dto.ConversationListResponse;
|
||||
import com.emotionmuseum.ai.service.GuestChatService;
|
||||
import com.emotionmuseum.common.interceptor.UserContextInterceptor;
|
||||
import com.emotionmuseum.common.result.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -12,31 +14,34 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 访客聊天控制器
|
||||
*
|
||||
* 提供访客模式下的聊天功能
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @since 2025-07-13
|
||||
* @since 2025-07-24
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/ai/guest")
|
||||
@RequestMapping("/ai/guest")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "访客聊天", description = "访客模式AI聊天接口")
|
||||
@Tag(name = "访客聊天", description = "访客模式下的AI聊天功能")
|
||||
public class GuestChatController {
|
||||
|
||||
private final GuestChatService guestChatService;
|
||||
private final UserContextInterceptor userContextInterceptor = new UserContextInterceptor();
|
||||
|
||||
@PostMapping("/chat")
|
||||
@Operation(summary = "访客聊天", description = "访客模式下发送消息并获取AI回复")
|
||||
public Result<com.emotionmuseum.ai.dto.GuestChatResponse> guestChat(
|
||||
@RequestBody com.emotionmuseum.ai.dto.GuestChatRequest request) {
|
||||
public Result<GuestChatResponse> guestChat(
|
||||
@RequestBody GuestChatRequest request) {
|
||||
|
||||
// 自动获取客户端IP和User-Agent
|
||||
String clientIp = getClientIp();
|
||||
@@ -62,8 +67,8 @@ public class GuestChatController {
|
||||
return (Result) guestChatService.getGuestConversations(clientIp, pageNum, pageSize);
|
||||
}
|
||||
|
||||
@GetMapping("/conversation/{conversationId}/messages")
|
||||
@Operation(summary = "获取访客会话消息", description = "获取指定会话的消息列表")
|
||||
@GetMapping("/messages/{conversationId}")
|
||||
@Operation(summary = "获取会话消息", description = "获取指定会话的所有消息")
|
||||
public Result<List<MessageListResponse>> getGuestConversationMessages(
|
||||
@Parameter(description = "会话ID") @PathVariable String conversationId,
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@@ -72,11 +77,11 @@ public class GuestChatController {
|
||||
String clientIp = getClientIp();
|
||||
log.info("获取访客会话消息: IP={}, ConversationId={}", clientIp, conversationId);
|
||||
|
||||
return guestChatService.getGuestConversationMessages(conversationId, clientIp, pageNum, pageSize);
|
||||
return (Result) guestChatService.getGuestConversationMessages(conversationId, clientIp, pageNum, pageSize);
|
||||
}
|
||||
|
||||
@PostMapping("/conversation/{conversationId}/end")
|
||||
@Operation(summary = "结束访客会话", description = "结束指定的访客会话")
|
||||
@PostMapping("/end/{conversationId}")
|
||||
@Operation(summary = "结束会话", description = "结束指定的访客会话")
|
||||
public Result<Void> endGuestConversation(
|
||||
@Parameter(description = "会话ID") @PathVariable String conversationId) {
|
||||
|
||||
@@ -86,7 +91,7 @@ public class GuestChatController {
|
||||
return guestChatService.endGuestConversation(conversationId, clientIp);
|
||||
}
|
||||
|
||||
@GetMapping("/user/info")
|
||||
@GetMapping("/user-info")
|
||||
@Operation(summary = "获取访客用户信息", description = "根据IP地址获取或创建访客用户信息")
|
||||
public Result<GuestUserInfo> getGuestUserInfo() {
|
||||
String clientIp = getClientIp();
|
||||
@@ -94,38 +99,23 @@ public class GuestChatController {
|
||||
|
||||
log.info("获取访客用户信息: IP={}", clientIp);
|
||||
|
||||
return guestChatService.getOrCreateGuestUser(clientIp, userAgent);
|
||||
return guestChatService.getGuestUserInfo(clientIp, userAgent);
|
||||
}
|
||||
|
||||
@PostMapping("/emotion/analyze")
|
||||
@Operation(summary = "访客情绪分析", description = "分析访客输入文本的情绪")
|
||||
public Result<com.emotionmuseum.ai.dto.EmotionAnalysisResponse> analyzeGuestEmotion(
|
||||
@RequestBody com.emotionmuseum.ai.dto.EmotionAnalysisRequest request) {
|
||||
@PostMapping("/test-split")
|
||||
@Operation(summary = "测试拆分功能", description = "测试AI回复的拆分功能")
|
||||
public Result<GuestChatResponse> testSplitFunction(
|
||||
@RequestBody GuestChatRequest request) {
|
||||
|
||||
String clientIp = getClientIp();
|
||||
log.info("访客情绪分析: IP={}, Text={}", clientIp, request.getText());
|
||||
log.info("测试拆分功能: Message={}", request.getMessage());
|
||||
|
||||
return guestChatService.analyzeGuestEmotion(request, clientIp);
|
||||
}
|
||||
|
||||
@GetMapping("/health")
|
||||
@Operation(summary = "访客服务健康检查", description = "检查访客聊天服务状态")
|
||||
public Result<Boolean> healthCheck() {
|
||||
return Result.success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/test/split")
|
||||
@Operation(summary = "测试消息拆分功能", description = "测试AI回复消息的拆分功能")
|
||||
public Result<com.emotionmuseum.ai.dto.GuestChatResponse> testMessageSplit(
|
||||
@RequestBody com.emotionmuseum.ai.dto.GuestChatRequest request) {
|
||||
log.info("测试消息拆分功能: message={}", request.getMessage());
|
||||
|
||||
// 模拟包含不同换行符的AI回复进行测试
|
||||
// 根据消息内容生成不同的模拟回复
|
||||
String mockAiReply;
|
||||
if (request.getMessage().contains("双换行")) {
|
||||
mockAiReply = "这是第一段回复,介绍了基本功能。我可以帮助你进行日常对话。\n\n" +
|
||||
"这是第二段回复,详细说明了聊天功能。我能理解你的情感并给出合适的回应。\n\n" +
|
||||
"这是第三段回复,介绍了情感分析功能。我可以分析你的情绪状态并提供建议。";
|
||||
mockAiReply = "这是第一段回复,介绍基本功能。\n\n" +
|
||||
"这是第二段回复,说明聊天功能。\n\n" +
|
||||
"这是第三段回复,介绍情感分析。\n\n" +
|
||||
"这是第四段回复,提供使用建议。";
|
||||
} else if (request.getMessage().contains("单换行")) {
|
||||
mockAiReply = "这是第一行回复,介绍基本功能。\n" +
|
||||
"这是第二行回复,说明聊天功能。\n" +
|
||||
@@ -136,7 +126,7 @@ public class GuestChatController {
|
||||
}
|
||||
|
||||
// 创建模拟的访客聊天响应
|
||||
com.emotionmuseum.ai.dto.GuestChatResponse response = new com.emotionmuseum.ai.dto.GuestChatResponse();
|
||||
GuestChatResponse response = new GuestChatResponse();
|
||||
response.setGuestUserId("test_guest_user");
|
||||
response.setGuestNickname("测试用户");
|
||||
response.setConversationId("test_conversation_" + System.currentTimeMillis());
|
||||
@@ -166,29 +156,7 @@ public class GuestChatController {
|
||||
}
|
||||
|
||||
var request = attributes.getRequest();
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
|
||||
// 处理多个IP的情况,取第一个
|
||||
if (ip != null && ip.contains(",")) {
|
||||
ip = ip.split(",")[0].trim();
|
||||
}
|
||||
|
||||
return ip;
|
||||
return userContextInterceptor.getClientIpAddress(request);
|
||||
} catch (Exception e) {
|
||||
return "127.0.0.1";
|
||||
}
|
||||
@@ -211,4 +179,4 @@ public class GuestChatController {
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user