refactor: 重命名 backend-single 目录为 server
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
package com.emotion;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 情感博物馆简化版启动类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-21
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.emotion.mapper")
|
||||
public class EmotionSimpleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.setProperty("spring.profiles.active",
|
||||
System.getProperty("spring.profiles.active", "local"));
|
||||
|
||||
SpringApplication.run(EmotionSimpleApplication.class, args);
|
||||
|
||||
System.out.println("========================================");
|
||||
System.out.println("🎉 情感博物馆服务启动成功!");
|
||||
System.out.println("📋 服务信息:");
|
||||
System.out.println(" - 服务名称: backend-single");
|
||||
System.out.println(" - 服务端口: 19089");
|
||||
System.out.println(" - 环境配置: " + System.getProperty("spring.profiles.active"));
|
||||
System.out.println(" - API文档: http://localhost:19089/api/health");
|
||||
System.out.println("========================================");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.emotion.common;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 基础实体类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-22
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BaseEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(value = "create_by", fill = FieldFill.INSERT)
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(value = "create_time", fill = FieldFill.INSERT)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除:0-未删除,1-已删除
|
||||
*/
|
||||
@TableField("is_deleted")
|
||||
@TableLogic
|
||||
private Integer isDeleted;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@TableField("remarks")
|
||||
private String remarks;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.emotion.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
|
||||
/**
|
||||
* 分页查询基类请求参数
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Data
|
||||
public class BasePageRequest {
|
||||
|
||||
/**
|
||||
* 当前页码,从1开始
|
||||
*/
|
||||
@Min(value = 1, message = "页码不能小于1")
|
||||
private Long current = 1L;
|
||||
|
||||
/**
|
||||
* 每页大小
|
||||
*/
|
||||
@Min(value = 1, message = "每页大小不能小于1")
|
||||
@Max(value = 100, message = "每页大小不能超过100")
|
||||
private Long size = 10L;
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
*/
|
||||
private String orderBy;
|
||||
|
||||
/**
|
||||
* 排序方式:asc-升序,desc-降序
|
||||
*/
|
||||
private String orderDirection = "desc";
|
||||
|
||||
/**
|
||||
* 搜索关键词
|
||||
*/
|
||||
private String keyword;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.emotion.common;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页结果封装
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Data
|
||||
public class PageResult<T> {
|
||||
|
||||
/**
|
||||
* 当前页码
|
||||
*/
|
||||
private Long current;
|
||||
|
||||
/**
|
||||
* 每页大小
|
||||
*/
|
||||
private Long size;
|
||||
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
private Long total;
|
||||
|
||||
/**
|
||||
* 总页数
|
||||
*/
|
||||
private Long pages;
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
*/
|
||||
private List<T> records;
|
||||
|
||||
public PageResult() {}
|
||||
|
||||
public PageResult(IPage<T> page) {
|
||||
this.current = page.getCurrent();
|
||||
this.size = page.getSize();
|
||||
this.total = page.getTotal();
|
||||
this.pages = page.getPages();
|
||||
this.records = page.getRecords();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.emotion.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 统一返回结果
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-22
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 返回消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 返回数据
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
public Result() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Result(Integer code, String message, T data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功返回
|
||||
*/
|
||||
public static <T> Result<T> success() {
|
||||
return new Result<>(200, "操作成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功返回
|
||||
*/
|
||||
public static <T> Result<T> success(T data) {
|
||||
return new Result<>(200, "操作成功", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功返回
|
||||
*/
|
||||
public static <T> Result<T> success(String message, T data) {
|
||||
return new Result<>(200, message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败返回
|
||||
*/
|
||||
public static <T> Result<T> error() {
|
||||
return new Result<>(500, "操作失败", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败返回
|
||||
*/
|
||||
public static <T> Result<T> error(String message) {
|
||||
return new Result<>(500, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败返回
|
||||
*/
|
||||
public static <T> Result<T> error(Integer code, String message) {
|
||||
return new Result<>(code, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
public static <T> Result<T> unauthorized() {
|
||||
return new Result<>(401, "未授权", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 未授权带消息
|
||||
*/
|
||||
public static <T> Result<T> unauthorized(String message) {
|
||||
return new Result<>(401, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止访问
|
||||
*/
|
||||
public static <T> Result<T> forbidden() {
|
||||
return new Result<>(403, "禁止访问", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止访问带消息
|
||||
*/
|
||||
public static <T> Result<T> forbidden(String message) {
|
||||
return new Result<>(403, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求参数错误
|
||||
*/
|
||||
public static <T> Result<T> badRequest(String message) {
|
||||
return new Result<>(400, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源未找到
|
||||
*/
|
||||
public static <T> Result<T> notFound() {
|
||||
return new Result<>(404, "资源未找到", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源未找到带消息
|
||||
*/
|
||||
public static <T> Result<T> notFound(String message) {
|
||||
return new Result<>(404, message, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* 多线程异步任务线程池配置
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
public class AsyncConfig {
|
||||
|
||||
@Value("${async.core-pool-size:10}")
|
||||
private int corePoolSize;
|
||||
|
||||
@Value("${async.max-pool-size:50}")
|
||||
private int maxPoolSize;
|
||||
|
||||
@Value("${async.queue-capacity:200}")
|
||||
private int queueCapacity;
|
||||
|
||||
@Value("${async.keep-alive-seconds:60}")
|
||||
private int keepAliveSeconds;
|
||||
|
||||
@Value("${async.thread-name-prefix:single-async-}")
|
||||
private String threadNamePrefix;
|
||||
|
||||
@Bean(name = "taskExecutor")
|
||||
public Executor taskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(corePoolSize);
|
||||
executor.setMaxPoolSize(maxPoolSize);
|
||||
executor.setQueueCapacity(queueCapacity);
|
||||
executor.setKeepAliveSeconds(keepAliveSeconds);
|
||||
executor.setThreadNamePrefix(threadNamePrefix);
|
||||
executor.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy());
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
|
||||
/**
|
||||
* ID生成器配置类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class IdGeneratorConfig {
|
||||
|
||||
/**
|
||||
* 机器ID配置(可通过配置文件指定)
|
||||
*/
|
||||
@Value("${emotion.snowflake.machine-id:#{null}}")
|
||||
private Long machineId;
|
||||
|
||||
/**
|
||||
* 配置雪花算法ID生成器
|
||||
*
|
||||
* @return SnowflakeIdGenerator实例
|
||||
*/
|
||||
@Bean
|
||||
public SnowflakeIdGenerator snowflakeIdGenerator() {
|
||||
long finalMachineId;
|
||||
|
||||
if (machineId != null) {
|
||||
// 使用配置文件中指定的机器ID
|
||||
finalMachineId = machineId;
|
||||
log.info("使用配置文件指定的机器ID: {}", finalMachineId);
|
||||
} else {
|
||||
// 自动生成机器ID
|
||||
finalMachineId = generateMachineId();
|
||||
log.info("自动生成机器ID: {}", finalMachineId);
|
||||
}
|
||||
|
||||
return new SnowflakeIdGenerator(finalMachineId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成机器ID
|
||||
* 基于MAC地址和IP地址生成唯一的机器ID
|
||||
*
|
||||
* @return 机器ID (0-1023)
|
||||
*/
|
||||
private long generateMachineId() {
|
||||
try {
|
||||
// 获取本机IP地址
|
||||
InetAddress localHost = InetAddress.getLocalHost();
|
||||
|
||||
// 获取网络接口
|
||||
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(localHost);
|
||||
|
||||
if (networkInterface != null) {
|
||||
// 获取MAC地址
|
||||
byte[] hardwareAddress = networkInterface.getHardwareAddress();
|
||||
|
||||
if (hardwareAddress != null && hardwareAddress.length >= 6) {
|
||||
// 使用MAC地址的后两个字节生成机器ID
|
||||
long machineId = ((hardwareAddress[4] & 0xFF) << 8) | (hardwareAddress[5] & 0xFF);
|
||||
// 确保机器ID在有效范围内 (0-1023)
|
||||
return machineId & 0x3FF;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果无法获取MAC地址,使用IP地址生成
|
||||
byte[] address = localHost.getAddress();
|
||||
if (address.length >= 4) {
|
||||
// 使用IP地址的后两个字节生成机器ID
|
||||
long machineId = ((address[2] & 0xFF) << 8) | (address[3] & 0xFF);
|
||||
return machineId & 0x3FF;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("自动生成机器ID失败,使用默认策略: {}", e.getMessage());
|
||||
}
|
||||
|
||||
// 如果所有方法都失败,使用当前时间戳生成
|
||||
return System.currentTimeMillis() % 1024;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Knife4j / OpenAPI 文档配置
|
||||
*/
|
||||
@Configuration
|
||||
public class Knife4jConfig {
|
||||
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("情绪博物馆 API 文档")
|
||||
.version("1.0.0")
|
||||
.description("情绪博物馆后端接口文档")
|
||||
.contact(new Contact()
|
||||
.name("emotion-museum")
|
||||
.email("")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* MyBatis-Plus配置类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* 分页插件配置
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 添加分页插件
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
* Redis配置类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
/**
|
||||
* 配置RedisTemplate
|
||||
*/
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
|
||||
// 使用String序列化器作为key的序列化器
|
||||
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
|
||||
template.setKeySerializer(stringRedisSerializer);
|
||||
template.setHashKeySerializer(stringRedisSerializer);
|
||||
|
||||
// 使用JSON序列化器作为value的序列化器
|
||||
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
|
||||
template.setValueSerializer(jsonRedisSerializer);
|
||||
template.setHashValueSerializer(jsonRedisSerializer);
|
||||
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import com.emotion.interceptor.WechatApiLoggingInterceptor;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.BufferingClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 统一 RestTemplate Bean
|
||||
*
|
||||
* <p>设计要点:
|
||||
* <ol>
|
||||
* <li>用 BufferingClientHttpRequestFactory 包装 SimpleClientHttpRequestFactory,
|
||||
* 响应体被缓存到字节数组,可被 Interceptor / ErrorHandler / Converter 多次读取
|
||||
* (避免破坏其他 7 个 RestTemplate 使用点:DifyProviderAdapter、
|
||||
* CozeProviderAdapter、HttpTtsEngineClient、AsrServiceImpl、
|
||||
* ApiEndpointServiceImpl、AiChatServiceImpl、ApiTestProxyController)</li>
|
||||
* <li>显式设置 User-Agent 为 Mozilla/5.0,绕过 WAF / CloudFlare 对 Java/* 默认 UA 的拦截</li>
|
||||
* <li>显式设置连接 / 读取超时,避免线程被 hang 住</li>
|
||||
* <li>加 StringHttpMessageConverter(UTF_8) 支持 text/plain 响应</li>
|
||||
* <li>加 WechatApiLoggingInterceptor 记录请求 / 响应(脱敏)</li>
|
||||
* <li>加 WechatResponseErrorHandler 4xx/5xx 不抛异常</li>
|
||||
* </ol>
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(RestTemplateBuilder builder) {
|
||||
SimpleClientHttpRequestFactory simpleFactory = new SimpleClientHttpRequestFactory();
|
||||
simpleFactory.setConnectTimeout((int) Duration.ofSeconds(5).toMillis());
|
||||
simpleFactory.setReadTimeout((int) Duration.ofSeconds(10).toMillis());
|
||||
BufferingClientHttpRequestFactory bufferingFactory =
|
||||
new BufferingClientHttpRequestFactory(simpleFactory);
|
||||
|
||||
return builder
|
||||
.requestFactory(() -> bufferingFactory)
|
||||
.defaultHeader("User-Agent", "Mozilla/5.0 (compatible; EmotionMuseum/1.0)")
|
||||
.defaultHeader("Accept", "application/json, text/plain, */*")
|
||||
.additionalMessageConverters(new StringHttpMessageConverter(StandardCharsets.UTF_8))
|
||||
.additionalInterceptors(new WechatApiLoggingInterceptor())
|
||||
.errorHandler(new WechatResponseErrorHandler())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 安全配置
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-22
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
/**
|
||||
* 密码编码器
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全过滤器链
|
||||
*/
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// 禁用CSRF
|
||||
.csrf(csrf -> csrf.disable())
|
||||
|
||||
// 配置CORS
|
||||
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
||||
|
||||
// 配置会话管理
|
||||
.sessionManagement(management -> management
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
|
||||
// 配置授权规则
|
||||
.authorizeHttpRequests(authz -> authz
|
||||
// 允许所有请求(简化配置)
|
||||
.anyRequest().permitAll())
|
||||
|
||||
// 禁用默认登录页面
|
||||
.formLogin(login -> login.disable())
|
||||
|
||||
// 禁用HTTP Basic认证
|
||||
.httpBasic(basic -> basic.disable());
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* CORS配置
|
||||
*/
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
|
||||
// 允许所有来源
|
||||
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
|
||||
|
||||
// 允许的HTTP方法
|
||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
|
||||
|
||||
// 允许的请求头
|
||||
configuration.setAllowedHeaders(Arrays.asList("*"));
|
||||
|
||||
// 允许携带凭证
|
||||
configuration.setAllowCredentials(true);
|
||||
|
||||
// 预检请求的缓存时间
|
||||
configuration.setMaxAge(3600L);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
|
||||
return source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import com.emotion.interceptor.AuthInterceptor;
|
||||
import com.emotion.interceptor.AdminAuthInterceptor;
|
||||
import com.emotion.interceptor.UserContextInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Web配置类
|
||||
* 配置拦截器、跨域等
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
private AuthInterceptor authInterceptor;
|
||||
|
||||
@Autowired
|
||||
private AdminAuthInterceptor adminAuthInterceptor;
|
||||
|
||||
@Autowired
|
||||
private UserContextInterceptor userContextInterceptor;
|
||||
|
||||
/**
|
||||
* 添加拦截器
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 管理员认证拦截器 - 优先级最高
|
||||
registry.addInterceptor(adminAuthInterceptor)
|
||||
.addPathPatterns("/admin/**")
|
||||
.order(1);
|
||||
|
||||
// 用户认证拦截器
|
||||
registry.addInterceptor(authInterceptor)
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(
|
||||
"/auth/**",
|
||||
"/analytics/events/batch",
|
||||
"/tts/audio/**",
|
||||
"/admin/**", // 排除管理员接口,由AdminAuthInterceptor处理
|
||||
"/error",
|
||||
"/analytics/events/batch",
|
||||
"/tts/audio/**",
|
||||
"/favicon.ico",
|
||||
"/actuator/**",
|
||||
"/swagger-ui/**",
|
||||
"/swagger-resources/**",
|
||||
"/v2/api-docs",
|
||||
"/v3/api-docs",
|
||||
"/webjars/**",
|
||||
"/doc.html",
|
||||
"/static/**",
|
||||
"/public/**"
|
||||
)
|
||||
.order(2);
|
||||
|
||||
// 用户上下文拦截器
|
||||
registry.addInterceptor(userContextInterceptor)
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(
|
||||
"/error",
|
||||
"/favicon.ico",
|
||||
"/actuator/**",
|
||||
"/swagger-ui/**",
|
||||
"/swagger-resources/**",
|
||||
"/v2/api-docs",
|
||||
"/v3/api-docs",
|
||||
"/webjars/**",
|
||||
"/doc.html",
|
||||
"/static/**",
|
||||
"/public/**"
|
||||
)
|
||||
.order(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨域配置
|
||||
*/
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true)
|
||||
.maxAge(3600);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import com.emotion.interceptor.AdminAuthInterceptor;
|
||||
import com.emotion.interceptor.JwtAuthInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Web MVC配置
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
private JwtAuthInterceptor jwtAuthInterceptor;
|
||||
|
||||
@Autowired
|
||||
private AdminAuthInterceptor adminAuthInterceptor;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 管理员拦截器 - 优先级最高,只拦截 /admin/** 路径
|
||||
registry.addInterceptor(adminAuthInterceptor)
|
||||
.addPathPatterns("/admin/**")
|
||||
.excludePathPatterns(
|
||||
"/admin/auth/login", // 管理员登录接口
|
||||
"/admin/auth/refreshToken" // 管理员刷新token接口
|
||||
)
|
||||
.order(1); // 优先级1,最先执行
|
||||
|
||||
// 普通用户拦截器 - 拦截除管理员路径外的所有请求
|
||||
// 注意: 由于 context-path=/api, excludePathPatterns 需要包含 /api 前缀的路径
|
||||
registry.addInterceptor(jwtAuthInterceptor)
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(
|
||||
"/auth/login", // 登录接口
|
||||
"/auth/register", // 注册接口
|
||||
"/auth/captcha", // 图形验证码接口
|
||||
"/auth/sms-code", // 短信验证码接口(免登录)
|
||||
"/auth/refresh-token", // 刷新token接口
|
||||
"/auth/resetPassword", // 重置密码接口(免登录)
|
||||
"/analytics/events/batch", // Analytics event batch endpoint
|
||||
"/tts/audio/**", // Public generated TTS audio files
|
||||
"/health", // 健康检查接口
|
||||
"/ws/**", // WebSocket接口
|
||||
"/swagger-ui", // Swagger UI
|
||||
"/swagger-ui/**", // Swagger UI sub-paths
|
||||
"/v3/api-docs", // API docs root
|
||||
"/v3/api-docs/**", // API docs sub-paths
|
||||
"/doc.html", // Knife4j entry
|
||||
"/webjars", // Knife4j static resources
|
||||
"/webjars/**", // Knife4j static resources sub-paths
|
||||
"/swagger-resources", // Swagger resources root
|
||||
"/swagger-resources/**", // Swagger resources sub-paths
|
||||
"/actuator/**", // Actuator endpoints
|
||||
"/admin/**", // 排除管理员路径,由管理员拦截器处理
|
||||
"/auth/wechat/login", // WeChat mini program login endpoint
|
||||
"/auth/loginConfig", // 登录方式配置接口(免登录)
|
||||
"/error" // Spring Boot error page
|
||||
)
|
||||
.order(2); // 优先级2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import com.emotion.interceptor.WebSocketAuthInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||
|
||||
/**
|
||||
* WebSocket配置
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-22
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Autowired
|
||||
private WebSocketAuthInterceptor webSocketAuthInterceptor;
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry config) {
|
||||
// 启用简单消息代理,并设置消息代理的前缀
|
||||
config.enableSimpleBroker("/topic", "/queue", "/user");
|
||||
|
||||
// 设置应用程序的目标前缀
|
||||
config.setApplicationDestinationPrefixes("/app");
|
||||
|
||||
// 设置用户目标前缀
|
||||
config.setUserDestinationPrefix("/user");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
// 注册STOMP端点
|
||||
registry.addEndpoint("/ws/chat")
|
||||
.setAllowedOriginPatterns("*")
|
||||
.withSockJS();
|
||||
|
||||
// 注册普通WebSocket端点
|
||||
registry.addEndpoint("/ws/chat")
|
||||
.setAllowedOriginPatterns("*");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
// 添加WebSocket认证拦截器
|
||||
registration.interceptors(webSocketAuthInterceptor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "emotion.wechat.mini-program")
|
||||
public class WechatMiniProgramProperties {
|
||||
|
||||
private String appId;
|
||||
|
||||
private String appSecret;
|
||||
|
||||
private String baseUrl = "https://api.weixin.qq.com";
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.emotion.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 微信 API 错误处理器
|
||||
*
|
||||
* <p>设计要点:
|
||||
* <ol>
|
||||
* <li>显式重写 hasError() 委托父类判断(4xx/5xx → true),让 handleError() 在错误时被调用</li>
|
||||
* <li>handleError() 不抛 RestClientException,由业务层(WechatMiniProgramServiceImpl)
|
||||
* 根据 status code 判断如何处理</li>
|
||||
* <li>不在此处读 body(避免流消费,由 LoggingInterceptor 统一处理日志)</li>
|
||||
* </ol>
|
||||
*/
|
||||
@Slf4j
|
||||
public class WechatResponseErrorHandler extends DefaultResponseErrorHandler {
|
||||
|
||||
@Override
|
||||
public boolean hasError(ClientHttpResponse response) throws IOException {
|
||||
// 委托父类 DefaultResponseErrorHandler.hasError():4xx/5xx 返回 true,其他返回 false
|
||||
return super.hasError(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse response) throws IOException {
|
||||
// 不抛异常;body 已在 LoggingInterceptor 中读取并脱敏记录
|
||||
// 业务层会通过 response.getStatusCode() 判断
|
||||
log.warn("[WeChatAPI] HTTP error response, status={}", response.getStatusCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.achievement.*;
|
||||
import com.emotion.dto.response.achievement.AchievementResponse;
|
||||
import com.emotion.service.AchievementService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 成就控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/achievement")
|
||||
@Tag(name = "成就管理", description = "成就的增删改查功能")
|
||||
public class AchievementController {
|
||||
|
||||
@Autowired
|
||||
private AchievementService achievementService;
|
||||
|
||||
/**
|
||||
* 分页查询成就
|
||||
*/
|
||||
@Operation(summary = "分页查询成就", description = "分页查询成就列表")
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<AchievementResponse>> getPage(@Validated AchievementPageRequest request) {
|
||||
PageResult<AchievementResponse> pageResult = achievementService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取成就
|
||||
*/
|
||||
@Operation(summary = "根据ID获取成就", description = "根据ID获取成就详情")
|
||||
@GetMapping("/detail")
|
||||
public Result<AchievementResponse> getById(@Parameter(description = "成就 ID") @RequestParam String id) {
|
||||
AchievementResponse response = achievementService.getAchievementResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("成就不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建成就
|
||||
*/
|
||||
@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("创建成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新成就
|
||||
*/
|
||||
@Operation(summary = "更新成就", description = "更新指定成就")
|
||||
@PutMapping("/update")
|
||||
public Result<AchievementResponse> update(@RequestBody @Validated AchievementUpdateRequest request) {
|
||||
AchievementResponse response = achievementService.updateAchievementWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success("更新成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除成就
|
||||
*/
|
||||
@Operation(summary = "删除成就", description = "删除指定成就")
|
||||
@DeleteMapping("/delete")
|
||||
public Result<Void> delete(@Parameter(description = "成就 ID") @RequestParam String id) {
|
||||
boolean deleted = achievementService.removeById(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据分类查询成就
|
||||
*/
|
||||
@Operation(summary = "根据分类查询成就", description = "根据分类查询成就列表")
|
||||
@GetMapping("/byCategory")
|
||||
public Result<List<AchievementResponse>> getByCategory(@Parameter(description = "分类") @RequestParam String category) {
|
||||
List<AchievementResponse> responses = achievementService.getByCategoryWithResponse(category);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据稀有度查询成就
|
||||
*/
|
||||
@Operation(summary = "根据稀有度查询成就", description = "根据稀有度查询成就列表")
|
||||
@GetMapping("/byRarity")
|
||||
public Result<List<AchievementResponse>> getByRarity(@Parameter(description = "稀有度") @RequestParam String rarity) {
|
||||
List<AchievementResponse> responses = achievementService.getByRarityWithResponse(rarity);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已解锁的成就
|
||||
*/
|
||||
@Operation(summary = "查询已解锁的成就", description = "查询已解锁的成就列表")
|
||||
@GetMapping("/unlocked")
|
||||
public Result<List<AchievementResponse>> getUnlockedAchievements() {
|
||||
List<AchievementResponse> responses = achievementService.getUnlockedAchievementsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询未解锁的成就
|
||||
*/
|
||||
@Operation(summary = "查询未解锁的成就", description = "查询未解锁的成就列表")
|
||||
@GetMapping("/locked")
|
||||
public Result<List<AchievementResponse>> getLockedAchievements() {
|
||||
List<AchievementResponse> responses = achievementService.getLockedAchievementsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计已解锁成就数量
|
||||
*/
|
||||
@Operation(summary = "统计已解锁成就数量", description = "统计已解锁成就数量")
|
||||
@GetMapping("/unlockedCount")
|
||||
public Result<Long> countUnlockedAchievements() {
|
||||
Long count = achievementService.countUnlockedAchievements();
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计未解锁成就数量
|
||||
*/
|
||||
@Operation(summary = "统计未解锁成就数量", description = "统计未解锁成就数量")
|
||||
@GetMapping("/lockedCount")
|
||||
public Result<Long> countLockedAchievements() {
|
||||
Long count = achievementService.countLockedAchievements();
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁成就
|
||||
*/
|
||||
@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("解锁失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新成就进度
|
||||
*/
|
||||
@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("更新进度失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询最近解锁的成就
|
||||
*/
|
||||
@Operation(summary = "查询最近解锁的成就", description = "查询最近解锁的成就列表")
|
||||
@GetMapping("/recent")
|
||||
public Result<List<AchievementResponse>> getRecentlyUnlocked(@Parameter(description = "排名数量限制") @RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<AchievementResponse> responses = achievementService.getRecentlyUnlockedWithResponse(limit);
|
||||
return Result.success(responses);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.analytics.AnalyticsQueryRequest;
|
||||
import com.emotion.dto.response.analytics.AnalyticsFunnelItem;
|
||||
import com.emotion.dto.response.analytics.AnalyticsOverviewResponse;
|
||||
import com.emotion.dto.response.analytics.AnalyticsPreferenceItem;
|
||||
import com.emotion.dto.response.analytics.AnalyticsTopEventItem;
|
||||
import com.emotion.dto.response.analytics.AnalyticsTrendItem;
|
||||
import com.emotion.dto.response.analytics.AnalyticsUserItem;
|
||||
import com.emotion.service.AnalyticsService;
|
||||
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.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/admin/analytics")
|
||||
@Tag(name = "数据分析管理", description = "管理后台的数据分析看板接口")
|
||||
public class AdminAnalyticsController {
|
||||
|
||||
@Autowired
|
||||
private AnalyticsService analyticsService;
|
||||
|
||||
@Operation(summary = "获取数据概览", description = "获取数据分析概览,包括关键指标和汇总统计")
|
||||
@GetMapping("/overview")
|
||||
public Result<AnalyticsOverviewResponse> overview(@Validated AnalyticsQueryRequest request) {
|
||||
return Result.success(analyticsService.getOverview(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取趋势数据", description = "获取指定时间范围内的数据趋势")
|
||||
@GetMapping("/trend")
|
||||
public Result<List<AnalyticsTrendItem>> trend(@Validated AnalyticsQueryRequest request) {
|
||||
return Result.success(analyticsService.getTrend(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取漏斗数据", description = "获取用户转化漏斗数据,分析各阶段转化率")
|
||||
@GetMapping("/funnel")
|
||||
public Result<List<AnalyticsFunnelItem>> funnel(@Validated AnalyticsQueryRequest request) {
|
||||
return Result.success(analyticsService.getFunnel(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户偏好数据", description = "获取用户偏好设置和使用习惯统计")
|
||||
@GetMapping("/preferences")
|
||||
public Result<List<AnalyticsPreferenceItem>> preferences(@Validated AnalyticsQueryRequest request) {
|
||||
return Result.success(analyticsService.getPreferences(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取热门事件排行", description = "获取热门事件排行榜,按访问次数倒序排列")
|
||||
@GetMapping("/top-events")
|
||||
public Result<List<AnalyticsTopEventItem>> topEvents(@Validated AnalyticsQueryRequest request) {
|
||||
return Result.success(analyticsService.getTopEvents(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户活跃数据", description = "获取用户活跃度分析和行为统计数据")
|
||||
@GetMapping("/users")
|
||||
public Result<List<AnalyticsUserItem>> users(@Validated AnalyticsQueryRequest request) {
|
||||
return Result.success(analyticsService.getUsers(request));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.AdminChangePasswordRequest;
|
||||
import com.emotion.dto.request.AdminLoginRequest;
|
||||
import com.emotion.dto.request.RefreshTokenRequest;
|
||||
import com.emotion.dto.response.AdminAuthResponse;
|
||||
import com.emotion.dto.response.AdminInfoResponse;
|
||||
import com.emotion.service.AdminAuthService;
|
||||
import com.emotion.util.JwtUtil;
|
||||
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 javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 管理员认证控制器
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-10-27
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/auth")
|
||||
@Tag(name = "管理员认证管理", description = "管理员登录、登出等认证相关接口")
|
||||
public class AdminAuthController {
|
||||
|
||||
@Autowired
|
||||
private AdminAuthService adminAuthService;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
/**
|
||||
* 管理员登录
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "管理员登录", description = "使用账号和密码登录")
|
||||
public Result<AdminAuthResponse> login(@Validated @RequestBody AdminLoginRequest request) {
|
||||
AdminAuthResponse response = adminAuthService.login(request);
|
||||
return Result.success("登录成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前管理员信息
|
||||
*/
|
||||
@GetMapping("/info")
|
||||
@Operation(summary = "获取当前管理员信息", description = "根据Token获取当前登录的管理员信息")
|
||||
public Result<AdminInfoResponse> getCurrentAdminInfo(HttpServletRequest request) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
return Result.unauthorized("未登录");
|
||||
}
|
||||
|
||||
String token = authHeader.substring(7);
|
||||
String adminId = jwtUtil.getUserIdFromToken(token);
|
||||
|
||||
AdminInfoResponse adminInfo = adminAuthService.getCurrentAdminInfo(adminId);
|
||||
return Result.success(adminInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员登出
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
@Operation(summary = "管理员登出", description = "退出登录")
|
||||
public Result<Void> logout(HttpServletRequest request) {
|
||||
adminAuthService.logout(request);
|
||||
return Result.success("登出成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*/
|
||||
@PostMapping("/refreshToken")
|
||||
@Operation(summary = "刷新访问令牌", description = "使用刷新令牌获取新的访问令牌")
|
||||
public Result<AdminAuthResponse> refreshToken(@Validated @RequestBody RefreshTokenRequest request) {
|
||||
AdminAuthResponse response = adminAuthService.refreshToken(request.getRefreshToken());
|
||||
return Result.success("令牌刷新成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证访问令牌
|
||||
*/
|
||||
@GetMapping("/validateToken")
|
||||
@Operation(summary = "验证访问令牌", description = "验证当前Token是否有效")
|
||||
public Result<Boolean> validateToken(HttpServletRequest request) {
|
||||
boolean isValid = adminAuthService.validateToken(request);
|
||||
return Result.success(isValid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改管理员密码(修改自己的密码)
|
||||
*/
|
||||
@PostMapping("/changePassword")
|
||||
@Operation(summary = "修改管理员密码", description = "当前登录的管理员修改自己的密码,需要提供原密码")
|
||||
public Result<Void> changePassword(HttpServletRequest request, @Validated @RequestBody AdminChangePasswordRequest req) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
return Result.unauthorized("未登录");
|
||||
}
|
||||
|
||||
String token = authHeader.substring(7);
|
||||
String adminId = jwtUtil.getUserIdFromToken(token);
|
||||
|
||||
adminAuthService.changePassword(adminId, req);
|
||||
return Result.success("密码修改成功", null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.AiConfigCallStatsRequest;
|
||||
import com.emotion.dto.request.AdminCreateRequest;
|
||||
import com.emotion.dto.request.AdminPageRequest;
|
||||
import com.emotion.dto.request.AdminResetPasswordRequest;
|
||||
import com.emotion.dto.request.AdminUpdateRequest;
|
||||
import com.emotion.dto.response.AiConfigCallStatsResponse;
|
||||
import com.emotion.dto.response.AdminResponse;
|
||||
import com.emotion.dto.response.DashboardStatsResponse;
|
||||
import com.emotion.service.AdminService;
|
||||
import com.emotion.service.DashboardService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.util.List;
|
||||
|
||||
/**
|
||||
* 管理员控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-10-27
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin")
|
||||
@Tag(name = "管理员管理", description = "管理员的增删改查功能")
|
||||
public class AdminController {
|
||||
|
||||
@Autowired
|
||||
private AdminService adminService;
|
||||
|
||||
@Autowired
|
||||
private DashboardService dashboardService;
|
||||
|
||||
/**
|
||||
* 分页查询管理员
|
||||
*/
|
||||
@Operation(summary = "分页查询管理员", description = "分页查询管理员列表")
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<AdminResponse>> getPage(@Validated AdminPageRequest request) {
|
||||
PageResult<AdminResponse> pageResult = adminService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取管理员
|
||||
*/
|
||||
@Operation(summary = "根据ID获取管理员", description = "根据ID获取管理员详情")
|
||||
@GetMapping("/detail")
|
||||
public Result<AdminResponse> getById(
|
||||
@Parameter(description = "管理员ID", required = true)
|
||||
@RequestParam String id) {
|
||||
AdminResponse response = adminService.getAdminResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("管理员不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建管理员
|
||||
*/
|
||||
@Operation(summary = "创建管理员", description = "创建新的管理员")
|
||||
@PostMapping("/create")
|
||||
public Result<AdminResponse> create(@RequestBody @Validated AdminCreateRequest request) {
|
||||
AdminResponse response = adminService.createAdminWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("创建失败");
|
||||
}
|
||||
return Result.success("创建成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新管理员
|
||||
*/
|
||||
@Operation(summary = "更新管理员", description = "更新指定管理员")
|
||||
@PutMapping("/update")
|
||||
public Result<AdminResponse> update(@RequestBody @Validated AdminUpdateRequest request) {
|
||||
AdminResponse response = adminService.updateAdminWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success("更新成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除管理员
|
||||
*/
|
||||
@Operation(summary = "删除管理员", description = "删除指定管理员")
|
||||
@DeleteMapping("/delete")
|
||||
public Result<Void> delete(
|
||||
@Parameter(description = "管理员ID", required = true)
|
||||
@RequestParam String id) {
|
||||
boolean deleted = adminService.removeById(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success("删除成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置管理员密码(超级管理员操作)
|
||||
*/
|
||||
@Operation(summary = "重置管理员密码", description = "超级管理员重置指定管理员的密码")
|
||||
@PostMapping("/changePassword")
|
||||
public Result<Void> changePassword(@Validated @RequestBody AdminResetPasswordRequest request) {
|
||||
adminService.resetPassword(request.getId(), request.getNewPassword());
|
||||
return Result.success("密码重置成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仪表盘统计数据
|
||||
*/
|
||||
@Operation(summary = "获取仪表盘统计数据", description = "获取管理后台仪表盘的统计数据,包括用户、内容、AI服务和系统统计")
|
||||
@GetMapping("/dashboard/stats")
|
||||
public Result<DashboardStatsResponse> getDashboardStats() {
|
||||
DashboardStatsResponse stats = dashboardService.getDashboardStats();
|
||||
return Result.success("获取成功", stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户统计数据
|
||||
*/
|
||||
@Operation(summary = "获取用户统计数据", description = "获取用户相关的统计数据")
|
||||
@GetMapping("/dashboard/user-stats")
|
||||
public Result<DashboardStatsResponse.UserStats> getUserStats() {
|
||||
DashboardStatsResponse.UserStats userStats = dashboardService.getUserStats();
|
||||
return Result.success("获取成功", userStats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内容统计数据
|
||||
*/
|
||||
@Operation(summary = "获取内容统计数据", description = "获取内容相关的统计数据")
|
||||
@GetMapping("/dashboard/content-stats")
|
||||
public Result<DashboardStatsResponse.ContentStats> getContentStats() {
|
||||
DashboardStatsResponse.ContentStats contentStats = dashboardService.getContentStats();
|
||||
return Result.success("获取成功", contentStats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AI服务统计数据
|
||||
*/
|
||||
@Operation(summary = "获取AI服务统计数据", description = "获取AI服务相关的统计数据")
|
||||
@GetMapping("/dashboard/ai-stats")
|
||||
public Result<DashboardStatsResponse.AiServiceStats> getAiServiceStats() {
|
||||
DashboardStatsResponse.AiServiceStats aiStats = dashboardService.getAiServiceStats();
|
||||
return Result.success("获取成功", aiStats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统统计数据
|
||||
*/
|
||||
@Operation(summary = "获取系统统计数据", description = "获取系统相关的统计数据")
|
||||
@GetMapping("/dashboard/system-stats")
|
||||
public Result<DashboardStatsResponse.SystemStats> getSystemStats() {
|
||||
DashboardStatsResponse.SystemStats systemStats = dashboardService.getSystemStats();
|
||||
return Result.success("获取成功", systemStats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户增长趋势数据
|
||||
*/
|
||||
@Operation(summary = "获取用户增长趋势数据", description = "获取指定天数的用户增长趋势数据")
|
||||
@GetMapping("/dashboard/user-growth-trends")
|
||||
public Result<List<DashboardStatsResponse.UserGrowthTrend>> getUserGrowthTrends(
|
||||
@Parameter(description = "查询天数", required = false)
|
||||
@RequestParam(defaultValue = "7") int days) {
|
||||
List<DashboardStatsResponse.UserGrowthTrend> trends = dashboardService.getUserGrowthTrends(days);
|
||||
return Result.success("获取成功", trends);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近登录用户
|
||||
*/
|
||||
@Operation(summary = "获取最近登录用户", description = "获取最近登录的用户列表")
|
||||
@GetMapping("/dashboard/recent-logins")
|
||||
public Result<List<DashboardStatsResponse.RecentLogin>> getRecentLogins(
|
||||
@Parameter(description = "返回数量限制", required = false)
|
||||
@RequestParam(defaultValue = "10") int limit) {
|
||||
List<DashboardStatsResponse.RecentLogin> recentLogins = dashboardService.getRecentLogins(limit);
|
||||
return Result.success("获取成功", recentLogins);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 AI 配置调用次数统计
|
||||
*/
|
||||
@Operation(summary = "获取AI配置调用次数统计", description = "按 t_ai_config 的 workflow_id 关联 t_coze_api_call 统计调用次数并按次数倒序返回")
|
||||
@GetMapping(value = "/dashboard/aiConfigCallStats")
|
||||
public Result<AiConfigCallStatsResponse> getAiConfigCallStats(@Validated AiConfigCallStatsRequest request) {
|
||||
AiConfigCallStatsResponse response = dashboardService.getAiConfigCallStats(request);
|
||||
return Result.success("获取成功", response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
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.response.AiChatResponse;
|
||||
import com.emotion.dto.response.AiSummaryResponse;
|
||||
import com.emotion.dto.response.AiStatusResponse;
|
||||
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.service.AiChatService;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* AI聊天控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/ai")
|
||||
@Tag(name = "AI 聊天", description = "AI 智能对话聊天接口,支持普通聊天、总结、状态查询、访客模式等")
|
||||
public class AiChatController {
|
||||
|
||||
@Autowired
|
||||
private AiChatService aiChatService;
|
||||
|
||||
/**
|
||||
* 发送聊天消息
|
||||
*/
|
||||
@Operation(summary = "发送 AI 聊天消息", description = "向 AI 发送聊天消息,返回 AI 回复。支持指定会话 ID 和用户 ID。")
|
||||
@PostMapping("/chat")
|
||||
public Result<AiChatResponse> sendChatMessage(@Valid @RequestBody AiChatRequest request) {
|
||||
log.info("收到AI聊天请求: conversationId={}, userId={}, message={}",
|
||||
request.getConversationId(), request.getUserId(), request.getMessage());
|
||||
|
||||
// 调用AI服务
|
||||
AiChatResponse response = aiChatService.sendChatMessage(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成对话总结
|
||||
*/
|
||||
@Operation(summary = "获取 AI 聊天总结", description = "获取指定会话的 AI 聊天总结,概括对话要点。")
|
||||
@PostMapping("/summary")
|
||||
public Result<AiSummaryResponse> generateSummary(@Valid @RequestBody AiSummaryRequest request) {
|
||||
log.info("收到对话总结请求: conversationId={}, userId={}", request.getConversationId(), request.getUserId());
|
||||
|
||||
// 调用AI总结服务
|
||||
AiSummaryResponse response = aiChatService.generateConversationSummary(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AI服务状态
|
||||
*/
|
||||
@Operation(summary = "获取 AI 状态", description = "获取当前 AI 服务的运行状态信息。")
|
||||
@GetMapping("/status")
|
||||
public Result<AiStatusResponse> getServiceStatus() {
|
||||
log.info("获取AI服务状态");
|
||||
|
||||
AiStatusResponse response = aiChatService.getServiceStatus();
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天记录统计
|
||||
*/
|
||||
@Operation(summary = "获取聊天统计数据", description = "获取指定时间范围内的聊天统计数据,包括消息数、Token 消耗等。")
|
||||
@GetMapping("/stats")
|
||||
public Result<ChatStatsResponse> getChatStats(@Valid ChatStatsRequest request) {
|
||||
log.info("获取聊天统计: userId={}, conversationId={}", request.getUserId(), request.getConversationId());
|
||||
|
||||
ChatStatsResponse response = aiChatService.getChatStats(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 访客聊天(不需要登录)
|
||||
*/
|
||||
@Operation(summary = "访客模式聊天", description = "未登录用户通过访客模式与 AI 进行对话,无需 token 认证。")
|
||||
@PostMapping("/guestChat")
|
||||
public Result<GuestChatResponse> guestChat(@Valid @RequestBody GuestChatRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
String clientIp = UserContextUtils.getClientIpAddress(httpRequest);
|
||||
log.info("访客聊天请求: {}, IP: {}", request.getMessage(), clientIp);
|
||||
|
||||
GuestChatResponse response = aiChatService.guestChat(request, clientIp);
|
||||
return Result.success("发送成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访客用户信息
|
||||
*/
|
||||
@Operation(summary = "获取访客用户信息", description = "根据访客 ID 获取对应的用户信息。")
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建对话
|
||||
*/
|
||||
@Operation(summary = "创建对话会话", description = "为当前用户创建一个新的 AI 对话会话。")
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.aiconfig.*;
|
||||
import com.emotion.dto.response.aiconfig.AiConfigResponse;
|
||||
import com.emotion.service.AiConfigService;
|
||||
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.util.List;
|
||||
|
||||
/**
|
||||
* AI配置控制器
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-10-30
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/aiConfig")
|
||||
@Tag(name = "AI配置管理", description = "AI配置的增删改查功能")
|
||||
public class AiConfigController {
|
||||
|
||||
@Autowired
|
||||
private AiConfigService aiConfigService;
|
||||
|
||||
/**
|
||||
* 分页查询AI配置
|
||||
*/
|
||||
@Operation(summary = "分页查询AI配置", description = "分页查询AI配置列表")
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<AiConfigResponse>> getPage(@Validated AiConfigPageRequest request) {
|
||||
PageResult<AiConfigResponse> pageResult = aiConfigService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取AI配置
|
||||
*/
|
||||
@Operation(summary = "根据ID获取AI配置", description = "根据ID获取AI配置详情")
|
||||
@GetMapping("/detail")
|
||||
public Result<AiConfigResponse> getById(@RequestParam String id) {
|
||||
AiConfigResponse response = aiConfigService.getAiConfigResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("AI配置不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建AI配置
|
||||
*/
|
||||
@Operation(summary = "创建AI配置", description = "创建新的AI配置")
|
||||
@PostMapping("/create")
|
||||
public Result<AiConfigResponse> create(@RequestBody @Validated AiConfigCreateRequest request) {
|
||||
AiConfigResponse response = aiConfigService.createAiConfigWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("创建失败");
|
||||
}
|
||||
return Result.success("创建成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新AI配置
|
||||
*/
|
||||
@Operation(summary = "更新AI配置", description = "更新指定AI配置")
|
||||
@PutMapping("/update")
|
||||
public Result<AiConfigResponse> update(@RequestBody @Validated AiConfigUpdateRequest request) {
|
||||
AiConfigResponse response = aiConfigService.updateAiConfigWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success("更新成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除AI配置
|
||||
*/
|
||||
@Operation(summary = "删除AI配置", description = "删除指定AI配置")
|
||||
@DeleteMapping("/delete")
|
||||
public Result<Void> delete(@RequestParam String id) {
|
||||
boolean deleted = aiConfigService.removeById(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置类型查询AI配置
|
||||
*/
|
||||
@Operation(summary = "根据配置类型查询AI配置", description = "根据配置类型查询AI配置列表")
|
||||
@GetMapping("/byConfigType")
|
||||
public Result<List<AiConfigResponse>> getByConfigType(@RequestParam String configType) {
|
||||
List<AiConfigResponse> responses = aiConfigService.getByConfigTypeWithResponse(configType);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据服务提供商查询AI配置
|
||||
*/
|
||||
@Operation(summary = "根据服务提供商查询AI配置", description = "根据服务提供商查询AI配置列表")
|
||||
@GetMapping("/byProvider")
|
||||
public Result<List<AiConfigResponse>> getByProvider(@RequestParam String provider) {
|
||||
List<AiConfigResponse> responses = aiConfigService.getByProviderWithResponse(provider);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据使用场景查询AI配置
|
||||
*/
|
||||
@Operation(summary = "根据使用场景查询AI配置", description = "根据使用场景查询AI配置列表")
|
||||
@GetMapping("/byUsageScenario")
|
||||
public Result<List<AiConfigResponse>> getByUsageScenario(@RequestParam String usageScenario) {
|
||||
List<AiConfigResponse> responses = aiConfigService.getByUsageScenarioWithResponse(usageScenario);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据环境查询AI配置
|
||||
*/
|
||||
@Operation(summary = "根据环境查询AI配置", description = "根据环境查询AI配置列表")
|
||||
@GetMapping("/byEnvironment")
|
||||
public Result<List<AiConfigResponse>> getByEnvironment(@RequestParam String environment) {
|
||||
List<AiConfigResponse> responses = aiConfigService.getByEnvironmentWithResponse(environment);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已启用的AI配置
|
||||
*/
|
||||
@Operation(summary = "查询已启用的AI配置", description = "查询已启用的AI配置列表")
|
||||
@GetMapping("/enabled")
|
||||
public Result<List<AiConfigResponse>> getEnabledConfigs() {
|
||||
List<AiConfigResponse> responses = aiConfigService.getEnabledConfigsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已禁用的AI配置
|
||||
*/
|
||||
@Operation(summary = "查询已禁用的AI配置", description = "查询已禁用的AI配置列表")
|
||||
@GetMapping("/disabled")
|
||||
public Result<List<AiConfigResponse>> getDisabledConfigs() {
|
||||
List<AiConfigResponse> responses = aiConfigService.getDisabledConfigsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询默认配置
|
||||
*/
|
||||
@Operation(summary = "查询默认配置", description = "查询默认配置列表")
|
||||
@GetMapping("/default")
|
||||
public Result<List<AiConfigResponse>> getDefaultConfigs() {
|
||||
List<AiConfigResponse> responses = aiConfigService.getDefaultConfigsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置键值查询AI配置
|
||||
*/
|
||||
@Operation(summary = "根据配置键值查询AI配置", description = "根据配置键值查询AI配置")
|
||||
@GetMapping("/byConfigKey")
|
||||
public Result<AiConfigResponse> getByConfigKey(@RequestParam String configKey) {
|
||||
AiConfigResponse response = aiConfigService.getByConfigKeyWithResponse(configKey);
|
||||
if (response == null) {
|
||||
return Result.notFound("AI配置不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用AI配置
|
||||
*/
|
||||
@Operation(summary = "启用AI配置", description = "启用指定AI配置")
|
||||
@PutMapping("/enable")
|
||||
public Result<Void> enableConfig(@RequestParam String id) {
|
||||
boolean enabled = aiConfigService.enableConfig(id);
|
||||
if (!enabled) {
|
||||
return Result.error("启用失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用AI配置
|
||||
*/
|
||||
@Operation(summary = "禁用AI配置", description = "禁用指定AI配置")
|
||||
@PutMapping("/disable")
|
||||
public Result<Void> disableConfig(@RequestParam String id) {
|
||||
boolean disabled = aiConfigService.disableConfig(id);
|
||||
if (!disabled) {
|
||||
return Result.error("禁用失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置为默认配置
|
||||
*/
|
||||
@Operation(summary = "设置为默认配置", description = "设置指定AI配置为默认配置")
|
||||
@PutMapping("/setDefault")
|
||||
public Result<Void> setAsDefault(@RequestParam String id) {
|
||||
boolean set = aiConfigService.setAsDefault(id);
|
||||
if (!set) {
|
||||
return Result.error("设置默认配置失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消默认配置
|
||||
*/
|
||||
@Operation(summary = "取消默认配置", description = "取消指定AI配置的默认设置")
|
||||
@PutMapping("/unsetDefault")
|
||||
public Result<Void> unsetDefault(@RequestParam String id) {
|
||||
boolean unset = aiConfigService.unsetDefault(id);
|
||||
if (!unset) {
|
||||
return Result.error("取消默认配置失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据使用场景和环境查询最优配置
|
||||
*/
|
||||
@Operation(summary = "查询最优配置", description = "根据使用场景和环境查询最优配置")
|
||||
@GetMapping("/bestConfig")
|
||||
public Result<AiConfigResponse> getBestConfig(@RequestParam String usageScenario,
|
||||
@RequestParam String environment) {
|
||||
AiConfigResponse response = aiConfigService.getBestConfigWithResponse(usageScenario, environment);
|
||||
if (response == null) {
|
||||
return Result.notFound("未找到匹配的AI配置");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计已启用配置数量
|
||||
*/
|
||||
@Operation(summary = "统计已启用配置数量", description = "统计已启用配置数量")
|
||||
@GetMapping("/countEnabled")
|
||||
public Result<Long> countEnabledConfigs() {
|
||||
Long count = aiConfigService.countEnabledConfigs();
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计已禁用配置数量
|
||||
*/
|
||||
@Operation(summary = "统计已禁用配置数量", description = "统计已禁用配置数量")
|
||||
@GetMapping("/countDisabled")
|
||||
public Result<Long> countDisabledConfigs() {
|
||||
Long count = aiConfigService.countDisabledConfigs();
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计默认配置数量
|
||||
*/
|
||||
@Operation(summary = "统计默认配置数量", description = "统计默认配置数量")
|
||||
@GetMapping("/countDefault")
|
||||
public Result<Long> countDefaultConfigs() {
|
||||
Long count = aiConfigService.countDefaultConfigs();
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置类型统计数量
|
||||
*/
|
||||
@Operation(summary = "根据配置类型统计数量", description = "根据配置类型统计数量")
|
||||
@GetMapping("/countByConfigType")
|
||||
public Result<Long> countByConfigType(@RequestParam String configType) {
|
||||
Long count = aiConfigService.countByConfigType(configType);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据服务提供商统计数量
|
||||
*/
|
||||
@Operation(summary = "根据服务提供商统计数量", description = "根据服务提供商统计数量")
|
||||
@GetMapping("/countByProvider")
|
||||
public Result<Long> countByProvider(@RequestParam String provider) {
|
||||
Long count = aiConfigService.countByProvider(provider);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试后更新AI配置
|
||||
*/
|
||||
@Operation(summary = "测试后更新AI配置", description = "从测试请求中解析参数并更新配置")
|
||||
@PutMapping("/updateFromTest")
|
||||
public Result<AiConfigResponse> updateFromTest(@RequestBody @Validated AiConfigTestUpdateRequest request) {
|
||||
AiConfigResponse response = aiConfigService.updateFromTestRequest(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败,配置不存在");
|
||||
}
|
||||
return Result.success("更新成功", response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.ai.AiCallLogQueryRequest;
|
||||
import com.emotion.dto.request.ai.AiRuntimeRequest;
|
||||
import javax.validation.Valid;
|
||||
import com.emotion.dto.response.ai.AiTestTemplateResponse;
|
||||
import com.emotion.dto.response.ai.AiRuntimeTestResponse;
|
||||
import com.emotion.dto.response.ai.AiStreamEvent;
|
||||
import com.emotion.entity.AiCallLog;
|
||||
import com.emotion.entity.AiEndpointConfig;
|
||||
import com.emotion.entity.AiProvider;
|
||||
import com.emotion.entity.AiSceneBinding;
|
||||
import com.emotion.service.AiCallLogService;
|
||||
import com.emotion.service.AiEndpointConfigService;
|
||||
import com.emotion.service.AiProviderService;
|
||||
import com.emotion.service.AiRuntimeService;
|
||||
import com.emotion.service.AiSceneBindingService;
|
||||
import com.emotion.util.UserContextHolder;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/ai")
|
||||
@Tag(name = "AI 路由管理", description = "AI 服务提供商路由管理接口,包括 Provider/Endpoint/Scene 的 CRUD、调用日志、运行时测试和流式测试")
|
||||
public class AiRoutingController {
|
||||
|
||||
private final AiProviderService providerService;
|
||||
private final AiEndpointConfigService endpointConfigService;
|
||||
private final AiSceneBindingService sceneBindingService;
|
||||
private final AiCallLogService callLogService;
|
||||
private final AiRuntimeService runtimeService;
|
||||
|
||||
public AiRoutingController(AiProviderService providerService,
|
||||
AiEndpointConfigService endpointConfigService,
|
||||
AiSceneBindingService sceneBindingService,
|
||||
AiCallLogService callLogService,
|
||||
AiRuntimeService runtimeService) {
|
||||
this.providerService = providerService;
|
||||
this.endpointConfigService = endpointConfigService;
|
||||
this.sceneBindingService = sceneBindingService;
|
||||
this.callLogService = callLogService;
|
||||
this.runtimeService = runtimeService;
|
||||
}
|
||||
|
||||
@Operation(summary = "查询 Provider 列表", description = "查询所有可见的 AI Provider 列表。")
|
||||
@GetMapping("/providers")
|
||||
public Result<List<AiProvider>> providers() {
|
||||
return Result.success(providerService.listVisible());
|
||||
}
|
||||
|
||||
@Operation(summary = "创建 Provider", description = "创建一个新的 AI Provider 配置。")
|
||||
@PostMapping("/providers")
|
||||
public Result<AiProvider> createProvider(@RequestBody AiProvider provider) {
|
||||
return Result.success(providerService.saveProvider(provider));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新 Provider", description = "更新已有的 AI Provider 配置。")
|
||||
@PutMapping("/providers")
|
||||
public Result<AiProvider> updateProvider(@RequestBody AiProvider provider) {
|
||||
return Result.success(providerService.updateProvider(provider));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除 Provider", description = "根据 ID 删除指定的 AI Provider 配置。")
|
||||
@DeleteMapping("/providers")
|
||||
public Result<Void> deleteProvider(@Parameter(description = "Provider ID", required = true) @RequestParam String id) {
|
||||
providerService.removeById(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询 Endpoint 列表", description = "查询所有可见的 AI Endpoint 配置列表。")
|
||||
@GetMapping("/endpoints")
|
||||
public Result<List<AiEndpointConfig>> endpoints() {
|
||||
return Result.success(endpointConfigService.listVisible());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取 Endpoint 测试模板", description = "根据 Endpoint ID 获取对应的测试模板。")
|
||||
@GetMapping("/endpoints/test-template")
|
||||
public Result<AiTestTemplateResponse> endpointTestTemplate(@Parameter(description = "Endpoint ID", required = true) @RequestParam String id) {
|
||||
return Result.success(runtimeService.buildEndpointTestTemplate(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建 Endpoint", description = "创建一个新的 AI Endpoint 配置。")
|
||||
@PostMapping("/endpoints")
|
||||
public Result<AiEndpointConfig> createEndpoint(@RequestBody AiEndpointConfig endpoint) {
|
||||
return Result.success(endpointConfigService.saveEndpoint(endpoint));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新 Endpoint", description = "更新已有的 AI Endpoint 配置。")
|
||||
@PutMapping("/endpoints")
|
||||
public Result<AiEndpointConfig> updateEndpoint(@RequestBody AiEndpointConfig endpoint) {
|
||||
return Result.success(endpointConfigService.updateEndpoint(endpoint));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除 Endpoint", description = "根据 ID 删除指定的 AI Endpoint 配置。")
|
||||
@DeleteMapping("/endpoints")
|
||||
public Result<Void> deleteEndpoint(@Parameter(description = "Endpoint ID", required = true) @RequestParam String id) {
|
||||
endpointConfigService.removeById(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询 Scene 列表", description = "查询所有可见的 AI Scene 绑定配置列表。")
|
||||
@GetMapping("/scenes")
|
||||
public Result<List<AiSceneBinding>> scenes() {
|
||||
return Result.success(sceneBindingService.listVisible());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取 Scene 测试模板", description = "根据场景编码获取对应的测试模板。")
|
||||
@GetMapping("/scenes/test-template")
|
||||
public Result<AiTestTemplateResponse> sceneTestTemplate(@Parameter(description = "场景编码", required = true) @RequestParam String sceneCode) {
|
||||
return Result.success(runtimeService.buildSceneTestTemplate(sceneCode));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建 Scene", description = "创建一个新的 AI Scene 绑定配置。")
|
||||
@PostMapping("/scenes")
|
||||
public Result<AiSceneBinding> createScene(@RequestBody AiSceneBinding scene) {
|
||||
if (scene.getIsEnabled() == null) {
|
||||
scene.setIsEnabled(1);
|
||||
}
|
||||
if (scene.getRequiredStream() == null) {
|
||||
scene.setRequiredStream(1);
|
||||
}
|
||||
sceneBindingService.save(scene);
|
||||
return Result.success(scene);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新 Scene", description = "更新已有的 AI Scene 绑定配置。")
|
||||
@PutMapping("/scenes")
|
||||
public Result<AiSceneBinding> updateScene(@RequestBody AiSceneBinding scene) {
|
||||
sceneBindingService.updateById(scene);
|
||||
return Result.success(sceneBindingService.getById(scene.getId()));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除 Scene", description = "根据 ID 删除指定的 AI Scene 绑定配置。")
|
||||
@DeleteMapping("/scenes")
|
||||
public Result<Void> deleteScene(@Parameter(description = "Scene ID", required = true) @RequestParam String id) {
|
||||
sceneBindingService.removeById(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询调用日志", description = "查询 AI 调用日志列表,支持限制返回数量。")
|
||||
@GetMapping("/call-logs")
|
||||
public Result<List<AiCallLog>> callLogs(@Parameter(description = "返回数量限制") @RequestParam(required = false) Integer limit) {
|
||||
return Result.success(callLogService.latest(limit));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询调用日志", description = "分页查询 AI 调用日志,支持多条件筛选和关键词搜索。")
|
||||
@PostMapping("/call-logs")
|
||||
public Result<PageResult<AiCallLog>> queryCallLogs(@RequestBody @Valid AiCallLogQueryRequest request) {
|
||||
return Result.success(callLogService.query(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询运行时调用结果", description = "用户端根据 requestId 查询刚刚触发的 AI 调用结果,用于流式连接异常后的结果恢复。")
|
||||
@GetMapping("/runtime/result")
|
||||
public Result<AiCallLog> runtimeResult(@RequestParam String requestId) {
|
||||
AiCallLog log = callLogService.findByRequestId(requestId, UserContextHolder.getCurrentUserId());
|
||||
if (log == null) {
|
||||
return Result.notFound("AI 调用结果未生成");
|
||||
}
|
||||
return Result.success(log);
|
||||
}
|
||||
|
||||
@Operation(summary = "运行时测试", description = "对指定的 AI 配置进行运行时连通性测试,支持同步和流式模式。")
|
||||
@PostMapping("/runtime/test")
|
||||
public Result<AiRuntimeTestResponse> runtimeTest(@RequestBody JSONObject payload) {
|
||||
AiRuntimeRequest request = withCurrentUser(AiRuntimeRequest.fromPayload(payload));
|
||||
return Result.success(runtimeService.test(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "流式运行时测试", description = "对指定的 AI 配置进行流式运行时连通性测试,以 SSE 事件流返回结果。")
|
||||
@PostMapping("/runtime/stream")
|
||||
public SseEmitter runtimeStream(@RequestBody JSONObject payload) {
|
||||
AiRuntimeRequest request = withCurrentUser(AiRuntimeRequest.fromPayload(payload));
|
||||
SseEmitter emitter = new SseEmitter(0L);
|
||||
CompletableFuture.runAsync(() -> {
|
||||
runtimeService.invokeStream(request, event -> sendEvent(emitter, event));
|
||||
emitter.complete();
|
||||
}).exceptionally(error -> {
|
||||
sendEvent(emitter, AiStreamEvent.error("AI_STREAM_INTERRUPTED", error.getMessage()));
|
||||
emitter.completeWithError(error);
|
||||
return null;
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
|
||||
@Operation(summary = "Endpoint 运行时测试", description = "对指定的 Endpoint 进行运行时连通性测试。")
|
||||
@PostMapping("/endpoint/test")
|
||||
public Result<AiRuntimeTestResponse> endpointTest(@RequestBody JSONObject payload) {
|
||||
String endpointId = payload.getString("endpointId");
|
||||
JSONObject inputs = payload.getJSONObject("inputs");
|
||||
Map<String, Object> inputMap = inputs == null ? Map.of() : inputs;
|
||||
return Result.success(runtimeService.testEndpoint(endpointId, inputMap));
|
||||
}
|
||||
|
||||
@Operation(summary = "Endpoint 流式测试", description = "对指定的 Endpoint 进行流式运行时测试,以 SSE 事件流返回结果。")
|
||||
@PostMapping("/endpoint/stream")
|
||||
public SseEmitter endpointStream(@RequestBody JSONObject payload) {
|
||||
String endpointId = payload.getString("endpointId");
|
||||
JSONObject inputs = payload.getJSONObject("inputs");
|
||||
Map<String, Object> inputMap = inputs == null ? Map.of() : inputs;
|
||||
SseEmitter emitter = new SseEmitter(0L);
|
||||
CompletableFuture.runAsync(() -> {
|
||||
runtimeService.invokeEndpointStream(endpointId, inputMap, event -> sendEvent(emitter, event));
|
||||
emitter.complete();
|
||||
}).exceptionally(error -> {
|
||||
sendEvent(emitter, AiStreamEvent.error("AI_ENDPOINT_TEST_INTERRUPTED", error.getMessage()));
|
||||
emitter.completeWithError(error);
|
||||
return null;
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
|
||||
private AiRuntimeRequest withCurrentUser(AiRuntimeRequest request) {
|
||||
request.setUserId(UserContextHolder.getCurrentUserId());
|
||||
request.setUserName(UserContextHolder.getCurrentUsername());
|
||||
request.setUserType(UserContextHolder.getCurrentUserType());
|
||||
if (!org.springframework.util.StringUtils.hasText(request.getRequestId())) {
|
||||
request.setRequestId(UserContextHolder.getRequestId());
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private void sendEvent(SseEmitter emitter, AiStreamEvent event) {
|
||||
try {
|
||||
emitter.send(SseEmitter.event()
|
||||
.name(event.getType())
|
||||
.data(event));
|
||||
} catch (IOException e) {
|
||||
log.warn("AI stream client disconnected: {}", e.getMessage());
|
||||
throw new IllegalStateException("AI_STREAM_CLIENT_DISCONNECTED", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.analytics.AnalyticsEventBatchRequest;
|
||||
import com.emotion.dto.response.analytics.AnalyticsBatchResponse;
|
||||
import com.emotion.service.AnalyticsService;
|
||||
import com.emotion.util.JwtUtil;
|
||||
import com.emotion.util.UserContextHolder;
|
||||
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.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/analytics")
|
||||
@Tag(name = "事件分析", description = "前端行为事件上报和分析接口")
|
||||
public class AnalyticsController {
|
||||
|
||||
@Autowired
|
||||
private AnalyticsService analyticsService;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@PostMapping("/events/batch")
|
||||
public Result<AnalyticsBatchResponse> batch(@Valid @RequestBody AnalyticsEventBatchRequest request,
|
||||
HttpServletRequest servletRequest) {
|
||||
bindOptionalUser(servletRequest);
|
||||
try {
|
||||
return Result.success(analyticsService.ingestBatch(request));
|
||||
} finally {
|
||||
UserContextHolder.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void bindOptionalUser(HttpServletRequest request) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String token = authHeader.substring(7);
|
||||
if (!jwtUtil.validateToken(token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserContextHolder.setCurrentUserId(jwtUtil.getUserIdFromToken(token));
|
||||
UserContextHolder.setCurrentUsername(jwtUtil.getUsernameFromToken(token));
|
||||
UserContextHolder.setCurrentToken(token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
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.ApiEndpointListRequest;
|
||||
import com.emotion.dto.response.ApiEndpointDetailResponse;
|
||||
import com.emotion.dto.response.ApiEndpointItemResponse;
|
||||
import com.emotion.service.ApiEndpointService;
|
||||
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.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 接口端点控制器
|
||||
*
|
||||
* @author Peanut
|
||||
* @date 2026-05-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/endpoint")
|
||||
@Tag(name = "接口管理", description = "接口发现、同步和测试")
|
||||
public class ApiEndpointController {
|
||||
|
||||
@Autowired
|
||||
private ApiEndpointService apiEndpointService;
|
||||
|
||||
@Operation(summary = "分页查询接口列表", description = "支持关键词、方法、标签过滤")
|
||||
@PostMapping("/list")
|
||||
public Result<PageResult<ApiEndpointItemResponse>> list(@Valid @RequestBody ApiEndpointListRequest request) {
|
||||
IPage<ApiEndpointItemResponse> page = apiEndpointService.getPage(request);
|
||||
PageResult<ApiEndpointItemResponse> result = new PageResult<>(page);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询接口详情", description = "返回接口详情和参数列表")
|
||||
@GetMapping("/detail")
|
||||
public Result<ApiEndpointDetailResponse> detail(@RequestParam String operationId) {
|
||||
ApiEndpointDetailResponse response = apiEndpointService.getDetail(operationId);
|
||||
if (response == null) {
|
||||
return Result.notFound("接口不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
@Operation(summary = "手动触发同步", description = "异步执行,返回 taskId")
|
||||
@PostMapping("/sync")
|
||||
public Result<Map<String, String>> sync() {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("taskId", "sync-" + System.currentTimeMillis());
|
||||
new Thread(() -> {
|
||||
try {
|
||||
apiEndpointService.syncFromOpenApi();
|
||||
} catch (Exception e) {
|
||||
// 异常已在 service 层记录
|
||||
}
|
||||
}).start();
|
||||
return Result.success("同步任务已提交", result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.ApiTestProxyRequest;
|
||||
import com.emotion.dto.response.ApiTestProxyResponse;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
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.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpServerErrorException;
|
||||
import org.springframework.web.client.ResourceAccessException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 接口代理测试控制器
|
||||
* 仅允许转发到 /api/* 路径,避免 SSRF
|
||||
*
|
||||
* @author Peanut
|
||||
* @date 2026-05-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/endpoint")
|
||||
@Tag(name = "接口管理", description = "接口测试代理")
|
||||
public class ApiTestProxyController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ApiTestProxyController.class);
|
||||
private static final int DEFAULT_TIMEOUT = 30;
|
||||
private static final int MAX_RAW_BODY_LENGTH = 2000;
|
||||
|
||||
@Value("${server.port:19089}")
|
||||
private int serverPort;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Operation(summary = "代理测试请求", description = "转发请求到本地后端并返回响应")
|
||||
@PostMapping("/test")
|
||||
public Result<ApiTestProxyResponse> test(@Valid @RequestBody ApiTestProxyRequest request) {
|
||||
if (!request.getPath().startsWith("/api/")) {
|
||||
return Result.error("仅允许代理 /api/* 路径的请求");
|
||||
}
|
||||
|
||||
String url = "http://127.0.0.1:" + serverPort + request.getPath();
|
||||
int timeout = request.getTimeoutSeconds() != null ? request.getTimeoutSeconds() : DEFAULT_TIMEOUT;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
if (request.getHeaders() != null) {
|
||||
for (Map.Entry<String, String> entry : request.getHeaders().entrySet()) {
|
||||
headers.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// Append query params to URL if present
|
||||
if (request.getParams() != null && !request.getParams().isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder(url);
|
||||
sb.append("?");
|
||||
for (Map.Entry<String, String> entry : request.getParams().entrySet()) {
|
||||
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
|
||||
}
|
||||
url = sb.substring(0, sb.length() - 1);
|
||||
}
|
||||
|
||||
Object body = null;
|
||||
if (request.getBody() != null && !request.getBody().isBlank()) {
|
||||
try {
|
||||
body = objectMapper.readValue(request.getBody(), Object.class);
|
||||
} catch (Exception e) {
|
||||
return Result.error("请求体 JSON 格式错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
HttpMethod httpMethod = HttpMethod.valueOf(request.getMethod().toUpperCase());
|
||||
HttpEntity<Object> entity = new HttpEntity<>(body, headers);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
ApiTestProxyResponse response = new ApiTestProxyResponse();
|
||||
|
||||
try {
|
||||
ResponseEntity<String> rawResponse = restTemplate.exchange(url, httpMethod, entity, String.class);
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
|
||||
response.setStatus(rawResponse.getStatusCodeValue());
|
||||
response.setDuration(duration);
|
||||
|
||||
try {
|
||||
response.setBody(objectMapper.readValue(rawResponse.getBody(), Object.class));
|
||||
} catch (Exception e) {
|
||||
String rawBody = rawResponse.getBody();
|
||||
if (rawBody != null && rawBody.length() > MAX_RAW_BODY_LENGTH) {
|
||||
rawBody = rawBody.substring(0, MAX_RAW_BODY_LENGTH) + "\n... (已截断)";
|
||||
}
|
||||
if (rawBody != null) {
|
||||
rawBody = rawBody.replace("<", "<").replace(">", ">");
|
||||
}
|
||||
response.setRawBody(rawBody);
|
||||
}
|
||||
|
||||
Map<String, String> respHeaders = new HashMap<>();
|
||||
for (String key : rawResponse.getHeaders().keySet()) {
|
||||
respHeaders.put(key, rawResponse.getHeaders().getFirst(key));
|
||||
}
|
||||
response.setHeaders(respHeaders);
|
||||
|
||||
return Result.success(response);
|
||||
|
||||
} catch (ResourceAccessException e) {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
if (e.getCause() instanceof SocketTimeoutException) {
|
||||
return Result.error("代理请求超时(" + timeout + "s),目标接口可能响应过慢或不可达");
|
||||
}
|
||||
return Result.error("代理请求失败: " + e.getMessage());
|
||||
} catch (HttpClientErrorException | HttpServerErrorException e) {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
response.setStatus(e.getStatusCode().value());
|
||||
response.setDuration(duration);
|
||||
String rawBody = e.getResponseBodyAsString();
|
||||
if (rawBody.length() > MAX_RAW_BODY_LENGTH) {
|
||||
rawBody = rawBody.substring(0, MAX_RAW_BODY_LENGTH) + "\n... (已截断)";
|
||||
}
|
||||
response.setRawBody(rawBody.replace("<", "<").replace(">", ">"));
|
||||
return Result.success(response);
|
||||
} catch (Exception e) {
|
||||
return Result.error("代理请求失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.response.asr.AsrTranscribeResponse;
|
||||
import com.emotion.service.AsrService;
|
||||
import com.emotion.util.UserContextHolder;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/asr")
|
||||
@Tag(name = "语音识别(ASR)", description = "语音转文字识别接口")
|
||||
public class AsrController {
|
||||
|
||||
private final AsrService asrService;
|
||||
|
||||
public AsrController(AsrService asrService) {
|
||||
this.asrService = asrService;
|
||||
}
|
||||
|
||||
@Operation(summary = "语音转文字", description = "上传音频文件并将其转换为文字内容。")
|
||||
@PostMapping("/transcribe")
|
||||
public Result<AsrTranscribeResponse> transcribe(@Parameter(description = "音频文件") @RequestPart("file") MultipartFile file) {
|
||||
if (UserContextHolder.getCurrentUserId() == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
try {
|
||||
return Result.success(asrService.transcribe(file));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.badRequest(e.getMessage());
|
||||
} catch (IllegalStateException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.LoginRequest;
|
||||
import com.emotion.dto.request.RegisterRequest;
|
||||
import com.emotion.dto.request.RefreshTokenRequest;
|
||||
import com.emotion.dto.request.ResetPasswordRequest;
|
||||
import com.emotion.dto.request.WechatLoginRequest;
|
||||
import com.emotion.dto.response.ResetPasswordResponse;
|
||||
|
||||
import com.emotion.dto.response.AuthResponse;
|
||||
import com.emotion.dto.response.CaptchaResponse;
|
||||
import com.emotion.dto.response.LoginConfigResponse;
|
||||
import com.emotion.dto.response.SmsCodeResponse;
|
||||
import com.emotion.dto.response.UserInfoResponse;
|
||||
import com.emotion.service.AuthService;
|
||||
import com.emotion.service.SystemConfigService;
|
||||
import com.emotion.service.TokenService;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 认证控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
@Tag(name = "认证管理", description = "用户注册、登录、验证码等认证相关接口")
|
||||
public class AuthController {
|
||||
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
@Autowired
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 用户登录(简化版:手机号+验证码,不存在则自动注册)
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "用户登录", description = "使用手机号和短信验证码登录,若用户不存在则自动注册")
|
||||
public Result<AuthResponse> login(@Valid @RequestBody LoginRequest request) {
|
||||
AuthResponse response = authService.login(request);
|
||||
return Result.success("登录成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册(简化版:仅需手机号、密码和短信验证码)
|
||||
*/
|
||||
@PostMapping("/wechat/login")
|
||||
@Operation(summary = "微信小程序登录", description = "使用微信小程序登录 code 换取 openid 并登录")
|
||||
public Result<AuthResponse> wechatLogin(@Valid @RequestBody WechatLoginRequest request) {
|
||||
AuthResponse response = authService.wechatLogin(request);
|
||||
return Result.success("登录成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录方式配置
|
||||
*/
|
||||
@GetMapping("/loginConfig")
|
||||
@Operation(summary = "获取登录方式配置", description = "返回当前启用的登录方式,供小程序登录页动态渲染")
|
||||
public Result<LoginConfigResponse> getLoginConfig() {
|
||||
LoginConfigResponse response = systemConfigService.getLoginConfig();
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/register")
|
||||
@Operation(summary = "用户注册", description = "使用手机号、密码和短信验证码进行注册")
|
||||
public Result<AuthResponse> register(@Valid @RequestBody RegisterRequest request) {
|
||||
AuthResponse response = authService.register(request);
|
||||
return Result.success("注册成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码(手机号 + 验证码)
|
||||
*/
|
||||
@PostMapping(value = "/resetPassword")
|
||||
@Operation(summary = "重置密码", description = "通过手机号和验证码重置密码,验证码本期固定为123456")
|
||||
public Result<ResetPasswordResponse> resetPassword(@Valid @RequestBody ResetPasswordRequest request) {
|
||||
ResetPasswordResponse response = authService.resetPassword(request);
|
||||
return Result.success("重置密码成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
@GetMapping("/userInfo")
|
||||
@Operation(summary = "获取当前用户信息", description = "根据当前登录用户的Token获取用户详细信息")
|
||||
public Result<UserInfoResponse> getCurrentUserInfo(HttpServletRequest request) {
|
||||
UserInfoResponse userInfo = tokenService.getUserInfoByToken(request);
|
||||
return Result.success(userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成验证码(图形验证码,用于登录)
|
||||
*/
|
||||
@GetMapping("/captcha")
|
||||
@Operation(summary = "获取图形验证码", description = "用于登录时的图形验证码")
|
||||
public Result<CaptchaResponse> generateCaptcha() {
|
||||
CaptchaResponse response = authService.generateCaptcha();
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取短信验证码(用于注册)
|
||||
*/
|
||||
@GetMapping("/sms-code")
|
||||
@Operation(summary = "获取短信验证码", description = "用于注册时的短信验证码")
|
||||
public Result<SmsCodeResponse> getSmsCode(
|
||||
@Parameter(description = "手机号", required = true)
|
||||
@RequestParam String phone) {
|
||||
SmsCodeResponse response = authService.sendSmsCode(phone);
|
||||
return Result.success("验证码已发送", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
@Operation(summary = "用户登出", description = "退出登录,清除服务端Token")
|
||||
public Result<Void> logout(HttpServletRequest request) {
|
||||
authService.logoutByToken(request);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*/
|
||||
@PostMapping("/refreshToken")
|
||||
@Operation(summary = "刷新访问令牌", description = "使用刷新令牌获取新的访问令牌和刷新令牌")
|
||||
public Result<AuthResponse> refreshToken(@Valid @RequestBody RefreshTokenRequest request) {
|
||||
AuthResponse response = authService.refreshToken(request.getRefreshToken());
|
||||
return Result.success("令牌刷新成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证访问令牌
|
||||
*/
|
||||
@GetMapping("/validateToken")
|
||||
@Operation(summary = "验证访问令牌", description = "验证当前Token是否有效")
|
||||
public Result<Boolean> validateToken(HttpServletRequest request) {
|
||||
boolean isValid = authService.validateToken(request);
|
||||
return Result.success(isValid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户名(通过令牌)
|
||||
*/
|
||||
@GetMapping("/username")
|
||||
@Operation(summary = "获取用户名", description = "根据Token获取当前登录用户的用户名")
|
||||
public Result<String> getUsernameFromToken(HttpServletRequest request) {
|
||||
String username = tokenService.getUsernameByToken(request);
|
||||
return Result.success(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查账号是否存在
|
||||
*/
|
||||
@GetMapping("/checkAccount")
|
||||
@Operation(summary = "检查账号是否存在", description = "检查指定账号是否已被注册")
|
||||
public Result<Boolean> checkAccount(
|
||||
@Parameter(description = "账号(手机号/邮箱/用户名)", required = true)
|
||||
@RequestParam String account) {
|
||||
boolean exists = authService.existsByAccount(account);
|
||||
return Result.success(exists);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查邮箱是否存在
|
||||
*/
|
||||
@GetMapping("/checkEmail")
|
||||
@Operation(summary = "检查邮箱是否存在", description = "检查指定邮箱是否已被注册")
|
||||
public Result<Boolean> checkEmail(
|
||||
@Parameter(description = "邮箱地址", required = true)
|
||||
@RequestParam String email) {
|
||||
boolean exists = authService.existsByEmail(email);
|
||||
return Result.success(exists);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查手机号是否存在
|
||||
*/
|
||||
@GetMapping("/checkPhone")
|
||||
@Operation(summary = "检查手机号是否存在", description = "检查指定手机号是否已被注册")
|
||||
public Result<Boolean> checkPhone(
|
||||
@Parameter(description = "手机号码", required = true)
|
||||
@RequestParam String phone) {
|
||||
boolean exists = authService.existsByPhone(phone);
|
||||
return Result.success(exists);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.dto.request.WebSocketRequest;
|
||||
import com.emotion.dto.websocket.ConnectRequest;
|
||||
import com.emotion.service.WebSocketService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* WebSocket聊天控制器
|
||||
* 使用规范的请求对象封装参数,符合项目开发规则
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
@MessageMapping("/chat")
|
||||
@Tag(name = "WebSocket 聊天", description = "基于 WebSocket 的实时 AI 聊天通信接口")
|
||||
public class ChatWebSocketController {
|
||||
|
||||
@Autowired
|
||||
private WebSocketService webSocketService;
|
||||
|
||||
/**
|
||||
* 处理聊天消息
|
||||
*
|
||||
* @param webSocketRequest WebSocket请求对象
|
||||
* @param headerAccessor 消息头访问器
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
@Operation(summary = "发送聊天消息", description = "向 AI 发送聊天消息,触发流式响应。")
|
||||
@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 用户主体
|
||||
*/
|
||||
@Operation(summary = "用户连接", description = "处理用户 WebSocket 连接建立。")
|
||||
@MessageMapping("/connect")
|
||||
public void connectUser(@Payload ConnectRequest connectRequest,
|
||||
SimpMessageHeaderAccessor headerAccessor,
|
||||
Principal principal) {
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
webSocketService.handleUserConnect(connectRequest, sessionId, principal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户断开连接
|
||||
*
|
||||
* @param headerAccessor 消息头访问器
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
@Operation(summary = "用户断开连接", description = "处理用户 WebSocket 连接断开。")
|
||||
@MessageMapping("/disconnect")
|
||||
public void disconnectUser(SimpMessageHeaderAccessor headerAccessor, Principal principal) {
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
webSocketService.handleUserDisconnect(sessionId, principal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理心跳消息
|
||||
*
|
||||
* @param headerAccessor 消息头访问器
|
||||
* @param principal 用户主体
|
||||
*/
|
||||
@Operation(summary = "心跳检测", description = "处理客户端心跳消息,保持 WebSocket 连接活跃。")
|
||||
@MessageMapping("/heartbeat")
|
||||
public void heartbeat(SimpMessageHeaderAccessor headerAccessor, Principal principal) {
|
||||
String sessionId = headerAccessor.getSessionId();
|
||||
webSocketService.handleHeartbeat(sessionId, principal);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
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 com.emotion.util.UserContextUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* 评论控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/comment")
|
||||
@Tag(name = "评论管理", description = "评论的查询、创建、更新和删除接口")
|
||||
public class CommentController {
|
||||
|
||||
@Autowired
|
||||
private CommentService commentService;
|
||||
|
||||
/**
|
||||
* 分页查询评论
|
||||
*/
|
||||
@Operation(summary = "分页查询评论", description = "根据条件分页查询评论列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<CommentResponse>> getCommentPage(@Validated CommentPageRequest request) {
|
||||
PageResult<CommentResponse> pageResult = commentService.getPage(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建评论
|
||||
*/
|
||||
@Operation(summary = "创建评论", description = "对指定目标发表一条新评论。")
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新评论
|
||||
*/
|
||||
@Operation(summary = "更新评论", description = "修改已有评论的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<CommentResponse> updateComment(@RequestBody @Validated CommentUpdateRequest request) {
|
||||
CommentResponse response = commentService.update(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*/
|
||||
@Operation(summary = "删除评论", description = "删除指定的评论。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> deleteComment(@Parameter(description = "评论 ID", required = true) @RequestParam String id) {
|
||||
boolean deleted = commentService.delete(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取评论
|
||||
*/
|
||||
@Operation(summary = "获取评论详情", description = "根据 ID 获取评论的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<CommentResponse> getCommentById(@Parameter(description = "评论 ID", required = true) @RequestParam String id) {
|
||||
CommentResponse response = commentService.getById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("评论不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* 社区帖子控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/communityPost")
|
||||
@Tag(name = "社区帖子管理", description = "社区帖子的查询、创建、更新和删除接口")
|
||||
public class CommunityPostController {
|
||||
|
||||
@Autowired
|
||||
private CommunityPostService communityPostService;
|
||||
|
||||
/**
|
||||
* 分页查询帖子
|
||||
*/
|
||||
@Operation(summary = "分页查询社区帖子", description = "根据条件分页查询社区帖子列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<CommunityPostResponse>> getPage(@Validated CommunityPostPageRequest request) {
|
||||
PageResult<CommunityPostResponse> pageResult = communityPostService.getPage(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取帖子
|
||||
*/
|
||||
@Operation(summary = "获取帖子详情", description = "根据 ID 获取社区帖子的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<CommunityPostResponse> getById(@Parameter(description = "帖子 ID", required = true) @RequestParam String id) {
|
||||
CommunityPostResponse response = communityPostService.getById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("帖子不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建帖子
|
||||
*/
|
||||
@Operation(summary = "创建社区帖子", description = "发布一篇新的社区帖子。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<CommunityPostResponse> create(@RequestBody @Validated CommunityPostCreateRequest request) {
|
||||
CommunityPostResponse response = communityPostService.create(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新帖子
|
||||
*/
|
||||
@Operation(summary = "更新社区帖子", description = "修改已有的社区帖子内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<CommunityPostResponse> update(@RequestBody @Validated CommunityPostUpdateRequest request) {
|
||||
CommunityPostResponse response = communityPostService.update(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除帖子
|
||||
*/
|
||||
@Operation(summary = "删除社区帖子", description = "删除指定的社区帖子。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "帖子 ID", required = true) @RequestParam String id) {
|
||||
boolean deleted = communityPostService.delete(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.ConversationCreateRequest;
|
||||
import com.emotion.dto.request.ConversationPageRequest;
|
||||
import com.emotion.dto.response.ConversationResponse;
|
||||
import com.emotion.service.ConversationService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 对话控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/conversation")
|
||||
@Tag(name = "会话管理", description = "AI 对话会话的查询、创建、更新、删除和状态管理接口")
|
||||
public class ConversationController {
|
||||
|
||||
@Autowired
|
||||
private ConversationService conversationService;
|
||||
|
||||
/**
|
||||
* 分页查询对话
|
||||
*/
|
||||
@Operation(summary = "分页查询会话", description = "分页查询当前用户的对话会话列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<ConversationResponse>> getPage(@Valid ConversationPageRequest request) {
|
||||
PageResult<ConversationResponse> pageResult = conversationService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取对话
|
||||
*/
|
||||
@Operation(summary = "获取会话详情", description = "根据 ID 获取会话的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<ConversationResponse> getById(@Parameter(description = "会话 ID") @RequestParam String id) {
|
||||
ConversationResponse response = conversationService.getConversationResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("对话不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建对话
|
||||
*/
|
||||
@Operation(summary = "创建会话", description = "创建一个新的 AI 对话会话。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<ConversationResponse> create(@Valid @RequestBody ConversationCreateRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
ConversationResponse response = conversationService.createConversationWithResponse(request, httpRequest);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对话
|
||||
*/
|
||||
@Operation(summary = "更新会话", description = "修改已有会话的标题等属性。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<ConversationResponse> update(@RequestBody ConversationCreateRequest request) {
|
||||
ConversationResponse response = conversationService.updateConversationWithResponse(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对话
|
||||
*/
|
||||
@Operation(summary = "删除会话", description = "删除指定的对话会话。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "会话 ID") @RequestParam String id) {
|
||||
boolean deleted = conversationService.removeById(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃对话
|
||||
*/
|
||||
@Operation(summary = "获取活跃会话", description = "获取当前用户最近的活跃会话列表。")
|
||||
@GetMapping(value = "/active")
|
||||
public Result<List<ConversationResponse>> getActiveConversations() {
|
||||
List<ConversationResponse> responses = conversationService.getActiveConversationsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取归档对话
|
||||
*/
|
||||
@Operation(summary = "获取归档会话", description = "获取当前用户已归档的会话列表。")
|
||||
@GetMapping(value = "/archived")
|
||||
public Result<List<ConversationResponse>> getArchivedConversations() {
|
||||
List<ConversationResponse> responses = conversationService.getArchivedConversationsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对话状态
|
||||
*/
|
||||
@Operation(summary = "获取会话状态", description = "获取指定会话的当前状态信息。")
|
||||
@PutMapping(value = "/status")
|
||||
public Result<Void> updateConversationStatus(
|
||||
@Parameter(description = "会话 ID") @RequestParam String id,
|
||||
@RequestParam String status) {
|
||||
boolean updated = conversationService.updateConversationStatus(id, status);
|
||||
if (!updated) {
|
||||
return Result.error("更新状态失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Coze API调用记录控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/coze-api-call")
|
||||
@Tag(name = "Coze API 调用记录", description = "Coze API 调用记录的查询、创建、更新和删除接口")
|
||||
public class CozeApiCallController {
|
||||
|
||||
@Autowired
|
||||
private CozeApiCallService cozeApiCallService;
|
||||
|
||||
/**
|
||||
* 分页查询API调用记录
|
||||
*/
|
||||
@Operation(summary = "分页查询调用记录", description = "根据条件分页查询 Coze API 调用记录。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<CozeApiCallResponse>> getCozeApiCallPage(@Validated CozeApiCallPageRequest request) {
|
||||
PageResult<CozeApiCallResponse> pageResult = cozeApiCallService.getPage(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取API调用记录
|
||||
*/
|
||||
@Operation(summary = "获取调用记录详情", description = "根据 ID 获取单条调用记录的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<CozeApiCallResponse> getCozeApiCallById(@Parameter(description = "调用记录 ID", required = true) @RequestParam String id) {
|
||||
CozeApiCallResponse response = cozeApiCallService.getById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("API调用记录不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建API调用记录
|
||||
*/
|
||||
@Operation(summary = "创建调用记录", description = "新增一条 Coze API 调用记录。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<CozeApiCallResponse> createCozeApiCall(@RequestBody @Validated CozeApiCallCreateRequest request) {
|
||||
CozeApiCallResponse response = cozeApiCallService.create(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新API调用记录
|
||||
*/
|
||||
@Operation(summary = "更新调用记录", description = "更新已有的 Coze API 调用记录。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<CozeApiCallResponse> updateCozeApiCall(@RequestBody @Validated CozeApiCallUpdateRequest request) {
|
||||
CozeApiCallResponse response = cozeApiCallService.update(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败,记录不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除API调用记录
|
||||
*/
|
||||
@Operation(summary = "删除调用记录", description = "删除指定的 Coze API 调用记录。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> deleteCozeApiCall(@Parameter(description = "调用记录 ID", required = true) @RequestParam String id) {
|
||||
boolean deleted = cozeApiCallService.delete(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户的API调用次数
|
||||
*/
|
||||
@Operation(summary = "按用户统计调用量", description = "统计指定用户在时间范围内的 Coze API 调用次数。")
|
||||
@GetMapping(value = "/countByUser")
|
||||
public Result<Long> countByUserId(@Parameter(description = "用户 ID", required = true) @RequestParam String userId) {
|
||||
Long count = cozeApiCallService.countByUserId(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计Bot的API调用次数
|
||||
*/
|
||||
@Operation(summary = "按 Bot 统计调用量", description = "统计指定 Bot 的 Coze API 调用次数。")
|
||||
@GetMapping(value = "/countByBot")
|
||||
public Result<Long> countByBotId(@Parameter(description = "Bot ID", required = true) @RequestParam String botId) {
|
||||
Long count = cozeApiCallService.countByBotId(botId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计指定状态的API调用次数
|
||||
*/
|
||||
@Operation(summary = "按状态统计调用量", description = "按调用状态统计 Coze API 调用次数。")
|
||||
@GetMapping(value = "/countByStatus")
|
||||
public Result<Long> countByStatus(@RequestParam String status) {
|
||||
Long count = cozeApiCallService.countByStatus(status);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户的Token使用量
|
||||
*/
|
||||
@Operation(summary = "按用户统计 Token 消耗", description = "统计指定用户的 Token 总消耗量。")
|
||||
@GetMapping(value = "/tokensByUser")
|
||||
public Result<Long> sumTokensByUserId(@Parameter(description = "用户 ID", required = true) @RequestParam String userId) {
|
||||
Long totalTokens = cozeApiCallService.sumTokensByUserId(userId);
|
||||
return Result.success(totalTokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户的API调用费用
|
||||
*/
|
||||
@Operation(summary = "按用户统计费用", description = "统计指定用户的 API 调用总费用。")
|
||||
@GetMapping(value = "/costByUser")
|
||||
public Result<BigDecimal> sumCostByUserId(@Parameter(description = "用户 ID", required = true) @RequestParam String userId) {
|
||||
BigDecimal totalCost = cozeApiCallService.sumCostByUserId(userId);
|
||||
return Result.success(totalCost);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
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.service.DiaryCommentService;
|
||||
import com.emotion.service.DiaryPostService;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 日记评论控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/diaryComment")
|
||||
@Tag(name = "日记评论管理", description = "日记评论的查询、创建、更新、删除、点赞、置顶等接口")
|
||||
public class DiaryCommentController {
|
||||
|
||||
@Autowired
|
||||
private DiaryCommentService diaryCommentService;
|
||||
|
||||
@Autowired
|
||||
private DiaryPostService diaryPostService;
|
||||
|
||||
/**
|
||||
* 分页查询评论
|
||||
*/
|
||||
@Operation(summary = "分页查询评论", description = "分页查询指定日记的评论列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<DiaryCommentResponse>> getPage(@Validated DiaryCommentPageRequest request) {
|
||||
PageResult<DiaryCommentResponse> pageResult = diaryCommentService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评论树结构
|
||||
*/
|
||||
@Operation(summary = "获取评论树", description = "以树形结构获取日记的评论及其回复列表。")
|
||||
@GetMapping(value = "/commentTree")
|
||||
public Result<List<DiaryCommentResponse>> getCommentTree(@Parameter(description = "日记 ID") @RequestParam String diaryId) {
|
||||
List<DiaryCommentResponse> responses = diaryCommentService.getCommentTreeWithResponse(diaryId);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取评论详情
|
||||
*/
|
||||
@Operation(summary = "获取评论详情", description = "根据 ID 获取评论详情。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<DiaryCommentResponse> getById(@Parameter(description = "评论 ID") @RequestParam String id) {
|
||||
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("评论不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建评论
|
||||
*/
|
||||
@Operation(summary = "创建评论", description = "对指定日记发表一条新评论或回复。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<DiaryCommentResponse> create(@Valid @RequestBody DiaryCommentCreateRequest request) {
|
||||
// 从上下文中获取当前用户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(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新评论
|
||||
*/
|
||||
@Operation(summary = "更新评论", description = "修改已有评论的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<DiaryCommentResponse> update(@Valid @RequestBody DiaryCommentCreateRequest request) {
|
||||
DiaryCommentResponse response = diaryCommentService.updateCommentWithResponse(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*/
|
||||
@Operation(summary = "删除评论", description = "永久删除指定评论。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "评论 ID") @RequestParam String id) {
|
||||
boolean deleted = diaryCommentService.deleteComment(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
|
||||
// 更新日记的评论数
|
||||
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
|
||||
if (response != null) {
|
||||
diaryPostService.decrementCommentCount(response.getDiaryId());
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 软删除评论
|
||||
*/
|
||||
@Operation(summary = "软删除评论", description = "将评论标记为已删除状态。")
|
||||
@DeleteMapping(value = "/softDelete")
|
||||
public Result<Void> softDelete(@Parameter(description = "评论 ID") @RequestParam String id) {
|
||||
boolean deleted = diaryCommentService.softDeleteComment(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
|
||||
// 更新日记的评论数
|
||||
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
|
||||
if (response != null) {
|
||||
diaryPostService.decrementCommentCount(response.getDiaryId());
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复评论
|
||||
*/
|
||||
@Operation(summary = "恢复评论", description = "恢复已软删除的评论。")
|
||||
@PutMapping(value = "/restore")
|
||||
public Result<Void> restore(@Parameter(description = "评论 ID") @RequestParam String id) {
|
||||
boolean restored = diaryCommentService.restoreComment(id);
|
||||
if (!restored) {
|
||||
return Result.error("恢复失败");
|
||||
}
|
||||
|
||||
// 更新日记的评论数
|
||||
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
|
||||
if (response != null) {
|
||||
diaryPostService.incrementCommentCount(response.getDiaryId());
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞评论
|
||||
*/
|
||||
@Operation(summary = "点赞评论", description = "对指定评论进行点赞。")
|
||||
@PostMapping(value = "/like")
|
||||
public Result<Void> like(@Parameter(description = "评论 ID") @RequestParam String id) {
|
||||
boolean liked = diaryCommentService.incrementLikeCount(id);
|
||||
if (!liked) {
|
||||
return Result.error("点赞失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消点赞评论
|
||||
*/
|
||||
@Operation(summary = "取消点赞评论", description = "取消对指定评论的点赞。")
|
||||
@DeleteMapping(value = "/unlike")
|
||||
public Result<Void> unlike(@Parameter(description = "评论 ID") @RequestParam String id) {
|
||||
boolean unliked = diaryCommentService.decrementLikeCount(id);
|
||||
if (!unliked) {
|
||||
return Result.error("取消点赞失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置置顶状态
|
||||
*/
|
||||
@Operation(summary = "置顶评论", description = "将指定评论设为置顶显示。")
|
||||
@PutMapping(value = "/setTop")
|
||||
public Result<Void> setTop(@Parameter(description = "评论 ID") @RequestParam String id, @RequestParam Integer isTop) {
|
||||
boolean set = diaryCommentService.setTop(id, isTop);
|
||||
if (!set) {
|
||||
return Result.error("设置置顶状态失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
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.service.DiaryPostService;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 用户日记控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/diaryPost")
|
||||
@Tag(name = "日记管理", description = "用户日记帖子的查询、创建、发布、更新、删除、点赞、分享等接口")
|
||||
public class DiaryPostController {
|
||||
|
||||
@Autowired
|
||||
private DiaryPostService diaryPostService;
|
||||
|
||||
/**
|
||||
* 分页查询日记
|
||||
*/
|
||||
@Operation(summary = "分页查询日记", description = "根据条件分页查询当前用户的日记帖子列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<DiaryPostResponse>> getPage(@Validated DiaryPostPageRequest request) {
|
||||
return Result.success(diaryPostService.getPageWithResponse(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取日记详情
|
||||
*/
|
||||
@Operation(summary = "获取日记详情", description = "根据 ID 获取日记帖子的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<DiaryPostResponse> getById(@Parameter(description = "日记 ID") @RequestParam String id) {
|
||||
DiaryPostResponse response = diaryPostService.getDiaryPostResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("日记不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建日记
|
||||
*/
|
||||
@Operation(summary = "创建日记", description = "创建一篇新的日记帖子。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<DiaryPostResponse> create(@Valid @RequestBody DiaryPostCreateRequest request) {
|
||||
// 从上下文中获取当前用户ID,而不是直接使用请求中的用户ID
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
if (currentUserId != null) {
|
||||
request.setUserId(currentUserId);
|
||||
}
|
||||
return Result.success(diaryPostService.createDiaryPostWithResponse(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发表日记并生成AI评论
|
||||
*/
|
||||
@Operation(summary = "发布日记", description = "将草稿状态的日记帖子发布为正式内容。")
|
||||
@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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新日记
|
||||
*/
|
||||
@Operation(summary = "更新日记", description = "修改已有日记帖子的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<DiaryPostResponse> update(@Valid @RequestBody DiaryPostUpdateRequest request) {
|
||||
DiaryPostResponse response = diaryPostService.updateDiaryPostWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除日记
|
||||
*/
|
||||
@Operation(summary = "删除日记", description = "永久删除指定的日记帖子。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "日记 ID") @RequestParam String id) {
|
||||
boolean deleted = diaryPostService.deleteDiaryPost(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 软删除日记
|
||||
*/
|
||||
@Operation(summary = "软删除日记", description = "将日记帖子标记为已删除状态(可恢复)。")
|
||||
@DeleteMapping(value = "/softDelete")
|
||||
public Result<Void> softDelete(@Parameter(description = "日记 ID") @RequestParam String id) {
|
||||
boolean deleted = diaryPostService.softDeleteDiaryPost(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复日记
|
||||
*/
|
||||
@Operation(summary = "恢复日记", description = "将已软删除的日记帖子恢复为正常状态。")
|
||||
@PutMapping(value = "/restore")
|
||||
public Result<Void> restore(@Parameter(description = "日记 ID") @RequestParam String id) {
|
||||
boolean restored = diaryPostService.restoreDiaryPost(id);
|
||||
if (!restored) {
|
||||
return Result.error("恢复失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞日记
|
||||
*/
|
||||
@Operation(summary = "点赞日记", description = "对指定日记帖子进行点赞操作。")
|
||||
@PostMapping(value = "/like")
|
||||
public Result<Void> like(@Parameter(description = "日记 ID") @RequestParam String id) {
|
||||
boolean liked = diaryPostService.incrementLikeCount(id);
|
||||
if (!liked) {
|
||||
return Result.error("点赞失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消点赞日记
|
||||
*/
|
||||
@Operation(summary = "取消点赞日记", description = "取消对指定日记帖子的点赞。")
|
||||
@DeleteMapping(value = "/unlike")
|
||||
public Result<Void> unlike(@Parameter(description = "日记 ID") @RequestParam String id) {
|
||||
boolean unliked = diaryPostService.decrementLikeCount(id);
|
||||
if (!unliked) {
|
||||
return Result.error("取消点赞失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分享日记
|
||||
*/
|
||||
@Operation(summary = "分享日记", description = "将指定日记帖子分享给其他用户。")
|
||||
@PostMapping(value = "/share")
|
||||
public Result<Void> share(@Parameter(description = "日记 ID") @RequestParam String id) {
|
||||
boolean shared = diaryPostService.incrementShareCount(id);
|
||||
if (!shared) {
|
||||
return Result.error("分享失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置精选状态
|
||||
*/
|
||||
@Operation(summary = "设置精选日记", description = "将指定日记帖子标记为精选内容。")
|
||||
@PutMapping(value = "/setFeatured")
|
||||
public Result<Void> setFeatured(@Parameter(description = "日记 ID") @RequestParam String id, @RequestParam Integer featured) {
|
||||
boolean set = diaryPostService.setFeatured(id, featured);
|
||||
if (!set) {
|
||||
return Result.error("设置精选状态失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置置顶优先级
|
||||
*/
|
||||
@Operation(summary = "设置日记优先级", description = "修改指定日记帖子的优先级排序。")
|
||||
@PutMapping(value = "/setPriority")
|
||||
public Result<Void> setPriority(@Parameter(description = "日记 ID") @RequestParam String id, @RequestParam Integer priority) {
|
||||
boolean set = diaryPostService.setPriority(id, priority);
|
||||
if (!set) {
|
||||
return Result.error("设置优先级失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.service.DictionaryService;
|
||||
import com.emotion.dto.request.dictionary.DictionaryCreateRequest;
|
||||
import com.emotion.dto.request.dictionary.DictionaryPageRequest;
|
||||
import com.emotion.dto.request.dictionary.DictionaryUpdateRequest;
|
||||
import com.emotion.dto.response.dictionary.DictionaryResponse;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.util.List;
|
||||
|
||||
/**
|
||||
* 字典Controller
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/dictionary")
|
||||
@Tag(name = "字典管理", description = "数据字典的查询、创建、更新和删除接口")
|
||||
public class DictionaryController {
|
||||
|
||||
@Autowired
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
/**
|
||||
* 创建字典
|
||||
*
|
||||
* @param request 创建请求
|
||||
* @return 创建结果
|
||||
*/
|
||||
@Operation(summary = "创建字典项", description = "创建一个新的数据字典项。")
|
||||
@PostMapping
|
||||
public Result<DictionaryResponse> createDictionary(@Validated @RequestBody DictionaryCreateRequest request) {
|
||||
return dictionaryService.createDictionary(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新字典
|
||||
*
|
||||
* @param request 更新请求
|
||||
* @return 更新结果
|
||||
*/
|
||||
@Operation(summary = "更新字典项", description = "修改已有数据字典项的内容。")
|
||||
@PutMapping
|
||||
public Result<DictionaryResponse> updateDictionary(@Validated @RequestBody DictionaryUpdateRequest request) {
|
||||
return dictionaryService.updateDictionary(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除字典
|
||||
*
|
||||
* @param id 字典ID
|
||||
* @return 删除结果
|
||||
*/
|
||||
@Operation(summary = "删除字典项", description = "删除指定的数据字典项。")
|
||||
@DeleteMapping("/delete")
|
||||
public Result<Void> deleteDictionary(@Parameter(description = "字典 ID") @RequestParam String id) {
|
||||
return dictionaryService.deleteDictionary(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字典详情
|
||||
*
|
||||
* @param id 字典ID
|
||||
* @return 字典详情
|
||||
*/
|
||||
@Operation(summary = "获取字典详情", description = "根据 ID 获取字典项的详细信息。")
|
||||
@GetMapping("/detail")
|
||||
public Result<DictionaryResponse> getDictionary(@Parameter(description = "字典 ID") @RequestParam String id) {
|
||||
return dictionaryService.getDictionary(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询字典
|
||||
*
|
||||
* @param request 分页请求
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Operation(summary = "分页查询字典", description = "分页查询数据字典列表。")
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<DictionaryResponse>> listDictionaries(DictionaryPageRequest request) {
|
||||
return dictionaryService.listDictionaries(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型查询字典集合
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @return 字典集合
|
||||
*/
|
||||
@Operation(summary = "根据类型查询字典", description = "根据字典类型查询字典集合。")
|
||||
@GetMapping("/byType")
|
||||
public Result<List<DictionaryResponse>> getDictionariesByType(@Parameter(description = "字典类型") @RequestParam String dictType) {
|
||||
return dictionaryService.getDictionariesByType(dictType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型查询启用的字典集合
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @return 启用的字典集合
|
||||
*/
|
||||
@Operation(summary = "查询启用的字典", description = "根据字典类型查询启用的字典集合。")
|
||||
@GetMapping("/enabledByType")
|
||||
public Result<List<DictionaryResponse>> getEnabledDictionariesByType(@Parameter(description = "字典类型") @RequestParam String dictType) {
|
||||
return dictionaryService.getEnabledDictionariesByType(dictType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 情绪分析控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/emotionAnalysis")
|
||||
@Tag(name = "情绪分析管理", description = "情绪分析记录的查询、创建、更新和删除接口")
|
||||
public class EmotionAnalysisController {
|
||||
|
||||
@Autowired
|
||||
private EmotionAnalysisService emotionAnalysisService;
|
||||
|
||||
/**
|
||||
* 分页查询情绪分析记录
|
||||
*/
|
||||
@Operation(summary = "分页查询情绪分析", description = "根据条件分页查询情绪分析记录列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<EmotionAnalysisResponse>> getPage(@Validated EmotionAnalysisPageRequest request) {
|
||||
return Result.success(emotionAnalysisService.getPageWithResponse(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取情绪分析记录
|
||||
*/
|
||||
@Operation(summary = "获取分析详情", description = "根据 ID 获取情绪分析报告的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<EmotionAnalysisResponse> getById(@Parameter(description = "分析 ID") @RequestParam String id) {
|
||||
EmotionAnalysisResponse response = emotionAnalysisService.getEmotionAnalysisResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("情绪分析记录不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建情绪分析记录
|
||||
*/
|
||||
@Operation(summary = "创建情绪分析", description = "基于情绪记录数据创建一条新的 AI 情绪分析报告。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<EmotionAnalysisResponse> create(@RequestBody @Valid EmotionAnalysisCreateRequest request) {
|
||||
return Result.success(emotionAnalysisService.createEmotionAnalysisWithResponse(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新情绪分析记录
|
||||
*/
|
||||
@Operation(summary = "更新情绪分析", description = "修改已有情绪分析报告的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<EmotionAnalysisResponse> update(@RequestBody @Valid EmotionAnalysisUpdateRequest request) {
|
||||
EmotionAnalysisResponse response = emotionAnalysisService.updateEmotionAnalysisWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除情绪分析记录
|
||||
*/
|
||||
@Operation(summary = "删除情绪分析", description = "删除指定的情绪分析记录。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "分析 ID") @RequestParam String id) {
|
||||
boolean deleted = emotionAnalysisService.deleteEmotionAnalysis(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
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.UserContextUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 情绪记录控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-22
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/emotionRecord")
|
||||
@Tag(name = "情绪记录管理", description = "用户情绪记录的查询、创建、更新、删除和统计接口")
|
||||
public class EmotionRecordController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(EmotionRecordController.class);
|
||||
|
||||
@Autowired
|
||||
private EmotionRecordService emotionRecordService;
|
||||
|
||||
/**
|
||||
* 创建情绪记录
|
||||
*/
|
||||
@Operation(summary = "创建情绪记录", description = "记录用户当天的情绪数据,包括类型、强度、触发因素等。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<EmotionRecordResponse> createRecord(@RequestBody @Valid EmotionRecordCreateRequest request) {
|
||||
log.info("创建情绪记录: userId={}", request.getUserId());
|
||||
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = UserContextUtils.requireCurrentUserId();
|
||||
request.setUserId(userId);
|
||||
|
||||
EmotionRecordResponse response = emotionRecordService.createEmotionRecordWithResponse(request);
|
||||
return Result.success("创建成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询情绪记录
|
||||
*/
|
||||
@Operation(summary = "分页查询情绪记录", description = "根据日期范围、情绪类型等条件分页查询情绪记录。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<EmotionRecordResponse>> getPage(@Validated EmotionRecordPageRequest request) {
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = UserContextUtils.requireCurrentUserId();
|
||||
|
||||
log.info("分页查询情绪记录: userId={}, current={}, size={}", userId, request.getCurrent(), request.getSize());
|
||||
|
||||
PageResult<EmotionRecordResponse> page = emotionRecordService.getPageByUserIdWithResponse(userId, request);
|
||||
|
||||
log.info("分页查询情绪记录成功: userId={}, total={}, records={}",
|
||||
userId, page.getTotal(), page.getRecords().size());
|
||||
|
||||
return Result.success(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取情绪记录详情
|
||||
*/
|
||||
@Operation(summary = "获取记录详情", description = "根据 ID 获取情绪记录的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<EmotionRecordResponse> getRecord(@Parameter(description = "记录 ID") @RequestParam String id) {
|
||||
log.info("获取情绪记录详情: {}", id);
|
||||
|
||||
EmotionRecordResponse response = emotionRecordService.getEmotionRecordResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("情绪记录不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新情绪记录
|
||||
*/
|
||||
@Operation(summary = "更新情绪记录", description = "修改已有情绪记录的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<EmotionRecordResponse> updateRecord(@RequestBody @Valid EmotionRecordUpdateRequest request) {
|
||||
log.info("更新情绪记录: {}", request.getId());
|
||||
|
||||
EmotionRecordResponse response = emotionRecordService.updateEmotionRecordWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.notFound("情绪记录不存在");
|
||||
}
|
||||
return Result.success("更新成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除情绪记录
|
||||
*/
|
||||
@Operation(summary = "删除情绪记录", description = "删除指定的情绪记录。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<String> deleteRecord(@Parameter(description = "记录 ID") @RequestParam String id) {
|
||||
log.info("删除情绪记录: {}", id);
|
||||
|
||||
boolean deleted = emotionRecordService.deleteEmotionRecord(id);
|
||||
if (!deleted) {
|
||||
return Result.notFound("情绪记录不存在");
|
||||
}
|
||||
return Result.success("删除成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取情绪统计
|
||||
*/
|
||||
@Operation(summary = "获取情绪统计", description = "统计指定时间范围内的情绪数据分布。")
|
||||
@GetMapping(value = "/stats")
|
||||
public Result<Map<String, Object>> getEmotionStats(
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate) {
|
||||
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = UserContextUtils.requireCurrentUserId();
|
||||
|
||||
log.info("获取情绪统计: userId={}, startDate={}, endDate={}", userId, startDate, endDate);
|
||||
|
||||
Map<String, Object> stats = emotionRecordService.getEmotionStats(userId, startDate, endDate);
|
||||
|
||||
return Result.success(stats);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.EmotionSummaryGenerateRequest;
|
||||
import com.emotion.dto.request.EmotionSummaryStatusRequest;
|
||||
import com.emotion.dto.response.EmotionSummaryGenerateResponse;
|
||||
import com.emotion.dto.response.EmotionSummaryStatusResponse;
|
||||
import com.emotion.service.AiChatService;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 情绪总结控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-25
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/emotionSummary")
|
||||
@Tag(name = "情绪总结管理", description = "用户情绪记录总结和分析功能")
|
||||
public class EmotionSummaryController {
|
||||
|
||||
@Autowired
|
||||
private AiChatService aiChatService;
|
||||
|
||||
/**
|
||||
* 生成用户当天的情绪记录总结
|
||||
*/
|
||||
@Operation(summary = "生成情绪总结", description = "基于指定时间范围的情绪记录数据,生成 AI 情绪总结报告。")
|
||||
@PostMapping(value = "/generate")
|
||||
public Result<EmotionSummaryGenerateResponse> generateEmotionSummary(
|
||||
@RequestBody @Valid EmotionSummaryGenerateRequest request) {
|
||||
String userId = UserContextUtils.requireCurrentUserId();
|
||||
log.info("收到生成情绪记录总结请求: userId={}", userId);
|
||||
|
||||
// 调用AI服务生成情绪总结
|
||||
EmotionSummaryGenerateResponse response = aiChatService.generateEmotionSummaryWithResponse(userId);
|
||||
|
||||
if (response.getSuccess()) {
|
||||
log.info("情绪记录总结生成成功: userId={}", userId);
|
||||
return Result.success("情绪记录总结生成成功", response);
|
||||
} else {
|
||||
log.warn("情绪记录总结生成失败: userId={}, message={}", userId, response.getMessage());
|
||||
return Result.error(response.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户情绪记录总结状态
|
||||
*/
|
||||
@Operation(summary = "查询总结状态", description = "查询指定时间段内是否已生成情绪总结及其状态。")
|
||||
@GetMapping(value = "/status")
|
||||
public Result<EmotionSummaryStatusResponse> getEmotionSummaryStatus(
|
||||
@Validated EmotionSummaryStatusRequest request) {
|
||||
// 从上下文中获取当前用户ID
|
||||
String userId = UserContextUtils.requireCurrentUserId();
|
||||
log.info("查询用户情绪记录总结状态: userId={}", userId);
|
||||
|
||||
// 调用AI服务获取状态信息
|
||||
EmotionSummaryStatusResponse response = aiChatService.getEmotionSummaryStatusWithResponse(userId);
|
||||
|
||||
return Result.success(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.EpicScriptCreateRequest;
|
||||
import com.emotion.dto.request.EpicScriptInspirationRequest;
|
||||
import com.emotion.dto.request.EpicScriptPageRequest;
|
||||
import com.emotion.dto.request.EpicScriptUpdateRequest;
|
||||
import com.emotion.dto.response.EpicScriptInspirationResponse;
|
||||
import com.emotion.dto.response.EpicScriptResponse;
|
||||
import com.emotion.dto.response.InspirationSuggestionResponse;
|
||||
import com.emotion.service.EpicScriptService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 爽文剧本控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/epicScript")
|
||||
@Tag(name = "爽文剧本管理", description = "爽文剧本的查询、创建、更新、删除和灵感推荐接口")
|
||||
public class EpicScriptController {
|
||||
|
||||
@Autowired
|
||||
private EpicScriptService epicScriptService;
|
||||
|
||||
/**
|
||||
* 分页查询当前用户的爽文剧本
|
||||
*/
|
||||
@Operation(summary = "分页查询爽文剧本", description = "分页查询当前用户的爽文剧本列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<EpicScriptResponse>> getPage(@Validated EpicScriptPageRequest request) {
|
||||
PageResult<EpicScriptResponse> pageResult = epicScriptService.getPageByCurrentUser(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的所有爽文剧本列表
|
||||
*/
|
||||
@Operation(summary = "获取爽文剧本列表", description = "获取当前用户的所有爽文剧本列表。")
|
||||
@GetMapping(value = "/listAll")
|
||||
public Result<List<EpicScriptResponse>> getList() {
|
||||
List<EpicScriptResponse> scripts = epicScriptService.getListByCurrentUser();
|
||||
return Result.success(scripts);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取灵感推荐", description = "获取系统推荐的灵感建议列表。")
|
||||
@GetMapping(value = "/inspiration/recommendations")
|
||||
public Result<List<InspirationSuggestionResponse>> getInspirationRecommendations() {
|
||||
return Result.success(epicScriptService.getInspirationRecommendations());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取随机灵感", description = "随机获取指定数量的灵感建议。")
|
||||
@GetMapping(value = "/inspiration/random")
|
||||
public Result<List<InspirationSuggestionResponse>> getRandomInspirations(
|
||||
@Parameter(description = "灵感数量") @RequestParam(required = false, defaultValue = "3") Integer size) {
|
||||
return Result.success(epicScriptService.getRandomInspirations(size));
|
||||
}
|
||||
|
||||
@Operation(summary = "从灵感生成剧本", description = "基于选定的灵感建议生成爽文剧本。")
|
||||
@PostMapping(value = "/inspiration/generate")
|
||||
public Result<EpicScriptInspirationResponse> generateFromInspiration(
|
||||
@Valid @RequestBody EpicScriptInspirationRequest request) {
|
||||
EpicScriptInspirationResponse response;
|
||||
try {
|
||||
response = epicScriptService.generateFromInspiration(request);
|
||||
} catch (IllegalStateException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
if (response == null) {
|
||||
return Result.error("灵感剧本生成失败");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取爽文剧本详情
|
||||
*/
|
||||
@Operation(summary = "获取剧本详情", description = "根据 ID 获取爽文剧本的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<EpicScriptResponse> getById(@Parameter(description = "剧本 ID") @RequestParam String id) {
|
||||
EpicScriptResponse script = epicScriptService.getScriptById(id);
|
||||
if (script == null) {
|
||||
return Result.notFound("爽文剧本不存在");
|
||||
}
|
||||
return Result.success(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建爽文剧本
|
||||
*/
|
||||
@Operation(summary = "创建爽文剧本", description = "创建一个新的爽文剧本。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<EpicScriptResponse> create(@Valid @RequestBody EpicScriptCreateRequest request) {
|
||||
EpicScriptResponse script = epicScriptService.createScript(request);
|
||||
if (script == null) {
|
||||
return Result.error("创建失败");
|
||||
}
|
||||
return Result.success(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新爽文剧本
|
||||
*/
|
||||
@Operation(summary = "更新爽文剧本", description = "修改已有爽文剧本的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<EpicScriptResponse> update(@Valid @RequestBody EpicScriptUpdateRequest request) {
|
||||
EpicScriptResponse script = epicScriptService.updateScript(request);
|
||||
if (script == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中剧本(取消其他选中状态)
|
||||
*/
|
||||
@Operation(summary = "选中剧本", description = "选中指定剧本并取消其他剧本的选中状态。")
|
||||
@PutMapping(value = "/select")
|
||||
public Result<EpicScriptResponse> select(@Parameter(description = "剧本 ID") @RequestParam String id) {
|
||||
EpicScriptResponse script = epicScriptService.selectScript(id);
|
||||
if (script == null) {
|
||||
return Result.error("选中失败");
|
||||
}
|
||||
return Result.success(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除爽文剧本
|
||||
*/
|
||||
@Operation(summary = "删除爽文剧本", description = "删除指定的爽文剧本。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "剧本 ID") @RequestParam String id) {
|
||||
boolean deleted = epicScriptService.deleteScript(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.growth.GrowthTopicCreateRequest;
|
||||
import com.emotion.dto.request.growth.GrowthTopicPageRequest;
|
||||
import com.emotion.dto.request.growth.GrowthTopicUpdateRequest;
|
||||
import com.emotion.dto.response.growth.GrowthTopicResponse;
|
||||
import com.emotion.service.GrowthTopicService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 成长话题控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/growthTopic")
|
||||
@Tag(name = "成长话题管理", description = "成长话题的查询、创建、更新和删除接口")
|
||||
public class GrowthTopicController {
|
||||
|
||||
@Autowired
|
||||
private GrowthTopicService growthTopicService;
|
||||
|
||||
/**
|
||||
* 分页查询成长话题
|
||||
*/
|
||||
@Operation(summary = "分页查询成长话题", description = "分页查询成长话题列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<GrowthTopicResponse>> getPage(@Validated GrowthTopicPageRequest request) {
|
||||
PageResult<GrowthTopicResponse> pageResult = growthTopicService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取成长话题
|
||||
*/
|
||||
@Operation(summary = "获取话题详情", description = "根据 ID 获取成长话题的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<GrowthTopicResponse> getById(@Parameter(description = "话题 ID") @RequestParam String id) {
|
||||
GrowthTopicResponse response = growthTopicService.getGrowthTopicResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("成长话题不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建成长话题
|
||||
*/
|
||||
@Operation(summary = "创建成长话题", description = "创建一个新的成长话题。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<GrowthTopicResponse> create(@Valid @RequestBody GrowthTopicCreateRequest request) {
|
||||
GrowthTopicResponse response = growthTopicService.createGrowthTopicWithResponse(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新成长话题
|
||||
*/
|
||||
@Operation(summary = "更新成长话题", description = "修改已有成长话题的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<GrowthTopicResponse> update(@Valid @RequestBody GrowthTopicUpdateRequest request) {
|
||||
GrowthTopicResponse response = growthTopicService.updateGrowthTopicWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.notFound("成长话题不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除成长话题
|
||||
*/
|
||||
@Operation(summary = "删除成长话题", description = "删除指定的成长话题。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "话题 ID") @RequestParam String id) {
|
||||
boolean deleted = growthTopicService.deleteGrowthTopic(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.guest.GuestUserCreateRequest;
|
||||
import com.emotion.dto.request.guest.GuestUserPageRequest;
|
||||
import com.emotion.dto.request.guest.GuestUserUpdateRequest;
|
||||
import com.emotion.dto.response.guest.GuestUserResponse;
|
||||
import com.emotion.service.GuestUserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 访客用户控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/guestUser")
|
||||
@Tag(name = "访客用户管理", description = "访客用户的查询、创建、更新和删除接口")
|
||||
public class GuestUserController {
|
||||
|
||||
@Autowired
|
||||
private GuestUserService guestUserService;
|
||||
|
||||
/**
|
||||
* 分页查询访客用户
|
||||
*/
|
||||
@Operation(summary = "分页查询访客用户", description = "分页查询访客用户列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<GuestUserResponse>> getPage(@Validated GuestUserPageRequest request) {
|
||||
PageResult<GuestUserResponse> pageResult = guestUserService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取访客用户
|
||||
*/
|
||||
@Operation(summary = "获取访客详情", description = "根据 ID 获取访客用户的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<GuestUserResponse> getById(@Parameter(description = "访客 ID") @RequestParam String id) {
|
||||
GuestUserResponse response = guestUserService.getGuestUserResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("访客用户不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建访客用户
|
||||
*/
|
||||
@Operation(summary = "创建访客用户", description = "创建一个新的访客用户。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<GuestUserResponse> create(@Valid @RequestBody GuestUserCreateRequest request) {
|
||||
GuestUserResponse response = guestUserService.createGuestUserWithResponse(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新访客用户
|
||||
*/
|
||||
@Operation(summary = "更新访客用户", description = "修改已有访客用户的信息。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<GuestUserResponse> update(@Valid @RequestBody GuestUserUpdateRequest request) {
|
||||
GuestUserResponse response = guestUserService.updateGuestUserWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.notFound("访客用户不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除访客用户
|
||||
*/
|
||||
@Operation(summary = "删除访客用户", description = "删除指定的访客用户。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "访客 ID") @RequestParam String id) {
|
||||
boolean deleted = guestUserService.deleteGuestUser(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 健康检查控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@Tag(name = "健康检查", description = "系统健康状态检查接口")
|
||||
public class HealthController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(HealthController.class);
|
||||
|
||||
/**
|
||||
* 健康检查
|
||||
*/
|
||||
@Operation(summary = "系统健康检查", description = "返回系统各组件的健康状态,包括数据库、Redis 等。")
|
||||
@GetMapping("/health")
|
||||
public Map<String, Object> health() {
|
||||
log.info("健康检查请求");
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("service", "backend-single");
|
||||
response.put("message", "情感博物馆单体服务运行正常");
|
||||
response.put("version", "1.0.0");
|
||||
response.put("status", "UP");
|
||||
response.put("timestamp", LocalDateTime.now());
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务信息
|
||||
*/
|
||||
@Operation(summary = "获取服务信息", description = "返回服务的基本信息,包括版本号、构建时间等。")
|
||||
@GetMapping("/health/info")
|
||||
public Map<String, Object> info() {
|
||||
log.info("服务信息请求");
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("service", "backend-single");
|
||||
response.put("description", "情感博物馆单体服务");
|
||||
response.put("version", "1.0.0");
|
||||
response.put("author", "emotion-museum");
|
||||
response.put("buildTime", "2025-07-23");
|
||||
response.put("javaVersion", System.getProperty("java.version"));
|
||||
response.put("timestamp", LocalDateTime.now());
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.LifeEventCreateRequest;
|
||||
import com.emotion.dto.request.LifeEventPageRequest;
|
||||
import com.emotion.dto.request.LifeEventUpdateRequest;
|
||||
import com.emotion.dto.response.LifeEventResponse;
|
||||
import com.emotion.service.LifeEventService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 生命事件控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/lifeEvent")
|
||||
@Tag(name = "生命事件管理", description = "生命事件的查询、创建、更新和删除接口")
|
||||
public class LifeEventController {
|
||||
|
||||
@Autowired
|
||||
private LifeEventService lifeEventService;
|
||||
|
||||
/**
|
||||
* 分页查询当前用户的生命事件
|
||||
*/
|
||||
@Operation(summary = "分页查询生命事件", description = "分页查询生命事件列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<LifeEventResponse>> getPage(@Validated LifeEventPageRequest request) {
|
||||
PageResult<LifeEventResponse> pageResult = lifeEventService.getPageByCurrentUser(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的所有生命事件列表
|
||||
*/
|
||||
@Operation(summary = "获取生命事件列表", description = "获取当前用户的所有生命事件列表。")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<List<LifeEventResponse>> getList() {
|
||||
List<LifeEventResponse> events = lifeEventService.getListByCurrentUser();
|
||||
return Result.success(events);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取生命事件详情
|
||||
*/
|
||||
@Operation(summary = "获取生命事件详情", description = "根据 ID 获取生命事件的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<LifeEventResponse> getById(@Parameter(description = "事件 ID") @RequestParam String id) {
|
||||
LifeEventResponse event = lifeEventService.getEventById(id);
|
||||
if (event == null) {
|
||||
return Result.notFound("生命事件不存在");
|
||||
}
|
||||
return Result.success(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建生命事件
|
||||
*/
|
||||
@Operation(summary = "创建生命事件", description = "创建一个新的生命事件。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<LifeEventResponse> create(@Valid @RequestBody LifeEventCreateRequest request) {
|
||||
LifeEventResponse event = lifeEventService.createEvent(request);
|
||||
if (event == null) {
|
||||
return Result.error("创建失败");
|
||||
}
|
||||
return Result.success(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新生命事件
|
||||
*/
|
||||
@Operation(summary = "更新生命事件", description = "修改已有生命事件的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<LifeEventResponse> update(@Valid @RequestBody LifeEventUpdateRequest request) {
|
||||
LifeEventResponse event = lifeEventService.updateEvent(request);
|
||||
if (event == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除生命事件
|
||||
*/
|
||||
@Operation(summary = "删除生命事件", description = "删除指定的生命事件。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "事件 ID") @RequestParam String id) {
|
||||
boolean deleted = lifeEventService.deleteEvent(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
@Operation(summary = "AI 辅助生成内容", description = "根据标题和内容调用 AI 生成生命事件的解读文本、标签等信息。")
|
||||
@PostMapping(value = "/ai-assist")
|
||||
public Result<Map<String, Object>> aiAssist(@RequestBody Map<String, Object> request) {
|
||||
String title = stringValue(request.get("title"), "这段经历");
|
||||
String content = stringValue(request.get("content"), "");
|
||||
List<String> tags = readTags(request.get("tags"));
|
||||
if (tags.isEmpty()) {
|
||||
tags.add("成长");
|
||||
tags.add("记录");
|
||||
}
|
||||
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("content", content.isBlank()
|
||||
? "那一天,我清楚地感受到自己正在经历一次变化。事情本身也许并不宏大,但它让我重新看见了自己的选择、情绪和力量。"
|
||||
: content + "\n\n我愿意把这段经历记录下来,因为它提醒我:每一次真实面对,都会成为未来的底气。");
|
||||
data.put("aiReply", "AI占位解读:" + title + "体现了你的自我观察和复盘能力。后续接入真实AI后,这里会返回更完整的情绪整理、能力映射和行动建议。");
|
||||
data.put("tags", tags);
|
||||
data.put("placeholder", true);
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
@Operation(summary = "聊天占位接口", description = "返回聊天功能的占位回复和建议问题。")
|
||||
@PostMapping(value = "/chat-placeholder")
|
||||
public Result<Map<String, Object>> chatPlaceholder(@RequestBody Map<String, Object> request) {
|
||||
String title = stringValue(request.get("title"), "这段经历");
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("reply", "我在这里陪你回看「" + title + "」。真实聊天能力后续接入AI工作流;当前先保留这个入口和上下文。");
|
||||
data.put("suggestions", List.of("这件事让我学到了什么?", "如果重来一次我会怎么选?", "它会怎样影响我的人生剧本?"));
|
||||
data.put("placeholder", true);
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
@Operation(summary = "分享占位接口", description = "返回分享功能的占位文本和分享信息。")
|
||||
@PostMapping(value = "/share-placeholder")
|
||||
public Result<Map<String, Object>> sharePlaceholder(@RequestBody Map<String, Object> request) {
|
||||
String title = stringValue(request.get("title"), "人生经历");
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("title", title);
|
||||
data.put("summary", "我刚刚记录了一段重要的人生轨迹。");
|
||||
data.put("shareText", "分享我的人生轨迹:" + title);
|
||||
data.put("placeholder", true);
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
@Operation(summary = "收藏占位接口", description = "返回收藏功能的占位响应。")
|
||||
@PostMapping(value = "/favorite-placeholder")
|
||||
public Result<Map<String, Object>> favoritePlaceholder(@RequestBody Map<String, Object> request) {
|
||||
String id = stringValue(request.get("id"), "");
|
||||
Boolean favorite = Boolean.TRUE.equals(request.get("favorite"));
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", id);
|
||||
data.put("favorite", favorite);
|
||||
data.put("placeholder", true);
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
private String stringValue(Object value, String fallback) {
|
||||
if (value == null) {
|
||||
return fallback;
|
||||
}
|
||||
String text = String.valueOf(value).trim();
|
||||
return text.isEmpty() ? fallback : text;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<String> readTags(Object value) {
|
||||
List<String> tags = new ArrayList<>();
|
||||
if (value instanceof List<?>) {
|
||||
for (Object item : (List<Object>) value) {
|
||||
if (item != null && !String.valueOf(item).trim().isEmpty()) {
|
||||
tags.add(String.valueOf(item).trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.LifePathCreateRequest;
|
||||
import com.emotion.dto.request.LifePathPageRequest;
|
||||
import com.emotion.dto.request.LifePathUpdateRequest;
|
||||
import com.emotion.dto.response.LifePathResponse;
|
||||
import com.emotion.service.LifePathService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 实现路径控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/lifePath")
|
||||
@Tag(name = "实现路径管理", description = "人生剧本实现路径的查询、创建、更新和删除接口")
|
||||
public class LifePathController {
|
||||
|
||||
@Autowired
|
||||
private LifePathService lifePathService;
|
||||
|
||||
/**
|
||||
* 分页查询当前用户的实现路径
|
||||
*/
|
||||
@Operation(summary = "分页查询实现路径", description = "分页查询当前用户的实现路径列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<LifePathResponse>> getPage(@Validated LifePathPageRequest request) {
|
||||
PageResult<LifePathResponse> pageResult = lifePathService.getPageByCurrentUser(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的所有实现路径列表
|
||||
*/
|
||||
@Operation(summary = "获取实现路径列表", description = "获取当前用户的所有实现路径列表。")
|
||||
@GetMapping(value = "/listAll")
|
||||
public Result<List<LifePathResponse>> getList() {
|
||||
List<LifePathResponse> paths = lifePathService.getListByCurrentUser();
|
||||
return Result.success(paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据剧本ID获取实现路径
|
||||
*/
|
||||
@Operation(summary = "根据剧本ID获取路径", description = "根据剧本 ID 获取对应的实现路径。")
|
||||
@GetMapping(value = "/byScript")
|
||||
public Result<LifePathResponse> getByScriptId(@Parameter(description = "剧本 ID") @RequestParam String scriptId) {
|
||||
LifePathResponse path = lifePathService.getByScriptId(scriptId);
|
||||
if (path == null) {
|
||||
return Result.notFound("实现路径不存在");
|
||||
}
|
||||
return Result.success(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取实现路径详情
|
||||
*/
|
||||
@Operation(summary = "获取路径详情", description = "根据 ID 获取实现路径的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<LifePathResponse> getById(@Parameter(description = "路径 ID") @RequestParam String id) {
|
||||
LifePathResponse path = lifePathService.getPathById(id);
|
||||
if (path == null) {
|
||||
return Result.notFound("实现路径不存在");
|
||||
}
|
||||
return Result.success(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建实现路径
|
||||
*/
|
||||
@Operation(summary = "创建实现路径", description = "创建一条新的实现路径。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<LifePathResponse> create(@Valid @RequestBody LifePathCreateRequest request) {
|
||||
LifePathResponse path = lifePathService.createPath(request);
|
||||
if (path == null) {
|
||||
return Result.error("创建失败");
|
||||
}
|
||||
return Result.success(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新实现路径
|
||||
*/
|
||||
@Operation(summary = "更新实现路径", description = "修改已有实现路径的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<LifePathResponse> update(@Valid @RequestBody LifePathUpdateRequest request) {
|
||||
LifePathResponse path = lifePathService.updatePath(request);
|
||||
if (path == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除实现路径
|
||||
*/
|
||||
@Operation(summary = "删除实现路径", description = "删除指定的实现路径。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "路径 ID") @RequestParam String id) {
|
||||
boolean deleted = lifePathService.deletePath(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.MessageCreateRequest;
|
||||
import com.emotion.dto.request.MessagePageRequest;
|
||||
import com.emotion.dto.request.MessageSearchRequest;
|
||||
import com.emotion.dto.request.MessageRecentRequest;
|
||||
import com.emotion.dto.response.MessageResponse;
|
||||
import com.emotion.service.MessageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 消息控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/message")
|
||||
@Slf4j
|
||||
@Tag(name = "消息管理", description = "会话消息的查询、创建、搜索、更新和删除接口")
|
||||
public class MessageController {
|
||||
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
|
||||
/**
|
||||
* 创建消息
|
||||
*/
|
||||
@Operation(summary = "创建消息", description = "在指定会话中创建一条新消息。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<MessageResponse> create(@Valid @RequestBody MessageCreateRequest request) {
|
||||
MessageResponse response = messageService.createMessageFromRequest(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取消息
|
||||
*/
|
||||
@Operation(summary = "获取消息详情", description = "根据 ID 获取消息的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<MessageResponse> getById(@Parameter(description = "消息 ID") @RequestParam String id) {
|
||||
MessageResponse response = messageService.getMessageById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("消息不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询消息
|
||||
*/
|
||||
@Operation(summary = "分页查询消息", description = "分页查询指定会话的消息列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<MessageResponse>> getPage(@Validated MessagePageRequest request) {
|
||||
PageResult<MessageResponse> pageResult = messageService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索消息
|
||||
*/
|
||||
@Operation(summary = "搜索消息", description = "根据关键词在指定会话中搜索消息内容。")
|
||||
@PostMapping(value = "/search")
|
||||
public Result<PageResult<MessageResponse>> search(@Valid @RequestBody MessageSearchRequest request) {
|
||||
PageResult<MessageResponse> pageResult = messageService.searchWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近的消息
|
||||
*/
|
||||
@Operation(summary = "获取最近消息", description = "获取指定会话最近的消息列表。")
|
||||
@PostMapping(value = "/recent")
|
||||
public Result<PageResult<MessageResponse>> getRecentMessages(@Valid @RequestBody MessageRecentRequest request) {
|
||||
PageResult<MessageResponse> pageResult = messageService.getRecentWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新消息
|
||||
*/
|
||||
@Operation(summary = "更新消息", description = "修改指定消息的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<MessageResponse> update(@RequestParam String id, @RequestParam String content) {
|
||||
MessageResponse response = messageService.updateMessage(id, content);
|
||||
if (response == null) {
|
||||
return Result.notFound("消息不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
*/
|
||||
@Operation(summary = "删除消息", description = "删除指定的消息记录。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "消息 ID") @RequestParam String id) {
|
||||
boolean deleted = messageService.deleteMessage(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.reward.RewardCreateRequest;
|
||||
import com.emotion.dto.request.reward.RewardPageRequest;
|
||||
import com.emotion.dto.request.reward.RewardUpdateRequest;
|
||||
import com.emotion.dto.response.reward.RewardResponse;
|
||||
import com.emotion.service.RewardService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 奖励控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/reward")
|
||||
@Tag(name = "奖励管理", description = "奖励的查询、创建、更新和删除接口")
|
||||
public class RewardController {
|
||||
|
||||
@Autowired
|
||||
private RewardService rewardService;
|
||||
|
||||
/**
|
||||
* 分页查询奖励
|
||||
*/
|
||||
@Operation(summary = "分页查询奖励", description = "分页查询奖励列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<RewardResponse>> getPage(@Validated RewardPageRequest request) {
|
||||
PageResult<RewardResponse> pageResult = rewardService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取奖励
|
||||
*/
|
||||
@Operation(summary = "获取奖励详情", description = "根据 ID 获取奖励的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<RewardResponse> getById(@Parameter(description = "奖励 ID") @RequestParam String id) {
|
||||
RewardResponse response = rewardService.getRewardResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("奖励不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建奖励
|
||||
*/
|
||||
@Operation(summary = "创建奖励", description = "创建一个新的奖励。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<RewardResponse> create(@Valid @RequestBody RewardCreateRequest request) {
|
||||
RewardResponse response = rewardService.createRewardWithResponse(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新奖励
|
||||
*/
|
||||
@Operation(summary = "更新奖励", description = "修改已有奖励的信息。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<RewardResponse> update(@Valid @RequestBody RewardUpdateRequest request) {
|
||||
RewardResponse response = rewardService.updateRewardWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.notFound("奖励不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除奖励
|
||||
*/
|
||||
@Operation(summary = "删除奖励", description = "删除指定的奖励。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "奖励 ID") @RequestParam String id) {
|
||||
boolean deleted = rewardService.deleteReward(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.social.SocialContentApprovalRequest;
|
||||
import com.emotion.dto.request.social.SocialContentLinkImportRequest;
|
||||
import com.emotion.dto.request.social.SocialContentManualImportRequest;
|
||||
import com.emotion.dto.response.social.SocialContentItemResponse;
|
||||
import com.emotion.service.SocialContentService;
|
||||
import com.emotion.util.UserContextHolder;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("/social/content")
|
||||
@Tag(name = "社交内容管理", description = "社交媒体内容的导入、截图、审核等接口")
|
||||
public class SocialContentController {
|
||||
|
||||
@Autowired
|
||||
private SocialContentService socialContentService;
|
||||
|
||||
@Operation(summary = "手动导入社交内容", description = "通过上传文本内容手动导入社交媒体数据。")
|
||||
@PostMapping("/manual")
|
||||
public Result<SocialContentItemResponse> manualImport(@Valid @RequestBody SocialContentManualImportRequest request) {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
try {
|
||||
return Result.success(socialContentService.manualImport(userId, request));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.badRequest(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "链接导入社交内容", description = "通过社交媒体链接 URL 导入内容。")
|
||||
@PostMapping("/link")
|
||||
public Result<SocialContentItemResponse> linkImport(@Valid @RequestBody SocialContentLinkImportRequest request) {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
try {
|
||||
return Result.success(socialContentService.linkImport(userId, request));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.badRequest(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "截图导入社交内容", description = "通过上传截图图片来导入社交媒体内容。")
|
||||
@PostMapping("/screenshot")
|
||||
public Result<SocialContentItemResponse> screenshotImport(@Parameter(description = "平台名称", required = true) @RequestParam String platform,
|
||||
@RequestPart("file") MultipartFile file) {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
try {
|
||||
return Result.success(socialContentService.screenshotImport(userId, platform, file));
|
||||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
return Result.badRequest(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "获取社交内容列表", description = "查询所有已导入的社交媒体内容列表。")
|
||||
@GetMapping("/list")
|
||||
public Result<List<SocialContentItemResponse>> list() {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
return Result.success(socialContentService.listByUser(userId));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新内容审核状态", description = "修改指定社交内容的审核状态(通过/拒绝)。")
|
||||
@PutMapping("/{id}/approval")
|
||||
public Result<SocialContentItemResponse> updateApproval(@Parameter(description = "内容 ID", required = true) @PathVariable String id,
|
||||
@RequestBody SocialContentApprovalRequest request) {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
SocialContentItemResponse response = socialContentService.updateApproval(userId, id, request.getApprovedForAi());
|
||||
if (response == null) {
|
||||
return Result.notFound("导入内容不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除社交内容", description = "删除指定的社交媒体内容记录。")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@Parameter(description = "内容 ID", required = true) @PathVariable String id,
|
||||
@Parameter(description = "是否保留已确认的洞察") @RequestParam(required = false, defaultValue = "true") Boolean keepConfirmedInsights) {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
boolean deleted = socialContentService.deleteByUser(userId, id, keepConfirmedInsights);
|
||||
if (!deleted) {
|
||||
return Result.notFound("导入内容不存在");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
private String currentUserId() {
|
||||
return UserContextHolder.getCurrentUserId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.social.SocialInsightGenerateRequest;
|
||||
import com.emotion.dto.request.social.SocialInsightUpdateRequest;
|
||||
import com.emotion.dto.response.social.SocialProfileInsightResponse;
|
||||
import com.emotion.service.SocialInsightService;
|
||||
import com.emotion.util.UserContextHolder;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("/social/insight")
|
||||
@Tag(name = "社交洞察管理", description = "社交媒体洞察生成、查询和更新接口")
|
||||
public class SocialInsightController {
|
||||
|
||||
@Autowired
|
||||
private SocialInsightService socialInsightService;
|
||||
|
||||
@Operation(summary = "生成社交洞察", description = "基于用户社交数据生成 AI 分析洞察报告。")
|
||||
@PostMapping("/generate")
|
||||
public Result<List<SocialProfileInsightResponse>> generate(@RequestBody(required = false) SocialInsightGenerateRequest request) {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
return Result.success(socialInsightService.generateInsights(userId, request));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取洞察列表", description = "查询当前用户的所有社交洞察记录。")
|
||||
@GetMapping("/list")
|
||||
public Result<List<SocialProfileInsightResponse>> list(@Parameter(description = "洞察状态") @RequestParam(required = false) String status) {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
return Result.success(socialInsightService.listByUser(userId, status));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新洞察记录", description = "更新已有洞察的状态或备注信息。")
|
||||
@PutMapping("/{id}")
|
||||
public Result<SocialProfileInsightResponse> update(@Parameter(description = "洞察 ID", required = true) @PathVariable String id,
|
||||
@Valid @RequestBody SocialInsightUpdateRequest request) {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
try {
|
||||
SocialProfileInsightResponse response = socialInsightService.updateByUser(userId, id, request);
|
||||
if (response == null) {
|
||||
return Result.notFound("画像不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.badRequest(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "删除洞察记录", description = "删除指定的社交洞察记录。")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@Parameter(description = "洞察 ID", required = true) @PathVariable String id) {
|
||||
String userId = currentUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
boolean deleted = socialInsightService.deleteByUser(userId, id);
|
||||
if (!deleted) {
|
||||
return Result.notFound("画像不存在");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
private String currentUserId() {
|
||||
return UserContextHolder.getCurrentUserId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.SystemConfigUpdateRequest;
|
||||
import com.emotion.dto.response.SystemConfigResponse;
|
||||
import com.emotion.service.SystemConfigService;
|
||||
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.util.List;
|
||||
|
||||
/**
|
||||
* 系统配置管理控制器(管理端)
|
||||
*
|
||||
* @author system
|
||||
* @date 2026-06-27
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/systemConfig")
|
||||
@Tag(name = "系统配置管理", description = "系统配置的查询和更新功能")
|
||||
public class SystemConfigController {
|
||||
|
||||
@Autowired
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 获取所有可见配置列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取配置列表", description = "获取所有可见的系统配置列表")
|
||||
public Result<List<SystemConfigResponse>> list() {
|
||||
List<SystemConfigResponse> configs = systemConfigService.getVisibleConfigList();
|
||||
return Result.success(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新配置
|
||||
*/
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "批量更新配置", description = "批量更新系统配置值")
|
||||
public Result<Void> update(@RequestBody @Validated List<SystemConfigUpdateRequest> requests) {
|
||||
systemConfigService.batchUpdateConfigs(requests);
|
||||
return Result.success("更新成功", null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.response.UserInfoResponse;
|
||||
import com.emotion.service.TokenService;
|
||||
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.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Token控制器
|
||||
* 提供基于请求头Authorization的token验证和用户信息获取功能
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/token")
|
||||
@Tag(name = "Token管理", description = "Token验证和用户信息获取")
|
||||
public class TokenController {
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
/**
|
||||
* 通过请求头中的token获取用户信息
|
||||
* Token应该在请求头中以 "Authorization: Bearer {token}" 的形式传递
|
||||
*/
|
||||
@Operation(summary = "获取用户信息", description = "通过请求头中的token获取当前用户信息")
|
||||
@GetMapping("/user-info")
|
||||
public Result<UserInfoResponse> getUserInfoByToken(HttpServletRequest request) {
|
||||
UserInfoResponse userInfo = tokenService.getUserInfoByToken(request);
|
||||
return Result.success(userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过请求头中的token获取用户名
|
||||
* Token应该在请求头中以 "Authorization: Bearer {token}" 的形式传递
|
||||
*/
|
||||
@GetMapping("/username")
|
||||
@Operation(summary = "获取用户名", description = "通过请求头中的token获取当前用户名")
|
||||
public Result<String> getUsernameByToken(HttpServletRequest request) {
|
||||
String username = tokenService.getUsernameByToken(request);
|
||||
return Result.success(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证请求头中的token并返回用户ID
|
||||
* Token应该在请求头中以 "Authorization: Bearer {token}" 的形式传递
|
||||
*/
|
||||
@GetMapping("/validate")
|
||||
@Operation(summary = "验证Token", description = "验证请求头中的token并返回用户ID")
|
||||
public Result<String> validateTokenAndGetUserId(HttpServletRequest request) {
|
||||
String userId = tokenService.validateTokenAndGetUserId(request);
|
||||
return Result.success(userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.TopicInteractionCreateRequest;
|
||||
import com.emotion.dto.request.TopicInteractionPageRequest;
|
||||
import com.emotion.dto.request.TopicInteractionUpdateRequest;
|
||||
import com.emotion.dto.response.TopicInteractionResponse;
|
||||
import com.emotion.service.TopicInteractionService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 话题互动控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/topicInteraction")
|
||||
@Tag(name = "话题互动管理", description = "话题互动记录的查询、创建、更新和删除接口")
|
||||
public class TopicInteractionController {
|
||||
|
||||
@Autowired
|
||||
private TopicInteractionService topicInteractionService;
|
||||
|
||||
/**
|
||||
* 分页查询话题互动
|
||||
*/
|
||||
@Operation(summary = "分页查询互动记录", description = "分页查询话题互动记录列表。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<TopicInteractionResponse>> getPage(@Validated TopicInteractionPageRequest request) {
|
||||
PageResult<TopicInteractionResponse> pageResult = topicInteractionService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取话题互动
|
||||
*/
|
||||
@Operation(summary = "获取互动详情", description = "根据 ID 获取互动记录的详细信息。")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<TopicInteractionResponse> getById(@Parameter(description = "互动 ID") @RequestParam String id) {
|
||||
TopicInteractionResponse response = topicInteractionService.getTopicInteractionResponseById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("话题互动不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建话题互动
|
||||
*/
|
||||
@Operation(summary = "创建互动记录", description = "创建一条新的话题互动记录。")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<TopicInteractionResponse> create(@Valid @RequestBody TopicInteractionCreateRequest request) {
|
||||
TopicInteractionResponse response = topicInteractionService.createTopicInteractionWithResponse(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新话题互动
|
||||
*/
|
||||
@Operation(summary = "更新互动记录", description = "修改已有话题互动的内容。")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<TopicInteractionResponse> update(@Valid @RequestBody TopicInteractionUpdateRequest request) {
|
||||
TopicInteractionResponse response = topicInteractionService.updateTopicInteractionWithResponse(request);
|
||||
if (response == null) {
|
||||
return Result.notFound("话题互动不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除话题互动
|
||||
*/
|
||||
@Operation(summary = "删除互动记录", description = "删除指定的话题互动记录。")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(@Parameter(description = "互动 ID") @RequestParam String id) {
|
||||
boolean deleted = topicInteractionService.deleteTopicInteraction(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.tts.TtsTaskCreateRequest;
|
||||
import com.emotion.dto.response.tts.TtsTaskResponse;
|
||||
import com.emotion.service.TtsTaskService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/tts")
|
||||
@Tag(name = "语音合成(TTS)", description = "文字转语音任务创建和查询接口")
|
||||
public class TtsController {
|
||||
|
||||
private final TtsTaskService ttsTaskService;
|
||||
|
||||
@Value("${emotion.tts.output-dir:/data/uploads/emotion-museum/tts}")
|
||||
private String outputDir;
|
||||
|
||||
public TtsController(TtsTaskService ttsTaskService) {
|
||||
this.ttsTaskService = ttsTaskService;
|
||||
}
|
||||
|
||||
@Operation(summary = "创建语音合成任务", description = "根据文本内容创建语音合成任务,返回任务信息。")
|
||||
@PostMapping("/tasks")
|
||||
public Result<TtsTaskResponse> create(@Valid @RequestBody TtsTaskCreateRequest request) {
|
||||
try {
|
||||
return Result.success(ttsTaskService.createOrReuse(request));
|
||||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
return Result.badRequest(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "获取语音合成任务详情", description = "根据任务 ID 获取语音合成任务的详细信息。")
|
||||
@GetMapping("/tasks/{id}")
|
||||
public Result<TtsTaskResponse> detail(@Parameter(description = "任务 ID") @PathVariable String id) {
|
||||
TtsTaskResponse response = ttsTaskService.getTask(id);
|
||||
return response == null ? Result.notFound("TTS task not found") : Result.success(response);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据来源查询语音合成任务", description = "根据来源类型和来源 ID 查询已存在的语音合成任务。")
|
||||
@GetMapping("/tasks/by-source")
|
||||
public Result<TtsTaskResponse> bySource(@Parameter(description = "来源类型") @RequestParam String sourceType,
|
||||
@Parameter(description = "来源 ID") @RequestParam String sourceId,
|
||||
@Parameter(description = "音色") @RequestParam(required = false) String voice,
|
||||
@Parameter(description = "语速") @RequestParam(required = false) Double speechRate,
|
||||
@Parameter(description = "音调") @RequestParam(required = false) Double pitch,
|
||||
@Parameter(description = "情绪") @RequestParam(required = false) String emotion) {
|
||||
return Result.success(ttsTaskService.getBySource(sourceType, sourceId, voice, speechRate, pitch, emotion));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取音频文件", description = "返回已合成的音频音频文件(MP3 或 WAV 格式)。")
|
||||
@GetMapping("/audio/{filename:.+}")
|
||||
public ResponseEntity<Resource> audio(@Parameter(description = "音频文件名") @PathVariable String filename) {
|
||||
if (filename.contains("..") || filename.contains("/") || filename.contains("\\")) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
Path path = Paths.get(outputDir).resolve(filename).normalize();
|
||||
FileSystemResource resource = new FileSystemResource(path);
|
||||
if (!resource.exists() || !resource.isReadable()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
MediaType mediaType = filename.endsWith(".wav")
|
||||
? MediaType.valueOf("audio/wav")
|
||||
: MediaType.valueOf("audio/mpeg");
|
||||
return ResponseEntity.ok()
|
||||
.contentType(mediaType)
|
||||
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS).cachePublic())
|
||||
.body(resource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.UserCreateRequest;
|
||||
import com.emotion.dto.request.UserPageRequest;
|
||||
import com.emotion.dto.request.UserProfileUpdateRequest;
|
||||
import com.emotion.dto.request.UserUpdateRequest;
|
||||
import com.emotion.dto.response.UserResponse;
|
||||
import com.emotion.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 用户控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-22
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
@Tag(name = "用户管理", description = "管理员对用户的增删改查功能")
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 分页查询用户
|
||||
*/
|
||||
@Operation(summary = "分页查询用户", description = "分页查询用户列表")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<UserResponse>> getPage(@Validated UserPageRequest request) {
|
||||
PageResult<UserResponse> pageResult = userService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取用户信息
|
||||
*/
|
||||
@Operation(summary = "根据ID获取用户信息", description = "根据ID获取用户详情")
|
||||
@GetMapping(value = "/detail")
|
||||
public Result<UserResponse> getById(
|
||||
@Parameter(description = "用户ID", required = true)
|
||||
@RequestParam String id) {
|
||||
UserResponse user = userService.getUserResponseById(id);
|
||||
if (user == null) {
|
||||
return Result.notFound("用户不存在");
|
||||
}
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用户
|
||||
*/
|
||||
@Operation(summary = "创建用户", description = "创建新用户")
|
||||
@PostMapping(value = "/create")
|
||||
public Result<UserResponse> create(@Valid @RequestBody UserCreateRequest request) {
|
||||
UserResponse user = userService.createUserWithResponse(request);
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户
|
||||
*/
|
||||
@Operation(summary = "更新用户", description = "更新指定用户信息")
|
||||
@PutMapping(value = "/update")
|
||||
public Result<UserResponse> update(@Valid @RequestBody UserUpdateRequest request) {
|
||||
UserResponse updatedUser = userService.updateUserWithResponse(request);
|
||||
if (updatedUser == null) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success(updatedUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
@Operation(summary = "删除用户", description = "删除指定用户")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<Void> delete(
|
||||
@Parameter(description = "用户ID", required = true)
|
||||
@RequestParam String id) {
|
||||
boolean deleted = userService.deleteUser(id);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户个人资料
|
||||
*/
|
||||
@Operation(summary = "获取当前用户个人资料", description = "获取当前登录用户的个人资料")
|
||||
@GetMapping(value = "/profile")
|
||||
public Result<UserResponse> getCurrentUserProfile() {
|
||||
UserResponse user = userService.getCurrentUserProfileWithResponse();
|
||||
if (user == null) {
|
||||
return Result.unauthorized("用户未登录");
|
||||
}
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新当前用户个人资料
|
||||
*/
|
||||
@PutMapping(value = "/profile")
|
||||
public Result<UserResponse> updateCurrentUserProfile(@Valid @RequestBody UserProfileUpdateRequest request) {
|
||||
UserResponse updatedUser = userService.updateCurrentUserProfileWithResponse(request);
|
||||
if (updatedUser == null) {
|
||||
return Result.unauthorized("用户未登录");
|
||||
}
|
||||
return Result.success(updatedUser);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.userprofile.UserProfileCreateRequest;
|
||||
import com.emotion.dto.request.userprofile.UserProfilePageRequest;
|
||||
import com.emotion.dto.request.userprofile.UserProfileUpdateRequest;
|
||||
import com.emotion.dto.response.userprofile.UserProfileResponse;
|
||||
import com.emotion.service.UserProfileService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户档案控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-21
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/user-profile")
|
||||
@Tag(name = "用户档案管理", description = "用户档案的查询、创建、更新、删除和 AI 设置管理接口")
|
||||
public class UserProfileController {
|
||||
|
||||
@Autowired
|
||||
private UserProfileService userProfileService;
|
||||
|
||||
/**
|
||||
* 新增档案
|
||||
*/
|
||||
@Operation(summary = "创建用户档案", description = "为当前用户创建个人档案,包含昵称、MBTI 人格类型、童年经历等。")
|
||||
@PostMapping("/create")
|
||||
public Result<UserProfileResponse> create(@Valid @RequestBody UserProfileCreateRequest request) {
|
||||
UserProfileResponse response = userProfileService.createProfile(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除档案
|
||||
*/
|
||||
@Operation(summary = "删除用户档案", description = "删除指定的用户档案。")
|
||||
@DeleteMapping("/delete")
|
||||
public Result<Void> delete(@Parameter(description = "档案 ID") @RequestParam String id) {
|
||||
boolean success = userProfileService.deleteProfile(id);
|
||||
if (success) {
|
||||
return Result.success();
|
||||
} else {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改档案
|
||||
*/
|
||||
@Operation(summary = "更新用户档案", description = "更新当前用户的个人档案信息。")
|
||||
@PutMapping("/update")
|
||||
public Result<UserProfileResponse> update(@Valid @RequestBody UserProfileUpdateRequest request) {
|
||||
UserProfileResponse response = userProfileService.updateProfile(request);
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询详情
|
||||
*/
|
||||
@Operation(summary = "获取档案详情", description = "根据 ID 获取用户档案详情。")
|
||||
@GetMapping("/detail")
|
||||
public Result<UserProfileResponse> getById(@Parameter(description = "档案 ID") @RequestParam String id) {
|
||||
UserProfileResponse response = userProfileService.getProfileById(id);
|
||||
if (response == null) {
|
||||
return Result.notFound("档案不存在");
|
||||
}
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户的档案
|
||||
*/
|
||||
@Operation(summary = "获取当前用户档案", description = "获取当前登录用户的个人档案信息。")
|
||||
@GetMapping("/me")
|
||||
public Result<UserProfileResponse> getCurrentProfile() {
|
||||
UserProfileResponse response = userProfileService.getCurrentUserProfile();
|
||||
// 如果不存在,返回null data,不报错
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*/
|
||||
@Operation(summary = "分页查询用户档案", description = "分页查询用户档案列表。")
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<UserProfileResponse>> getPage(@Validated UserProfilePageRequest request) {
|
||||
PageResult<UserProfileResponse> pageResult = userProfileService.getProfilePage(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表查询
|
||||
*/
|
||||
@Operation(summary = "列表查询用户档案", description = "列表查询用户档案。")
|
||||
@GetMapping("/list")
|
||||
public Result<List<UserProfileResponse>> getList(@Validated UserProfilePageRequest request) {
|
||||
List<UserProfileResponse> list = userProfileService.getProfileList(request);
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 迁移用户档案中的生命事件数据到t_life_event表
|
||||
* 将childhood、peak、valley数据迁移到生命事件表
|
||||
* 注意:此接口为一次性数据迁移接口,迁移完成后可删除
|
||||
*/
|
||||
@Operation(summary = "迁移生活事件", description = "将生活事件模块的数据迁移到用户档案中。")
|
||||
@PostMapping("/migrateLifeEvents")
|
||||
public Result<Integer> migrateLifeEvents() {
|
||||
int count = userProfileService.migrateLifeEventsFromProfiles();
|
||||
return Result.success(count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.UserStatsCreateRequest;
|
||||
import com.emotion.dto.request.UserStatsIncrementRequest;
|
||||
import com.emotion.dto.request.UserStatsPageRequest;
|
||||
import com.emotion.dto.request.UserStatsUpdateValueRequest;
|
||||
import com.emotion.dto.response.UserStatsResponse;
|
||||
import com.emotion.service.UserStatsService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户统计控制器
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/userStats")
|
||||
@Validated
|
||||
@Tag(name = "用户统计管理", description = "用户统计数据的查询、创建、更新、增量操作和重新计算接口")
|
||||
public class UserStatsController {
|
||||
|
||||
@Autowired
|
||||
private UserStatsService userStatsService;
|
||||
|
||||
/**
|
||||
* 分页查询用户统计
|
||||
*/
|
||||
@Operation(summary = "分页查询用户统计", description = "分页查询用户统计数据。")
|
||||
@GetMapping(value = "/page")
|
||||
public Result<PageResult<UserStatsResponse>> getPage(@Validated UserStatsPageRequest request) {
|
||||
PageResult<UserStatsResponse> pageResult = userStatsService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或更新用户统计
|
||||
*/
|
||||
@Operation(summary = "创建或更新用户统计", description = "创建新用户统计数据或更新已有数据。")
|
||||
@PostMapping(value = "/createOrUpdate")
|
||||
public Result<UserStatsResponse> createOrUpdate(@Valid @RequestBody UserStatsCreateRequest request) {
|
||||
UserStatsResponse stats = userStatsService.createOrUpdateUserStatsWithResponse(request);
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户统计值
|
||||
*/
|
||||
@Operation(summary = "更新用户统计值", description = "直接更新指定统计项的值。")
|
||||
@PutMapping(value = "/updateStatsValue")
|
||||
public Result<Void> updateStatsValue(@Valid @RequestBody UserStatsUpdateValueRequest request) {
|
||||
boolean updated = userStatsService.updateStatsValue(request);
|
||||
if (!updated) {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加用户统计值
|
||||
*/
|
||||
@Operation(summary = "增加用户统计值", description = "对指定统计项进行增量累加操作。")
|
||||
@PutMapping(value = "/incrementStatsValue")
|
||||
public Result<Void> incrementStatsValue(@Valid @RequestBody UserStatsIncrementRequest request) {
|
||||
boolean updated = userStatsService.incrementStatsValue(request);
|
||||
if (!updated) {
|
||||
return Result.error("增加失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算用户统计
|
||||
*/
|
||||
@Operation(summary = "重新计算用户统计", description = "基于原始数据重新计算用户的统计值。")
|
||||
@PutMapping(value = "/recalculateUserStats")
|
||||
public Result<Void> recalculateUserStats(@Parameter(description = "用户 ID") @RequestParam String userId) {
|
||||
boolean recalculated = userStatsService.recalculateUserStats(userId);
|
||||
if (!recalculated) {
|
||||
return Result.error("重新计算失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算所有用户统计
|
||||
*/
|
||||
@Operation(summary = "重新计算所有用户统计", description = "重新计算所有用户的统计值(管理员操作)。")
|
||||
@PutMapping(value = "/recalculateAll")
|
||||
public Result<Void> recalculateAllUserStats() {
|
||||
boolean recalculated = userStatsService.recalculateAllUserStats();
|
||||
if (!recalculated) {
|
||||
return Result.error("重新计算失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除过期的统计数据
|
||||
*/
|
||||
@Operation(summary = "删除过期统计数据", description = "清理超过指定天数的过期统计数据。")
|
||||
@DeleteMapping(value = "/deleteExpired")
|
||||
public Result<Void> deleteExpiredStats(@Parameter(description = "过期天数阈值") @RequestParam(defaultValue = "30") Integer days) {
|
||||
boolean deleted = userStatsService.deleteExpiredStats(days);
|
||||
if (!deleted) {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 管理员修改密码请求(修改自己的密码,需要原密码)
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2026-05-10
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "管理员修改密码请求")
|
||||
public class AdminChangePasswordRequest {
|
||||
|
||||
@NotBlank(message = "原密码不能为空")
|
||||
@Schema(description = "原密码")
|
||||
private String oldPassword;
|
||||
|
||||
@NotBlank(message = "新密码不能为空")
|
||||
@Schema(description = "新密码")
|
||||
private String newPassword;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 管理员创建请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-10-27
|
||||
*/
|
||||
@Data
|
||||
public class AdminCreateRequest {
|
||||
|
||||
/**
|
||||
* 管理员账号
|
||||
*/
|
||||
@NotBlank(message = "账号不能为空")
|
||||
@Size(min = 3, max = 50, message = "账号长度必须在3-50个字符之间")
|
||||
@Schema(description = "管理员账号")
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间")
|
||||
@Schema(description = "初始密码")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 管理员姓名
|
||||
*/
|
||||
@NotBlank(message = "姓名不能为空")
|
||||
@Size(min = 2, max = 50, message = "姓名长度必须在2-50个字符之间")
|
||||
@Schema(description = "管理员姓名")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(max = 100, message = "邮箱长度不能超过100个字符")
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
@Size(max = 500, message = "头像URL长度不能超过500个字符")
|
||||
@Schema(description = "头像URL")
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 角色
|
||||
*/
|
||||
@NotBlank(message = "角色不能为空")
|
||||
@Pattern(regexp = "^(super_admin|admin|operator)$", message = "角色必须是super_admin、admin或operator")
|
||||
@Schema(description = "角色(super_admin/admin/operator)")
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 权限列表(JSON格式)
|
||||
*/
|
||||
@Schema(description = "权限列表(JSON格式)")
|
||||
private String permissions;
|
||||
|
||||
/**
|
||||
* 所属部门
|
||||
*/
|
||||
@Size(max = 50, message = "部门长度不能超过50个字符")
|
||||
@Schema(description = "所属部门")
|
||||
private String department;
|
||||
|
||||
/**
|
||||
* 职位
|
||||
*/
|
||||
@Size(max = 50, message = "职位长度不能超过50个字符")
|
||||
@Schema(description = "职位")
|
||||
private String position;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 管理员登录请求
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @date 2025-10-27
|
||||
*/
|
||||
@Data
|
||||
public class AdminLoginRequest {
|
||||
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
@NotBlank(message = "账号不能为空")
|
||||
@Size(min = 3, max = 50, message = "账号长度必须在3-50个字符之间")
|
||||
@Schema(description = "管理员账号")
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间")
|
||||
@Schema(description = "管理员密码")
|
||||
private String password;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 管理员分页查询请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-10-27
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AdminPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
@Size(max = 50, message = "账号长度不能超过50个字符")
|
||||
@Schema(description = "账号(模糊搜索)")
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
@Size(max = 50, message = "姓名长度不能超过50个字符")
|
||||
@Schema(description = "姓名(模糊搜索)")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Size(max = 100, message = "邮箱长度不能超过100个字符")
|
||||
@Schema(description = "邮箱(模糊搜索)")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@Size(max = 20, message = "手机号长度不能超过20个字符")
|
||||
@Schema(description = "手机号(模糊搜索)")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 角色
|
||||
*/
|
||||
@Size(max = 20, message = "角色长度不能超过20个字符")
|
||||
@Schema(description = "角色")
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 状态: 0-禁用, 1-正常
|
||||
*/
|
||||
@Schema(description = "状态(0-禁用,1-正常)")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
@Size(max = 50, message = "部门长度不能超过50个字符")
|
||||
@Schema(description = "部门(模糊搜索)")
|
||||
private String department;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 超级管理员重置其他管理员密码请求(不需要原密码)
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2026-05-10
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "管理员重置密码请求")
|
||||
public class AdminResetPasswordRequest {
|
||||
|
||||
@NotBlank(message = "管理员ID不能为空")
|
||||
@Schema(description = "管理员ID")
|
||||
private String id;
|
||||
|
||||
@NotBlank(message = "新密码不能为空")
|
||||
@Schema(description = "新密码")
|
||||
private String newPassword;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 管理员更新请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-10-27
|
||||
*/
|
||||
@Data
|
||||
public class AdminUpdateRequest {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@NotBlank(message = "ID不能为空")
|
||||
@Schema(description = "管理员ID")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 管理员姓名
|
||||
*/
|
||||
@Size(min = 2, max = 50, message = "姓名长度必须在2-50个字符之间")
|
||||
@Schema(description = "管理员姓名")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(max = 100, message = "邮箱长度不能超过100个字符")
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
@Size(max = 500, message = "头像URL长度不能超过500个字符")
|
||||
@Schema(description = "头像URL")
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 角色
|
||||
*/
|
||||
@Pattern(regexp = "^(super_admin|admin|operator)$", message = "角色必须是super_admin、admin或operator")
|
||||
@Schema(description = "角色(super_admin/admin/operator)")
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 权限列表(JSON格式)
|
||||
*/
|
||||
@Schema(description = "权限列表(JSON格式)")
|
||||
private String permissions;
|
||||
|
||||
/**
|
||||
* 状态: 0-禁用, 1-正常
|
||||
*/
|
||||
@Schema(description = "状态(0-禁用,1-正常)")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 所属部门
|
||||
*/
|
||||
@Size(max = 50, message = "部门长度不能超过50个字符")
|
||||
@Schema(description = "所属部门")
|
||||
private String department;
|
||||
|
||||
/**
|
||||
* 职位
|
||||
*/
|
||||
@Size(max = 50, message = "职位长度不能超过50个字符")
|
||||
@Schema(description = "职位")
|
||||
private String position;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* AI聊天请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-24
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AiChatRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@NotBlank(message = "消息内容不能为空")
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
|
||||
/**
|
||||
* AI配置调用次数统计请求
|
||||
* 用于管理后台仪表盘:按 t_ai_config 配置统计 t_coze_api_call 调用次数
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-12-24
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "AI配置调用次数统计请求")
|
||||
public class AiConfigCallStatsRequest {
|
||||
|
||||
/**
|
||||
* 返回条数限制(默认 20,最大 200)
|
||||
*/
|
||||
@Min(1)
|
||||
@Max(200)
|
||||
@Schema(description = "返回条数限制(默认20,最大200)", example = "20")
|
||||
private Integer limit;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* AI总结请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-24
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AiSummaryRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
@NotBlank(message = "会话ID不能为空")
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 接口端点分页查询请求
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ApiEndpointListRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* HTTP 方法过滤:GET/POST/PUT/DELETE/PATCH
|
||||
*/
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 标签过滤
|
||||
*/
|
||||
private String tags;
|
||||
|
||||
/**
|
||||
* 是否仅显示废弃接口
|
||||
*/
|
||||
private Integer deprecated;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 代理测试请求参数
|
||||
*/
|
||||
@Data
|
||||
public class ApiTestProxyRequest {
|
||||
|
||||
@NotBlank(message = "请求方法不能为空")
|
||||
private String method;
|
||||
|
||||
@NotBlank(message = "接口路径不能为空")
|
||||
private String path;
|
||||
|
||||
private String body;
|
||||
|
||||
private Map<String, String> headers;
|
||||
|
||||
private Map<String, String> params;
|
||||
|
||||
private Integer timeoutSeconds;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 基础请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Data
|
||||
public class BaseRequest {
|
||||
|
||||
/**
|
||||
* 请求ID,用于链路追踪
|
||||
*/
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 客户端时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 聊天统计请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-24
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ChatStatsRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String conversationId;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 对话创建请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-24
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ConversationCreateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 对话ID(更新时使用)
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 对话标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 对话类型
|
||||
*/
|
||||
private String type;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 对话分页请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ConversationPageRequest extends PageRequest {
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 对话状态(可选)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 对话类型(可选)
|
||||
*/
|
||||
private String type;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 创建日记评论请求DTO
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Data
|
||||
public class DiaryCommentCreateRequest {
|
||||
|
||||
/**
|
||||
* 评论ID (用于更新操作)
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 日记ID
|
||||
*/
|
||||
@NotBlank(message = "日记ID不能为空")
|
||||
private String diaryId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
@NotBlank(message = "评论内容不能为空")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 评论图片
|
||||
*/
|
||||
private List<String> images;
|
||||
|
||||
/**
|
||||
* 父评论ID (用于回复功能)
|
||||
*/
|
||||
private String parentCommentId;
|
||||
|
||||
/**
|
||||
* 是否匿名评论: 0-实名, 1-匿名
|
||||
*/
|
||||
@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 huazhongmin
|
||||
* @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,97 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 创建日记请求DTO
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Data
|
||||
public class DiaryPostCreateRequest {
|
||||
|
||||
/**
|
||||
* 日记标题
|
||||
*/
|
||||
@Size(max = 200, message = "日记标题长度不能超过200个字符")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 日记内容
|
||||
*/
|
||||
@NotBlank(message = "日记内容不能为空")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 图片列表
|
||||
*/
|
||||
private List<String> images;
|
||||
|
||||
/**
|
||||
* 视频列表
|
||||
*/
|
||||
private List<String> videos;
|
||||
|
||||
/**
|
||||
* 发布地点
|
||||
*/
|
||||
@Size(max = 200, message = "发布地点长度不能超过200个字符")
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
private BigDecimal latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private BigDecimal longitude;
|
||||
|
||||
/**
|
||||
* 天气信息
|
||||
*/
|
||||
@Size(max = 50, message = "天气信息长度不能超过50个字符")
|
||||
private String weather;
|
||||
|
||||
/**
|
||||
* 心情状态
|
||||
*/
|
||||
@Size(max = 50, message = "心情状态长度不能超过50个字符")
|
||||
private String mood;
|
||||
|
||||
/**
|
||||
* 心情评分 (0-10)
|
||||
*/
|
||||
private BigDecimal moodScore;
|
||||
|
||||
/**
|
||||
* 标签列表
|
||||
*/
|
||||
private List<String> tags;
|
||||
|
||||
/**
|
||||
* 是否公开: 0-仅自己可见, 1-公开
|
||||
*/
|
||||
@NotNull(message = "是否公开不能为空")
|
||||
private Integer isPublic;
|
||||
|
||||
/**
|
||||
* 是否匿名发布: 0-实名, 1-匿名
|
||||
*/
|
||||
@NotNull(message = "是否匿名不能为空")
|
||||
private Integer isAnonymous;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 日记分页请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @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;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 更新日记请求DTO
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Data
|
||||
public class DiaryPostUpdateRequest {
|
||||
|
||||
/**
|
||||
* 日记ID
|
||||
*/
|
||||
@NotBlank(message = "日记ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 日记标题
|
||||
*/
|
||||
@Size(max = 200, message = "日记标题长度不能超过200个字符")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 日记内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 图片列表
|
||||
*/
|
||||
private List<String> images;
|
||||
|
||||
/**
|
||||
* 视频列表
|
||||
*/
|
||||
private List<String> videos;
|
||||
|
||||
/**
|
||||
* 发布地点
|
||||
*/
|
||||
@Size(max = 200, message = "发布地点长度不能超过200个字符")
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
private BigDecimal latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private BigDecimal longitude;
|
||||
|
||||
/**
|
||||
* 天气信息
|
||||
*/
|
||||
@Size(max = 50, message = "天气信息长度不能超过50个字符")
|
||||
private String weather;
|
||||
|
||||
/**
|
||||
* 心情状态
|
||||
*/
|
||||
@Size(max = 50, message = "心情状态长度不能超过50个字符")
|
||||
private String mood;
|
||||
|
||||
/**
|
||||
* 心情评分 (0-10)
|
||||
*/
|
||||
private BigDecimal moodScore;
|
||||
|
||||
/**
|
||||
* 标签列表
|
||||
*/
|
||||
private List<String> tags;
|
||||
|
||||
/**
|
||||
* 是否公开: 0-仅自己可见, 1-公开
|
||||
*/
|
||||
private Integer isPublic;
|
||||
|
||||
/**
|
||||
* 是否匿名发布: 0-实名, 1-匿名
|
||||
*/
|
||||
private Integer isAnonymous;
|
||||
|
||||
/**
|
||||
* 状态: draft-草稿, published-已发布, hidden-隐藏, deleted-已删除
|
||||
*/
|
||||
private String status;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 情绪分析创建请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionAnalysisCreateRequest {
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
@NotBlank(message = "消息ID不能为空")
|
||||
@Size(max = 64, message = "消息ID长度不能超过64个字符")
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
@Size(max = 32, message = "用户ID长度不能超过32个字符")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 主要情绪
|
||||
*/
|
||||
@NotBlank(message = "主要情绪不能为空")
|
||||
@Size(max = 32, message = "主要情绪长度不能超过32个字符")
|
||||
private String primaryEmotion;
|
||||
|
||||
/**
|
||||
* 情绪极性
|
||||
*/
|
||||
@Size(max = 9, message = "情绪极性长度不能超过9个字符")
|
||||
private String polarity;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
@NotNull(message = "情绪强度不能为空")
|
||||
private Double intensity;
|
||||
|
||||
/**
|
||||
* 置信度
|
||||
*/
|
||||
@NotNull(message = "置信度不能为空")
|
||||
private Double confidence;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 情绪分析分页请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EmotionAnalysisPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
@Size(max = 32, message = "用户ID长度不能超过32个字符")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 消息ID(可选)
|
||||
*/
|
||||
@Size(max = 64, message = "消息ID长度不能超过64个字符")
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 主要情绪(可选)
|
||||
*/
|
||||
@Size(max = 32, message = "主要情绪长度不能超过32个字符")
|
||||
private String primaryEmotion;
|
||||
|
||||
/**
|
||||
* 情绪极性(可选)
|
||||
*/
|
||||
@Size(max = 9, message = "情绪极性长度不能超过9个字符")
|
||||
private String polarity;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 情绪分析更新请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionAnalysisUpdateRequest {
|
||||
|
||||
/**
|
||||
* 情绪分析ID
|
||||
*/
|
||||
@NotBlank(message = "情绪分析ID不能为空")
|
||||
@Size(max = 32, message = "情绪分析ID长度不能超过32个字符")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
@Size(max = 64, message = "消息ID长度不能超过64个字符")
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@Size(max = 32, message = "用户ID长度不能超过32个字符")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 主要情绪
|
||||
*/
|
||||
@Size(max = 32, message = "主要情绪长度不能超过32个字符")
|
||||
private String primaryEmotion;
|
||||
|
||||
/**
|
||||
* 情绪极性
|
||||
*/
|
||||
@Size(max = 9, message = "情绪极性长度不能超过9个字符")
|
||||
private String polarity;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
private Double intensity;
|
||||
|
||||
/**
|
||||
* 置信度
|
||||
*/
|
||||
private Double confidence;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 情绪记录创建请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionRecordCreateRequest {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
@Size(max = 32, message = "用户ID长度不能超过32个字符")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 记录日期
|
||||
*/
|
||||
@NotNull(message = "记录日期不能为空")
|
||||
private LocalDate recordDate;
|
||||
|
||||
/**
|
||||
* 情绪类型
|
||||
*/
|
||||
@NotBlank(message = "情绪类型不能为空")
|
||||
@Size(max = 32, message = "情绪类型长度不能超过32个字符")
|
||||
private String emotionType;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
@NotNull(message = "情绪强度不能为空")
|
||||
private BigDecimal intensity;
|
||||
|
||||
/**
|
||||
* 触发因素
|
||||
*/
|
||||
@Size(max = 768, message = "触发因素长度不能超过768个字符")
|
||||
private String triggers;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
@Size(max = 768, message = "描述长度不能超过768个字符")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private List<String> tags;
|
||||
|
||||
/**
|
||||
* 天气
|
||||
*/
|
||||
@Size(max = 64, message = "天气长度不能超过64个字符")
|
||||
private String weather;
|
||||
|
||||
/**
|
||||
* 地点
|
||||
*/
|
||||
@Size(max = 128, message = "地点长度不能超过128个字符")
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 活动
|
||||
*/
|
||||
@Size(max = 128, message = "活动长度不能超过128个字符")
|
||||
private String activity;
|
||||
|
||||
/**
|
||||
* 相关人物
|
||||
*/
|
||||
@Size(max = 256, message = "相关人物长度不能超过256个字符")
|
||||
private String people;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@Size(max = 1024, message = "备注长度不能超过1024个字符")
|
||||
private String notes;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 情绪记录分页请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EmotionRecordPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
@Size(max = 32, message = "用户ID长度不能超过32个字符")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 情绪类型(可选)
|
||||
*/
|
||||
@Size(max = 32, message = "情绪类型长度不能超过32个字符")
|
||||
private String emotionType;
|
||||
|
||||
/**
|
||||
* 地点(可选)
|
||||
*/
|
||||
@Size(max = 128, message = "地点长度不能超过128个字符")
|
||||
private String location;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 情绪记录更新请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionRecordUpdateRequest {
|
||||
|
||||
/**
|
||||
* 情绪记录ID
|
||||
*/
|
||||
@NotBlank(message = "情绪记录ID不能为空")
|
||||
@Size(max = 32, message = "情绪记录ID长度不能超过32个字符")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 记录日期
|
||||
*/
|
||||
private LocalDate recordDate;
|
||||
|
||||
/**
|
||||
* 情绪类型
|
||||
*/
|
||||
@Size(max = 32, message = "情绪类型长度不能超过32个字符")
|
||||
private String emotionType;
|
||||
|
||||
/**
|
||||
* 情绪强度
|
||||
*/
|
||||
private BigDecimal intensity;
|
||||
|
||||
/**
|
||||
* 触发因素
|
||||
*/
|
||||
@Size(max = 768, message = "触发因素长度不能超过768个字符")
|
||||
private String triggers;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
@Size(max = 768, message = "描述长度不能超过768个字符")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private List<String> tags;
|
||||
|
||||
/**
|
||||
* 天气
|
||||
*/
|
||||
@Size(max = 64, message = "天气长度不能超过64个字符")
|
||||
private String weather;
|
||||
|
||||
/**
|
||||
* 地点
|
||||
*/
|
||||
@Size(max = 128, message = "地点长度不能超过128个字符")
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 活动
|
||||
*/
|
||||
@Size(max = 128, message = "活动长度不能超过128个字符")
|
||||
private String activity;
|
||||
|
||||
/**
|
||||
* 相关人物
|
||||
*/
|
||||
@Size(max = 256, message = "相关人物长度不能超过256个字符")
|
||||
private String people;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@Size(max = 1024, message = "备注长度不能超过1024个字符")
|
||||
private String notes;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 情绪总结生成请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionSummaryGenerateRequest {
|
||||
// 目前情绪总结生成不需要额外参数
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 情绪总结状态请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-09-08
|
||||
*/
|
||||
@Data
|
||||
public class EmotionSummaryStatusRequest {
|
||||
// 目前情绪总结状态查询不需要额外参数
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 爽文剧本创建请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EpicScriptCreateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 剧本标题
|
||||
*/
|
||||
@NotBlank(message = "剧本标题不能为空")
|
||||
@Size(max = 200, message = "剧本标题长度不能超过200个字符")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 剧本主题/渴望
|
||||
*/
|
||||
private String theme;
|
||||
|
||||
/**
|
||||
* 剧本风格: career-职场逆袭, love-情感圆满, fantasy-玄幻觉醒
|
||||
*/
|
||||
private String style;
|
||||
|
||||
/**
|
||||
* 篇幅长度: medium-标准篇, long-长篇
|
||||
*/
|
||||
private String length;
|
||||
|
||||
/**
|
||||
* 序幕:低谷回响
|
||||
*/
|
||||
private String plotIntro;
|
||||
|
||||
/**
|
||||
* 转折:契机出现
|
||||
*/
|
||||
private String plotTurning;
|
||||
|
||||
/**
|
||||
* 高潮:命运抉择
|
||||
*/
|
||||
private String plotClimax;
|
||||
|
||||
/**
|
||||
* 结局:新的开始
|
||||
*/
|
||||
private String plotEnding;
|
||||
|
||||
/**
|
||||
* 完整剧情JSON结构
|
||||
*/
|
||||
private Map<String, Object> plotJson;
|
||||
|
||||
/**
|
||||
* 是否当前选中
|
||||
*/
|
||||
private Boolean isSelected;
|
||||
|
||||
/**
|
||||
* 角色信息(前端传入,用于AI生成)
|
||||
*/
|
||||
private String characterInfo;
|
||||
|
||||
/**
|
||||
* 过往经历关键词(前端传入,用于AI生成)
|
||||
*/
|
||||
private String lifeEventsSummary;
|
||||
/**
|
||||
* 是否使用用户已确认的社交画像增强剧本生成。
|
||||
*/
|
||||
private Boolean useSocialInsights;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EpicScriptInspirationRequest extends BaseRequest {
|
||||
|
||||
@NotBlank(message = "灵感内容不能为空")
|
||||
@Size(max = 500, message = "灵感内容不能超过500个字符")
|
||||
private String prompt;
|
||||
|
||||
private String mode = "inspiration";
|
||||
|
||||
private String style;
|
||||
|
||||
private String length;
|
||||
|
||||
private String characterInfo;
|
||||
|
||||
private String lifeEventsSummary;
|
||||
|
||||
private String source;
|
||||
/**
|
||||
* 是否使用用户已确认的社交画像增强生成。
|
||||
*/
|
||||
private Boolean useSocialInsights;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 爽文剧本分页查询请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EpicScriptPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 剧本风格筛选
|
||||
*/
|
||||
private String style;
|
||||
|
||||
/**
|
||||
* 篇幅长度筛选
|
||||
*/
|
||||
private String length;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 爽文剧本更新请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EpicScriptUpdateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 剧本ID
|
||||
*/
|
||||
@NotBlank(message = "剧本ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 剧本标题
|
||||
*/
|
||||
@Size(max = 200, message = "剧本标题长度不能超过200个字符")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 剧本主题/渴望
|
||||
*/
|
||||
private String theme;
|
||||
|
||||
/**
|
||||
* 剧本风格: career-职场逆袭, love-情感圆满, fantasy-玄幻觉醒
|
||||
*/
|
||||
private String style;
|
||||
|
||||
/**
|
||||
* 篇幅长度: medium-标准篇, long-长篇
|
||||
*/
|
||||
private String length;
|
||||
|
||||
/**
|
||||
* 序幕:低谷回响
|
||||
*/
|
||||
private String plotIntro;
|
||||
|
||||
/**
|
||||
* 转折:契机出现
|
||||
*/
|
||||
private String plotTurning;
|
||||
|
||||
/**
|
||||
* 高潮:命运抉择
|
||||
*/
|
||||
private String plotClimax;
|
||||
|
||||
/**
|
||||
* 结局:新的开始
|
||||
*/
|
||||
private String plotEnding;
|
||||
|
||||
/**
|
||||
* 完整剧情JSON结构
|
||||
*/
|
||||
private Map<String, Object> plotJson;
|
||||
|
||||
/**
|
||||
* 是否当前选中
|
||||
*/
|
||||
private Boolean isSelected;
|
||||
|
||||
/**
|
||||
* 角色信息(用于AI重新生成)
|
||||
*/
|
||||
private String characterInfo;
|
||||
|
||||
/**
|
||||
* 过往经历摘要(用于AI重新生成)
|
||||
*/
|
||||
private String lifeEventsSummary;
|
||||
|
||||
/**
|
||||
* 是否需要重新生成AI内容
|
||||
*/
|
||||
private Boolean regenerateContent;
|
||||
/**
|
||||
* 是否使用用户已确认的社交画像增强重新生成。
|
||||
*/
|
||||
private Boolean useSocialInsights;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 访客聊天请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-24
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GuestChatRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@NotBlank(message = "消息内容不能为空")
|
||||
private String message;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 访客用户信息请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-24
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GuestUserInfoRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
private String clientIp;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* ID请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class IdRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@NotBlank(message = "ID不能为空")
|
||||
private String id;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生命事件创建请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LifeEventCreateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 事件类型: daily_log-日常记录, milestone-里程碑
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 事件日期 (ISO格式字符串)
|
||||
*/
|
||||
private String eventDate;
|
||||
|
||||
/**
|
||||
* 时间模式: date-具体日期, month-年月, season-季节, range-时间范围
|
||||
*/
|
||||
private String timeMode;
|
||||
|
||||
/**
|
||||
* 原始时间文本
|
||||
*/
|
||||
private String eventDateText;
|
||||
|
||||
/**
|
||||
* 结束日期,仅时间范围使用
|
||||
*/
|
||||
private String eventEndDate;
|
||||
|
||||
/**
|
||||
* 事件标题
|
||||
*/
|
||||
@NotBlank(message = "事件标题不能为空")
|
||||
@Size(max = 200, message = "事件标题长度不能超过200个字符")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 事件内容
|
||||
*/
|
||||
@NotBlank(message = "事件内容不能为空")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* AI疗愈回复
|
||||
*/
|
||||
private String aiReply;
|
||||
|
||||
/**
|
||||
* 情绪类型
|
||||
*/
|
||||
private String emotionType;
|
||||
|
||||
/**
|
||||
* 情绪评分
|
||||
*/
|
||||
private Double emotionScore;
|
||||
|
||||
/**
|
||||
* 标签列表
|
||||
*/
|
||||
private List<String> tags;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 生命事件分页查询请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LifeEventPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 事件类型筛选
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
private String startDate;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
private String endDate;
|
||||
|
||||
/**
|
||||
* 情绪类型筛选
|
||||
*/
|
||||
private String emotionType;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生命事件更新请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LifeEventUpdateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 事件ID
|
||||
*/
|
||||
@NotBlank(message = "事件ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 事件类型: daily_log-日常记录, milestone-里程碑
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 事件日期 (ISO格式字符串)
|
||||
*/
|
||||
private String eventDate;
|
||||
|
||||
/**
|
||||
* 时间模式: date-具体日期, month-年月, season-季节, range-时间范围
|
||||
*/
|
||||
private String timeMode;
|
||||
|
||||
/**
|
||||
* 原始时间文本
|
||||
*/
|
||||
private String eventDateText;
|
||||
|
||||
/**
|
||||
* 结束日期,仅时间范围使用
|
||||
*/
|
||||
private String eventEndDate;
|
||||
|
||||
/**
|
||||
* 事件标题
|
||||
*/
|
||||
@Size(max = 200, message = "事件标题长度不能超过200个字符")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 事件内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* AI疗愈回复
|
||||
*/
|
||||
private String aiReply;
|
||||
|
||||
/**
|
||||
* 情绪类型
|
||||
*/
|
||||
private String emotionType;
|
||||
|
||||
/**
|
||||
* 情绪评分
|
||||
*/
|
||||
private Double emotionScore;
|
||||
|
||||
/**
|
||||
* 标签列表
|
||||
*/
|
||||
private List<String> tags;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 实现路径创建请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LifePathCreateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 关联剧本ID
|
||||
*/
|
||||
@NotBlank(message = "关联剧本ID不能为空")
|
||||
private String scriptId;
|
||||
|
||||
/**
|
||||
* 路径标题
|
||||
*/
|
||||
@Size(max = 200, message = "路径标题长度不能超过200个字符")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 路径描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 路径步骤列表
|
||||
* 每个步骤包含: phase, time, content, action, resources, habit
|
||||
*/
|
||||
private List<Map<String, Object>> steps;
|
||||
|
||||
/**
|
||||
* 状态: active-进行中, completed-已完成, archived-已归档
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 完成进度百分比
|
||||
*/
|
||||
private Double progress;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import com.emotion.common.BasePageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 实现路径分页查询请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LifePathPageRequest extends BasePageRequest {
|
||||
|
||||
/**
|
||||
* 关联剧本ID筛选
|
||||
*/
|
||||
private String scriptId;
|
||||
|
||||
/**
|
||||
* 状态筛选
|
||||
*/
|
||||
private String status;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 实现路径更新请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LifePathUpdateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 路径ID
|
||||
*/
|
||||
@NotBlank(message = "路径ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 路径标题
|
||||
*/
|
||||
@Size(max = 200, message = "路径标题长度不能超过200个字符")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 路径描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 路径步骤列表
|
||||
* 每个步骤包含: phase, time, content, action, resources, habit
|
||||
*/
|
||||
private List<Map<String, Object>> steps;
|
||||
|
||||
/**
|
||||
* 状态: active-进行中, completed-已完成, archived-已归档
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 完成进度百分比
|
||||
*/
|
||||
private Double progress;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 登录请求
|
||||
* 简化版:仅需手机号和短信验证码
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-23
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LoginRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 短信验证码
|
||||
*/
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
@Size(min = 6, max = 6, message = "验证码必须为6位")
|
||||
@Schema(description = "短信验证码")
|
||||
private String smsCode;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 消息创建请求类
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-24
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MessageCreateRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
@NotBlank(message = "会话ID不能为空")
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotBlank(message = "用户ID不能为空")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@NotBlank(message = "消息内容不能为空")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 内容类型
|
||||
*/
|
||||
private String contentType;
|
||||
|
||||
/**
|
||||
* 发送者类型
|
||||
*/
|
||||
private String senderType;
|
||||
|
||||
/**
|
||||
* 发送者ID
|
||||
*/
|
||||
private String senderId;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user