优化并移除不再有任何用处的文件
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
package com.emotionmuseum;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* 情绪博物馆后端服务启动类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.emotionmuseum.mapper")
|
||||
@EnableCaching
|
||||
@EnableAsync
|
||||
@EnableTransactionManagement
|
||||
public class EmotionMuseumApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(EmotionMuseumApplication.class, args);
|
||||
System.out.println("=================================");
|
||||
System.out.println("情绪博物馆后端服务启动成功!");
|
||||
System.out.println("服务端口: 19089");
|
||||
System.out.println("API文档: http://localhost:19089/api/swagger-ui.html");
|
||||
System.out.println("健康检查: http://localhost:19089/api/health");
|
||||
System.out.println("=================================");
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package com.emotionmuseum.config;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* 异步配置类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
public class AsyncConfig implements AsyncConfigurer {
|
||||
|
||||
/**
|
||||
* 默认异步执行器
|
||||
*/
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(20);
|
||||
executor.setMaxPoolSize(100);
|
||||
executor.setQueueCapacity(500);
|
||||
executor.setKeepAliveSeconds(60);
|
||||
executor.setThreadNamePrefix("emotion-async-");
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AI任务执行器
|
||||
*/
|
||||
@Bean("aiTaskExecutor")
|
||||
public Executor aiTaskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(10);
|
||||
executor.setMaxPoolSize(50);
|
||||
executor.setQueueCapacity(200);
|
||||
executor.setKeepAliveSeconds(60);
|
||||
executor.setThreadNamePrefix("ai-task-");
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步异常处理器
|
||||
*/
|
||||
@Override
|
||||
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||
return new org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler();
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.emotionmuseum.config;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* Coze配置类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "emotion.coze")
|
||||
@Data
|
||||
@Slf4j
|
||||
public class CozeConfig {
|
||||
|
||||
/**
|
||||
* API密钥
|
||||
*/
|
||||
private String apiKey;
|
||||
|
||||
/**
|
||||
* 机器人ID
|
||||
*/
|
||||
private String botId;
|
||||
|
||||
/**
|
||||
* 基础URL
|
||||
*/
|
||||
private String baseUrl = "https://www.coze.cn/api";
|
||||
|
||||
/**
|
||||
* 超时时间(毫秒)
|
||||
*/
|
||||
private int timeout = 30000;
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
private int maxRetries = 3;
|
||||
|
||||
/**
|
||||
* 验证配置
|
||||
*/
|
||||
@PostConstruct
|
||||
public void validateConfig() {
|
||||
if (!StringUtils.hasText(apiKey)) {
|
||||
log.warn("Coze API Key未配置,AI功能可能无法正常使用");
|
||||
}
|
||||
if (!StringUtils.hasText(botId)) {
|
||||
log.warn("Coze Bot ID未配置,AI功能可能无法正常使用");
|
||||
}
|
||||
if (StringUtils.hasText(apiKey) && StringUtils.hasText(botId)) {
|
||||
log.info("Coze配置验证通过,Bot ID: {}", botId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.emotionmuseum.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* MyBatis-Plus配置类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* MyBatis-Plus拦截器配置
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
|
||||
// 分页插件
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
|
||||
// 乐观锁插件
|
||||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||||
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局配置
|
||||
*/
|
||||
@Bean
|
||||
public GlobalConfig globalConfig() {
|
||||
GlobalConfig globalConfig = new GlobalConfig();
|
||||
|
||||
// 设置数据库类型
|
||||
globalConfig.setDbConfig(new GlobalConfig.DbConfig());
|
||||
globalConfig.getDbConfig().setIdType(com.baomidou.mybatisplus.annotation.IdType.ASSIGN_ID);
|
||||
|
||||
// 设置逻辑删除
|
||||
globalConfig.getDbConfig().setLogicDeleteField("deleted");
|
||||
globalConfig.getDbConfig().setLogicDeleteValue("1");
|
||||
globalConfig.getDbConfig().setLogicNotDeleteValue("0");
|
||||
|
||||
return globalConfig;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.emotionmuseum.config;
|
||||
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* OpenAPI配置类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Configuration
|
||||
public class OpenApiConfig {
|
||||
|
||||
/**
|
||||
* OpenAPI配置
|
||||
*/
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("情绪博物馆API文档")
|
||||
.version("1.0.0")
|
||||
.description("情绪博物馆后端服务API文档")
|
||||
.contact(new Contact()
|
||||
.name("情绪博物馆团队")
|
||||
.email("support@emotion-museum.com")))
|
||||
.addSecurityItem(new SecurityRequirement().addList("Bearer Authentication"))
|
||||
.components(new Components()
|
||||
.addSecuritySchemes("Bearer Authentication",
|
||||
new SecurityScheme()
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.scheme("bearer")
|
||||
.bearerFormat("JWT")));
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.emotionmuseum.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
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.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Redis配置类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class RedisConfig {
|
||||
|
||||
/**
|
||||
* Redis模板配置
|
||||
*/
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(factory);
|
||||
|
||||
// 使用Jackson2JsonRedisSerializer
|
||||
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
|
||||
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
|
||||
mapper.registerModule(new JavaTimeModule());
|
||||
serializer.setObjectMapper(mapper);
|
||||
|
||||
// 设置序列化器
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(serializer);
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
template.setHashValueSerializer(serializer);
|
||||
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存管理器配置
|
||||
*/
|
||||
@Bean
|
||||
public CacheManager cacheManager(RedisConnectionFactory factory) {
|
||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
||||
.entryTtl(Duration.ofMinutes(30))
|
||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
|
||||
|
||||
return RedisCacheManager.builder(factory)
|
||||
.cacheDefaults(config)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package com.emotionmuseum.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Spring Security配置类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity(prePostEnabled = true)
|
||||
public class SecurityConfig {
|
||||
|
||||
/**
|
||||
* 安全过滤器链配置
|
||||
*/
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// 禁用CSRF
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
// 配置CORS
|
||||
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
||||
// 会话管理
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
// 授权配置
|
||||
.authorizeHttpRequests(authz -> authz
|
||||
// 允许访问的路径
|
||||
.requestMatchers("/health/**").permitAll()
|
||||
.requestMatchers("/auth/**").permitAll()
|
||||
.requestMatchers("/actuator/**").permitAll()
|
||||
.requestMatchers("/ws/**").permitAll()
|
||||
.requestMatchers("/ai/guest/**").permitAll()
|
||||
.requestMatchers("/swagger-ui/**").permitAll()
|
||||
.requestMatchers("/v3/api-docs/**").permitAll()
|
||||
.requestMatchers("/swagger-ui.html").permitAll()
|
||||
.requestMatchers("/favicon.ico").permitAll()
|
||||
// 其他请求需要认证
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* CORS配置
|
||||
*/
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
// 允许的源
|
||||
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
|
||||
// 允许的方法
|
||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||||
// 允许的头部
|
||||
configuration.setAllowedHeaders(Arrays.asList("*"));
|
||||
// 允许携带凭证
|
||||
configuration.setAllowCredentials(true);
|
||||
// 预检请求的有效期
|
||||
configuration.setMaxAge(3600L);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码编码器
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder(12);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.emotionmuseum.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* HTTP客户端配置类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class WebClientConfig {
|
||||
|
||||
/**
|
||||
* WebClient构建器
|
||||
*/
|
||||
@Bean
|
||||
public WebClient.Builder webClientBuilder() {
|
||||
return WebClient.builder()
|
||||
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024))
|
||||
.filter(ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
|
||||
log.debug("Request: {} {}", clientRequest.method(), clientRequest.url());
|
||||
return Mono.just(clientRequest);
|
||||
}))
|
||||
.filter(ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
|
||||
log.debug("Response: {}", clientResponse.statusCode());
|
||||
return Mono.just(clientResponse);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* RestTemplate配置
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
// 设置超时时间
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setConnectTimeout(30000);
|
||||
factory.setReadTimeout(30000);
|
||||
restTemplate.setRequestFactory(factory);
|
||||
|
||||
// 添加请求拦截器
|
||||
restTemplate.getInterceptors().add(new ClientHttpRequestInterceptor() {
|
||||
@Override
|
||||
public org.springframework.http.client.ClientHttpResponse intercept(
|
||||
org.springframework.http.HttpRequest request,
|
||||
byte[] body,
|
||||
org.springframework.http.client.ClientHttpRequestExecution execution) throws IOException {
|
||||
log.debug("Request: {} {}", request.getMethod(), request.getURI());
|
||||
org.springframework.http.client.ClientHttpResponse response = execution.execute(request, body);
|
||||
log.debug("Response: {}", response.getStatusCode());
|
||||
return response;
|
||||
}
|
||||
});
|
||||
|
||||
return restTemplate;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.emotionmuseum.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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 emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
// 注册STOMP端点
|
||||
registry.addEndpoint("/ws")
|
||||
.setAllowedOriginPatterns("*")
|
||||
.withSockJS();
|
||||
|
||||
// 支持原生WebSocket
|
||||
registry.addEndpoint("/ws")
|
||||
.setAllowedOriginPatterns("*");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
// 启用简单的消息代理
|
||||
registry.enableSimpleBroker("/topic", "/queue");
|
||||
|
||||
// 设置应用程序目标前缀
|
||||
registry.setApplicationDestinationPrefixes("/app");
|
||||
|
||||
// 设置用户目标前缀
|
||||
registry.setUserDestinationPrefix("/user");
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
package com.emotionmuseum.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.entity.Conversation;
|
||||
import com.emotionmuseum.entity.Message;
|
||||
import com.emotionmuseum.service.AiChatService;
|
||||
import com.emotionmuseum.service.AuthService;
|
||||
import com.emotionmuseum.service.CozeApiService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* AI聊天控制器
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ai")
|
||||
@Tag(name = "AI聊天", description = "AI聊天相关接口")
|
||||
@Slf4j
|
||||
public class AiChatController {
|
||||
|
||||
@Autowired
|
||||
private AiChatService aiChatService;
|
||||
|
||||
@Autowired
|
||||
private CozeApiService cozeApiService;
|
||||
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
@PostMapping("/chat/send")
|
||||
@Operation(summary = "发送消息", description = "发送消息并获取AI回复")
|
||||
public Result<Message> sendMessage(HttpServletRequest request,
|
||||
@RequestParam String content,
|
||||
@RequestParam(required = false) String conversationId) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
log.info("用户发送消息: {}, 会话: {}", userId, conversationId);
|
||||
return aiChatService.sendMessage(userId, content, conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新会话
|
||||
*/
|
||||
@PostMapping("/conversation/create")
|
||||
@Operation(summary = "创建会话", description = "创建新的聊天会话")
|
||||
public Result<Conversation> createConversation(HttpServletRequest request,
|
||||
@RequestParam(required = false) String title) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
log.info("用户创建会话: {}, 标题: {}", userId, title);
|
||||
return aiChatService.createConversation(userId, title);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户会话列表
|
||||
*/
|
||||
@GetMapping("/conversation/list")
|
||||
@Operation(summary = "获取会话列表", description = "获取当前用户的会话列表")
|
||||
public Result<IPage<Conversation>> getUserConversations(HttpServletRequest request,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
return aiChatService.getUserConversations(userId, page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话消息列表
|
||||
*/
|
||||
@GetMapping("/conversation/{conversationId}/messages")
|
||||
@Operation(summary = "获取会话消息", description = "获取指定会话的消息列表")
|
||||
public Result<IPage<Message>> getConversationMessages(@PathVariable String conversationId,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "20") int size) {
|
||||
return aiChatService.getConversationMessages(conversationId, page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除会话
|
||||
*/
|
||||
@DeleteMapping("/conversation/{conversationId}")
|
||||
@Operation(summary = "删除会话", description = "删除指定的聊天会话")
|
||||
public Result<String> deleteConversation(HttpServletRequest request,
|
||||
@PathVariable String conversationId) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
log.info("用户删除会话: {}, 会话: {}", userId, conversationId);
|
||||
return aiChatService.deleteConversation(userId, conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话详情
|
||||
*/
|
||||
@GetMapping("/conversation/{conversationId}")
|
||||
@Operation(summary = "获取会话详情", description = "获取指定会话的详细信息")
|
||||
public Result<Conversation> getConversationById(@PathVariable String conversationId) {
|
||||
return aiChatService.getConversationById(conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空会话消息
|
||||
*/
|
||||
@PostMapping("/conversation/{conversationId}/clear")
|
||||
@Operation(summary = "清空会话", description = "清空指定会话的所有消息")
|
||||
public Result<String> clearConversation(HttpServletRequest request,
|
||||
@PathVariable String conversationId) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
log.info("用户清空会话: {}, 会话: {}", userId, conversationId);
|
||||
return aiChatService.clearConversation(userId, conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查AI服务状态
|
||||
*/
|
||||
@GetMapping("/status")
|
||||
@Operation(summary = "检查AI状态", description = "检查AI服务是否正常运行")
|
||||
public Result<Boolean> checkAiStatus() {
|
||||
return cozeApiService.checkConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Bot信息
|
||||
*/
|
||||
@GetMapping("/bot/info")
|
||||
@Operation(summary = "获取Bot信息", description = "获取AI机器人的详细信息")
|
||||
public Result<String> getBotInfo() {
|
||||
return cozeApiService.getBotInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中提取令牌
|
||||
*/
|
||||
private String extractToken(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package com.emotionmuseum.controller;
|
||||
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.dto.auth.LoginRequest;
|
||||
import com.emotionmuseum.dto.auth.LoginResponse;
|
||||
import com.emotionmuseum.dto.auth.RegisterRequest;
|
||||
import com.emotionmuseum.service.AuthService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 认证控制器
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
@Tag(name = "认证管理", description = "用户认证相关接口")
|
||||
@Slf4j
|
||||
public class AuthController {
|
||||
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "用户登录", description = "用户登录接口")
|
||||
public Result<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
|
||||
log.info("用户登录请求: {}", request.getUsername());
|
||||
return authService.login(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
@Operation(summary = "用户注册", description = "用户注册接口")
|
||||
public Result<String> register(@Valid @RequestBody RegisterRequest request) {
|
||||
log.info("用户注册请求: {}", request.getUsername());
|
||||
return authService.register(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
@Operation(summary = "用户登出", description = "用户登出接口")
|
||||
public Result<String> logout(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
log.info("用户登出请求");
|
||||
return authService.logout(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
@PostMapping("/refresh")
|
||||
@Operation(summary = "刷新令牌", description = "刷新访问令牌")
|
||||
public Result<String> refreshToken(@RequestParam String refreshToken) {
|
||||
log.info("刷新令牌请求");
|
||||
return authService.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证令牌
|
||||
*/
|
||||
@GetMapping("/validate")
|
||||
@Operation(summary = "验证令牌", description = "验证访问令牌是否有效")
|
||||
public Result<Boolean> validateToken(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
boolean isValid = authService.validateToken(token);
|
||||
return Result.success("令牌验证完成", isValid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中提取令牌
|
||||
*/
|
||||
private String extractToken(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
package com.emotionmuseum.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.dto.diary.DiaryPostRequest;
|
||||
import com.emotionmuseum.entity.DiaryPost;
|
||||
import com.emotionmuseum.service.AuthService;
|
||||
import com.emotionmuseum.service.DiaryPostService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 日记控制器
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/diary")
|
||||
@Tag(name = "日记管理", description = "日记相关接口")
|
||||
@Slf4j
|
||||
public class DiaryPostController {
|
||||
|
||||
@Autowired
|
||||
private DiaryPostService diaryPostService;
|
||||
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
/**
|
||||
* 创建日记
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建日记", description = "创建新的日记")
|
||||
public Result<DiaryPost> createDiary(HttpServletRequest request, @Valid @RequestBody DiaryPostRequest diaryRequest) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
log.info("用户创建日记: {}", userId);
|
||||
return diaryPostService.createDiary(userId, diaryRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新日记
|
||||
*/
|
||||
@PutMapping("/{diaryId}")
|
||||
@Operation(summary = "更新日记", description = "更新指定的日记")
|
||||
public Result<String> updateDiary(HttpServletRequest request,
|
||||
@PathVariable String diaryId,
|
||||
@Valid @RequestBody DiaryPostRequest diaryRequest) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
log.info("用户更新日记: {}, 日记: {}", userId, diaryId);
|
||||
return diaryPostService.updateDiary(userId, diaryId, diaryRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日记详情
|
||||
*/
|
||||
@GetMapping("/{diaryId}")
|
||||
@Operation(summary = "获取日记详情", description = "获取指定日记的详细信息")
|
||||
public Result<DiaryPost> getDiaryById(@PathVariable String diaryId) {
|
||||
return diaryPostService.getDiaryById(diaryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户日记列表
|
||||
*/
|
||||
@GetMapping("/user/list")
|
||||
@Operation(summary = "获取用户日记列表", description = "获取当前用户的日记列表")
|
||||
public Result<IPage<DiaryPost>> getUserDiaries(HttpServletRequest request,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
return diaryPostService.getUserDiaries(userId, page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公开日记列表
|
||||
*/
|
||||
@GetMapping("/public/list")
|
||||
@Operation(summary = "获取公开日记列表", description = "获取所有公开的日记列表")
|
||||
public Result<IPage<DiaryPost>> getPublicDiaries(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
return diaryPostService.getPublicDiaries(page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据情绪标签查询日记
|
||||
*/
|
||||
@GetMapping("/emotion/{emotionTag}")
|
||||
@Operation(summary = "根据情绪标签查询日记", description = "根据情绪标签查询公开日记")
|
||||
public Result<IPage<DiaryPost>> getDiariesByEmotionTag(@PathVariable String emotionTag,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
return diaryPostService.getDiariesByEmotionTag(emotionTag, page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除日记
|
||||
*/
|
||||
@DeleteMapping("/{diaryId}")
|
||||
@Operation(summary = "删除日记", description = "删除指定的日记")
|
||||
public Result<String> deleteDiary(HttpServletRequest request, @PathVariable String diaryId) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
log.info("用户删除日记: {}, 日记: {}", userId, diaryId);
|
||||
return diaryPostService.deleteDiary(userId, diaryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞日记
|
||||
*/
|
||||
@PostMapping("/{diaryId}/like")
|
||||
@Operation(summary = "点赞日记", description = "对指定日记进行点赞")
|
||||
public Result<String> likeDiary(HttpServletRequest request, @PathVariable String diaryId) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
log.info("用户点赞日记: {}, 日记: {}", userId, diaryId);
|
||||
return diaryPostService.likeDiary(userId, diaryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消点赞
|
||||
*/
|
||||
@PostMapping("/{diaryId}/unlike")
|
||||
@Operation(summary = "取消点赞", description = "取消对指定日记的点赞")
|
||||
public Result<String> unlikeDiary(HttpServletRequest request, @PathVariable String diaryId) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
log.info("用户取消点赞: {}, 日记: {}", userId, diaryId);
|
||||
return diaryPostService.unlikeDiary(userId, diaryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AI点评
|
||||
*/
|
||||
@GetMapping("/{diaryId}/ai-comment")
|
||||
@Operation(summary = "获取AI点评", description = "获取指定日记的AI点评")
|
||||
public Result<String> getAiComment(HttpServletRequest request, @PathVariable String diaryId) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
|
||||
return diaryPostService.getAiComment(userId, diaryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中提取令牌
|
||||
*/
|
||||
private String extractToken(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.emotionmuseum.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 健康检查控制器
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/health")
|
||||
@Slf4j
|
||||
public class HealthController {
|
||||
|
||||
/**
|
||||
* 健康检查接口
|
||||
*/
|
||||
@GetMapping
|
||||
public Map<String, Object> health() {
|
||||
log.info("健康检查请求");
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("status", "UP");
|
||||
result.put("timestamp", LocalDateTime.now());
|
||||
result.put("service", "emotion-museum-backend");
|
||||
result.put("version", "1.0.0");
|
||||
result.put("message", "系统运行正常");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统信息接口
|
||||
*/
|
||||
@GetMapping("/info")
|
||||
public Map<String, Object> info() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("name", "情绪博物馆后端服务");
|
||||
result.put("version", "1.0.0");
|
||||
result.put("description", "基于Spring Boot 3.4.8的情绪博物馆后端服务");
|
||||
result.put("javaVersion", System.getProperty("java.version"));
|
||||
result.put("startTime", LocalDateTime.now());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package com.emotionmuseum.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.entity.User;
|
||||
import com.emotionmuseum.service.AuthService;
|
||||
import com.emotionmuseum.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 用户控制器
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
@Tag(name = "用户管理", description = "用户相关接口")
|
||||
@Slf4j
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
@GetMapping("/profile")
|
||||
@Operation(summary = "获取用户信息", description = "获取当前登录用户的详细信息")
|
||||
public Result<User> getProfile(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
return userService.getUserById(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*/
|
||||
@PutMapping("/profile")
|
||||
@Operation(summary = "更新用户信息", description = "更新当前登录用户的信息")
|
||||
public Result<String> updateProfile(HttpServletRequest request, @Valid @RequestBody User user) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
return userService.updateUser(userId, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
@PostMapping("/change-password")
|
||||
@Operation(summary = "修改密码", description = "修改当前登录用户的密码")
|
||||
public Result<String> changePassword(HttpServletRequest request,
|
||||
@RequestParam String oldPassword,
|
||||
@RequestParam String newPassword) {
|
||||
String token = extractToken(request);
|
||||
String userId = authService.getUserIdFromToken(token);
|
||||
if (userId == null) {
|
||||
return Result.unauthorized();
|
||||
}
|
||||
return userService.changePassword(userId, oldPassword, newPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表(管理员功能)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取用户列表", description = "分页获取用户列表(管理员功能)")
|
||||
public Result<IPage<User>> getUserList(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
return userService.getUserList(page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取用户信息(管理员功能)
|
||||
*/
|
||||
@GetMapping("/{userId}")
|
||||
@Operation(summary = "获取指定用户信息", description = "根据用户ID获取用户信息(管理员功能)")
|
||||
public Result<User> getUserById(@PathVariable String userId) {
|
||||
return userService.getUserById(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户(管理员功能)
|
||||
*/
|
||||
@DeleteMapping("/{userId}")
|
||||
@Operation(summary = "删除用户", description = "删除指定用户(管理员功能)")
|
||||
public Result<String> deleteUser(@PathVariable String userId) {
|
||||
return userService.deleteUser(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用用户(管理员功能)
|
||||
*/
|
||||
@PostMapping("/{userId}/status")
|
||||
@Operation(summary = "更新用户状态", description = "启用或禁用用户(管理员功能)")
|
||||
public Result<String> toggleUserStatus(@PathVariable String userId, @RequestParam Integer status) {
|
||||
return userService.toggleUserStatus(userId, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中提取令牌
|
||||
*/
|
||||
private String extractToken(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package com.emotionmuseum.controller;
|
||||
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.dto.websocket.ChatMessage;
|
||||
import com.emotionmuseum.service.AiChatService;
|
||||
import com.emotionmuseum.service.AuthService;
|
||||
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.handler.annotation.SendTo;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
/**
|
||||
* WebSocket控制器
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Controller
|
||||
@Slf4j
|
||||
public class WebSocketController {
|
||||
|
||||
@Autowired
|
||||
private SimpMessagingTemplate messagingTemplate;
|
||||
|
||||
@Autowired
|
||||
private AiChatService aiChatService;
|
||||
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
/**
|
||||
* 处理聊天消息
|
||||
*/
|
||||
@MessageMapping("/chat.sendMessage")
|
||||
@SendTo("/topic/public")
|
||||
public ChatMessage sendMessage(@Payload ChatMessage chatMessage) {
|
||||
log.info("收到WebSocket消息: {}", chatMessage.getContent());
|
||||
return chatMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户加入聊天
|
||||
*/
|
||||
@MessageMapping("/chat.addUser")
|
||||
@SendTo("/topic/public")
|
||||
public ChatMessage addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) {
|
||||
// 添加用户名到WebSocket会话
|
||||
headerAccessor.getSessionAttributes().put("username", chatMessage.getSenderId());
|
||||
log.info("用户加入聊天: {}", chatMessage.getSenderId());
|
||||
return chatMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理AI聊天消息
|
||||
*/
|
||||
@MessageMapping("/ai.chat")
|
||||
public void handleAiChat(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) {
|
||||
try {
|
||||
String userId = chatMessage.getSenderId();
|
||||
String content = chatMessage.getContent();
|
||||
String conversationId = chatMessage.getConversationId();
|
||||
|
||||
log.info("处理AI聊天消息: 用户={}, 内容={}, 会话={}", userId, content, conversationId);
|
||||
|
||||
// 调用AI服务获取回复
|
||||
var result = aiChatService.sendMessage(userId, content, conversationId);
|
||||
|
||||
if (result.getCode() == 200) {
|
||||
// 发送AI回复
|
||||
ChatMessage aiResponse = new ChatMessage();
|
||||
aiResponse.setType("CHAT");
|
||||
aiResponse.setConversationId(conversationId);
|
||||
aiResponse.setSenderId("AI");
|
||||
aiResponse.setSenderType("AI");
|
||||
aiResponse.setContent(result.getData().getContent());
|
||||
aiResponse.setMessageType("TEXT");
|
||||
|
||||
// 发送给特定用户
|
||||
messagingTemplate.convertAndSendToUser(
|
||||
userId,
|
||||
"/queue/ai.response",
|
||||
aiResponse
|
||||
);
|
||||
|
||||
log.info("AI回复发送成功: {}", aiResponse.getContent());
|
||||
} else {
|
||||
// 发送错误消息
|
||||
ChatMessage errorResponse = new ChatMessage();
|
||||
errorResponse.setType("ERROR");
|
||||
errorResponse.setConversationId(conversationId);
|
||||
errorResponse.setSenderId("SYSTEM");
|
||||
errorResponse.setSenderType("SYSTEM");
|
||||
errorResponse.setContent("抱歉,AI暂时无法回复,请稍后再试。");
|
||||
errorResponse.setMessageType("TEXT");
|
||||
|
||||
messagingTemplate.convertAndSendToUser(
|
||||
userId,
|
||||
"/queue/ai.response",
|
||||
errorResponse
|
||||
);
|
||||
|
||||
log.error("AI回复失败: {}", result.getMessage());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理AI聊天消息时发生错误: {}", e.getMessage(), e);
|
||||
|
||||
// 发送错误消息
|
||||
ChatMessage errorResponse = new ChatMessage();
|
||||
errorResponse.setType("ERROR");
|
||||
errorResponse.setConversationId(chatMessage.getConversationId());
|
||||
errorResponse.setSenderId("SYSTEM");
|
||||
errorResponse.setSenderType("SYSTEM");
|
||||
errorResponse.setContent("系统错误,请稍后再试。");
|
||||
errorResponse.setMessageType("TEXT");
|
||||
|
||||
messagingTemplate.convertAndSendToUser(
|
||||
chatMessage.getSenderId(),
|
||||
"/queue/ai.response",
|
||||
errorResponse
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户输入状态
|
||||
*/
|
||||
@MessageMapping("/chat.typing")
|
||||
@SendTo("/topic/public")
|
||||
public ChatMessage handleTyping(@Payload ChatMessage chatMessage) {
|
||||
log.info("用户正在输入: {}", chatMessage.getSenderId());
|
||||
return chatMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package com.emotionmuseum.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 通用响应结果
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> {
|
||||
|
||||
/**
|
||||
* 响应码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 响应消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
public Result() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Result(Integer code, String message) {
|
||||
this();
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Result(Integer code, String message, T data) {
|
||||
this(code, message);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应
|
||||
*/
|
||||
public static <T> Result<T> success() {
|
||||
return new Result<>(200, "操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应(带数据)
|
||||
*/
|
||||
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, "操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应(自定义消息)
|
||||
*/
|
||||
public static <T> Result<T> error(String message) {
|
||||
return new Result<>(500, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应(自定义码和消息)
|
||||
*/
|
||||
public static <T> Result<T> error(Integer code, String message) {
|
||||
return new Result<>(code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 未授权响应
|
||||
*/
|
||||
public static <T> Result<T> unauthorized() {
|
||||
return new Result<>(401, "未授权访问");
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止访问响应
|
||||
*/
|
||||
public static <T> Result<T> forbidden() {
|
||||
return new Result<>(403, "禁止访问");
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源不存在响应
|
||||
*/
|
||||
public static <T> Result<T> notFound() {
|
||||
return new Result<>(404, "资源不存在");
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.emotionmuseum.dto.auth;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录请求DTO
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class LoginRequest {
|
||||
|
||||
/**
|
||||
* 用户名或邮箱
|
||||
*/
|
||||
@NotBlank(message = "用户名或邮箱不能为空")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String captcha;
|
||||
|
||||
/**
|
||||
* 验证码ID
|
||||
*/
|
||||
private String captchaId;
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package com.emotionmuseum.dto.auth;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录响应DTO
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class LoginResponse {
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
private String accessToken;
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
private String refreshToken;
|
||||
|
||||
/**
|
||||
* 令牌类型
|
||||
*/
|
||||
private String tokenType = "Bearer";
|
||||
|
||||
/**
|
||||
* 过期时间(秒)
|
||||
*/
|
||||
private Long expiresIn;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private UserInfo userInfo;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
@Data
|
||||
public static class UserInfo {
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.emotionmuseum.dto.auth;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 注册请求DTO
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class RegisterRequest {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 20, message = "用户名长度必须在3-20个字符之间")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 确认密码
|
||||
*/
|
||||
@NotBlank(message = "确认密码不能为空")
|
||||
private String confirmPassword;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@Size(max = 50, message = "昵称长度不能超过50个字符")
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String captcha;
|
||||
|
||||
/**
|
||||
* 验证码ID
|
||||
*/
|
||||
private String captchaId;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.emotionmuseum.dto.comment;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 评论请求DTO
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class CommentRequest {
|
||||
|
||||
/**
|
||||
* 父评论ID (用于回复功能)
|
||||
*/
|
||||
private String parentId;
|
||||
|
||||
/**
|
||||
* 内容类型 (DIARY, POST)
|
||||
*/
|
||||
@NotBlank(message = "内容类型不能为空")
|
||||
private String contentType;
|
||||
|
||||
/**
|
||||
* 内容ID
|
||||
*/
|
||||
@NotBlank(message = "内容ID不能为空")
|
||||
private String contentId;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
@NotBlank(message = "评论内容不能为空")
|
||||
@Size(max = 1000, message = "评论内容长度不能超过1000个字符")
|
||||
private String content;
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package com.emotionmuseum.dto.comment;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论响应DTO
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class CommentResponse {
|
||||
|
||||
/**
|
||||
* 评论ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 父评论ID
|
||||
*/
|
||||
private String parentId;
|
||||
|
||||
/**
|
||||
* 内容类型
|
||||
*/
|
||||
private String contentType;
|
||||
|
||||
/**
|
||||
* 内容ID
|
||||
*/
|
||||
private String contentId;
|
||||
|
||||
/**
|
||||
* 评论者ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 评论者昵称
|
||||
*/
|
||||
private String userNickname;
|
||||
|
||||
/**
|
||||
* 评论者头像
|
||||
*/
|
||||
private String userAvatar;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 点赞数
|
||||
*/
|
||||
private Integer likeCount;
|
||||
|
||||
/**
|
||||
* 回复数
|
||||
*/
|
||||
private Integer replyCount;
|
||||
|
||||
/**
|
||||
* 是否已点赞
|
||||
*/
|
||||
private Boolean isLiked;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 子评论列表
|
||||
*/
|
||||
private List<CommentResponse> replies;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.emotionmuseum.dto.diary;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 日记请求DTO
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class DiaryPostRequest {
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
@NotBlank(message = "标题不能为空")
|
||||
@Size(max = 100, message = "标题长度不能超过100个字符")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
@NotBlank(message = "内容不能为空")
|
||||
@Size(max = 10000, message = "内容长度不能超过10000个字符")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 情绪标签
|
||||
*/
|
||||
private String emotionTags;
|
||||
|
||||
/**
|
||||
* 情绪评分 (1-10)
|
||||
*/
|
||||
private Integer emotionScore;
|
||||
|
||||
/**
|
||||
* 天气
|
||||
*/
|
||||
private String weather;
|
||||
|
||||
/**
|
||||
* 位置
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 图片URL列表 (JSON格式)
|
||||
*/
|
||||
private String images;
|
||||
|
||||
/**
|
||||
* 是否公开 (0:私密, 1:公开)
|
||||
*/
|
||||
private Integer isPublic = 0;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package com.emotionmuseum.dto.websocket;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* WebSocket聊天消息DTO
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class ChatMessage {
|
||||
|
||||
/**
|
||||
* 消息类型 (CHAT, JOIN, LEAVE, TYPING)
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 发送者ID
|
||||
*/
|
||||
private String senderId;
|
||||
|
||||
/**
|
||||
* 发送者类型 (USER, AI)
|
||||
*/
|
||||
private String senderType;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息类型 (TEXT, IMAGE, FILE)
|
||||
*/
|
||||
private String messageType;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
public ChatMessage() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public ChatMessage(String type, String conversationId, String senderId, String senderType, String content) {
|
||||
this();
|
||||
this.type = type;
|
||||
this.conversationId = conversationId;
|
||||
this.senderId = senderId;
|
||||
this.senderType = senderType;
|
||||
this.content = content;
|
||||
this.messageType = "TEXT";
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package com.emotionmuseum.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 评论实体类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("comment")
|
||||
public class Comment {
|
||||
|
||||
/**
|
||||
* 评论ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 父评论ID (用于回复功能)
|
||||
*/
|
||||
private String parentId;
|
||||
|
||||
/**
|
||||
* 内容类型 (DIARY, POST)
|
||||
*/
|
||||
private String contentType;
|
||||
|
||||
/**
|
||||
* 内容ID
|
||||
*/
|
||||
private String contentId;
|
||||
|
||||
/**
|
||||
* 评论者ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 点赞数
|
||||
*/
|
||||
private Integer likeCount;
|
||||
|
||||
/**
|
||||
* 回复数
|
||||
*/
|
||||
private Integer replyCount;
|
||||
|
||||
/**
|
||||
* 状态 (0:待审核, 1:正常, 2:已删除)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除 (0:未删除, 1:已删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.emotionmuseum.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 会话实体类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("conversation")
|
||||
public class Conversation {
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 会话标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 会话类型 (CHAT, SUMMARY)
|
||||
*/
|
||||
private String conversationType;
|
||||
|
||||
/**
|
||||
* 消息数量
|
||||
*/
|
||||
private Integer messageCount;
|
||||
|
||||
/**
|
||||
* 最后消息时间
|
||||
*/
|
||||
private LocalDateTime lastMessageTime;
|
||||
|
||||
/**
|
||||
* 会话状态 (0:进行中, 1:已结束, 2:已删除)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除 (0:未删除, 1:已删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package com.emotionmuseum.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 日记实体类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("diary_post")
|
||||
public class DiaryPost {
|
||||
|
||||
/**
|
||||
* 日记ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 情绪标签
|
||||
*/
|
||||
private String emotionTags;
|
||||
|
||||
/**
|
||||
* 情绪评分 (1-10)
|
||||
*/
|
||||
private Integer emotionScore;
|
||||
|
||||
/**
|
||||
* 天气
|
||||
*/
|
||||
private String weather;
|
||||
|
||||
/**
|
||||
* 位置
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 图片URL列表 (JSON格式)
|
||||
*/
|
||||
private String images;
|
||||
|
||||
/**
|
||||
* 是否公开 (0:私密, 1:公开)
|
||||
*/
|
||||
private Integer isPublic;
|
||||
|
||||
/**
|
||||
* 点赞数
|
||||
*/
|
||||
private Integer likeCount;
|
||||
|
||||
/**
|
||||
* 评论数
|
||||
*/
|
||||
private Integer commentCount;
|
||||
|
||||
/**
|
||||
* 分享数
|
||||
*/
|
||||
private Integer shareCount;
|
||||
|
||||
/**
|
||||
* AI点评
|
||||
*/
|
||||
private String aiComment;
|
||||
|
||||
/**
|
||||
* 状态 (0:草稿, 1:已发布, 2:已删除)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除 (0:未删除, 1:已删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.emotionmuseum.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 消息实体类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("message")
|
||||
public class Message {
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 发送者ID
|
||||
*/
|
||||
private String senderId;
|
||||
|
||||
/**
|
||||
* 发送者类型 (USER, AI)
|
||||
*/
|
||||
private String senderType;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息类型 (TEXT, IMAGE, FILE)
|
||||
*/
|
||||
private String messageType;
|
||||
|
||||
/**
|
||||
* 消息状态 (0:未读, 1:已读, 2:已发送, 3:发送失败)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除 (0:未删除, 1:已删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package com.emotionmuseum.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户实体类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("user")
|
||||
public class User {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 性别 (0:未知, 1:男, 2:女)
|
||||
*/
|
||||
private Integer gender;
|
||||
|
||||
/**
|
||||
* 生日
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
|
||||
/**
|
||||
* 个人简介
|
||||
*/
|
||||
private String bio;
|
||||
|
||||
/**
|
||||
* 状态 (0:禁用, 1:正常)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 用户类型 (0:普通用户, 1:VIP用户, 2:管理员)
|
||||
*/
|
||||
private Integer userType;
|
||||
|
||||
/**
|
||||
* 最后登录时间
|
||||
*/
|
||||
private LocalDateTime lastLoginTime;
|
||||
|
||||
/**
|
||||
* 最后登录IP
|
||||
*/
|
||||
private String lastLoginIp;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除 (0:未删除, 1:已删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.emotionmuseum.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户关注实体类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("user_follow")
|
||||
public class UserFollow {
|
||||
|
||||
/**
|
||||
* 关注ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 关注者ID
|
||||
*/
|
||||
private String followerId;
|
||||
|
||||
/**
|
||||
* 被关注者ID
|
||||
*/
|
||||
private String followingId;
|
||||
|
||||
/**
|
||||
* 关注状态 (0:取消关注, 1:已关注)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除 (0:未删除, 1:已删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.emotionmuseum.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.emotionmuseum.entity.Comment;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论Mapper接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Mapper
|
||||
public interface CommentMapper extends BaseMapper<Comment> {
|
||||
|
||||
/**
|
||||
* 分页查询内容的评论
|
||||
*/
|
||||
@Select("SELECT * FROM comment WHERE content_type = #{contentType} AND content_id = #{contentId} AND parent_id IS NULL AND deleted = 0 ORDER BY create_time DESC")
|
||||
IPage<Comment> selectContentComments(Page<Comment> page, @Param("contentType") String contentType, @Param("contentId") String contentId);
|
||||
|
||||
/**
|
||||
* 查询评论的回复
|
||||
*/
|
||||
@Select("SELECT * FROM comment WHERE parent_id = #{parentId} AND deleted = 0 ORDER BY create_time ASC")
|
||||
List<Comment> selectCommentReplies(@Param("parentId") String parentId);
|
||||
|
||||
/**
|
||||
* 统计内容的评论数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM comment WHERE content_type = #{contentType} AND content_id = #{contentId} AND deleted = 0")
|
||||
int countByContent(@Param("contentType") String contentType, @Param("contentId") String contentId);
|
||||
|
||||
/**
|
||||
* 统计用户的评论数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM comment WHERE user_id = #{userId} AND deleted = 0")
|
||||
int countByUserId(@Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* 查询用户的最新评论
|
||||
*/
|
||||
@Select("SELECT * FROM comment WHERE user_id = #{userId} AND deleted = 0 ORDER BY create_time DESC LIMIT #{limit}")
|
||||
List<Comment> selectUserLatestComments(@Param("userId") String userId, @Param("limit") int limit);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.emotionmuseum.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.emotionmuseum.entity.Conversation;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* 会话Mapper接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Mapper
|
||||
public interface ConversationMapper extends BaseMapper<Conversation> {
|
||||
|
||||
/**
|
||||
* 分页查询用户会话
|
||||
*/
|
||||
@Select("SELECT * FROM conversation WHERE user_id = #{userId} AND deleted = 0 ORDER BY last_message_time DESC")
|
||||
IPage<Conversation> selectUserConversations(Page<Conversation> page, @Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* 统计用户会话数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM conversation WHERE user_id = #{userId} AND deleted = 0")
|
||||
int countByUserId(@Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* 查询用户最新的会话
|
||||
*/
|
||||
@Select("SELECT * FROM conversation WHERE user_id = #{userId} AND deleted = 0 ORDER BY last_message_time DESC LIMIT 1")
|
||||
Conversation selectLatestConversation(@Param("userId") String userId);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.emotionmuseum.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.emotionmuseum.entity.DiaryPost;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* 日记Mapper接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Mapper
|
||||
public interface DiaryPostMapper extends BaseMapper<DiaryPost> {
|
||||
|
||||
/**
|
||||
* 分页查询用户的日记
|
||||
*/
|
||||
@Select("SELECT * FROM diary_post WHERE user_id = #{userId} AND deleted = 0 ORDER BY create_time DESC")
|
||||
IPage<DiaryPost> selectUserDiaries(Page<DiaryPost> page, @Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* 分页查询公开的日记
|
||||
*/
|
||||
@Select("SELECT * FROM diary_post WHERE is_public = 1 AND status = 1 AND deleted = 0 ORDER BY create_time DESC")
|
||||
IPage<DiaryPost> selectPublicDiaries(Page<DiaryPost> page);
|
||||
|
||||
/**
|
||||
* 根据情绪标签查询日记
|
||||
*/
|
||||
@Select("SELECT * FROM diary_post WHERE emotion_tags LIKE CONCAT('%', #{emotionTag}, '%') AND is_public = 1 AND status = 1 AND deleted = 0 ORDER BY create_time DESC")
|
||||
IPage<DiaryPost> selectDiariesByEmotionTag(Page<DiaryPost> page, @Param("emotionTag") String emotionTag);
|
||||
|
||||
/**
|
||||
* 统计用户的日记数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM diary_post WHERE user_id = #{userId} AND deleted = 0")
|
||||
int countByUserId(@Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* 统计公开日记数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM diary_post WHERE is_public = 1 AND status = 1 AND deleted = 0")
|
||||
int countPublicDiaries();
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.emotionmuseum.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.emotionmuseum.entity.Message;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息Mapper接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Mapper
|
||||
public interface MessageMapper extends BaseMapper<Message> {
|
||||
|
||||
/**
|
||||
* 分页查询会话消息
|
||||
*/
|
||||
@Select("SELECT * FROM message WHERE conversation_id = #{conversationId} AND deleted = 0 ORDER BY create_time ASC")
|
||||
IPage<Message> selectConversationMessages(Page<Message> page, @Param("conversationId") String conversationId);
|
||||
|
||||
/**
|
||||
* 查询会话的最新消息
|
||||
*/
|
||||
@Select("SELECT * FROM message WHERE conversation_id = #{conversationId} AND deleted = 0 ORDER BY create_time DESC LIMIT 1")
|
||||
Message selectLatestMessage(@Param("conversationId") String conversationId);
|
||||
|
||||
/**
|
||||
* 查询会话的所有消息
|
||||
*/
|
||||
@Select("SELECT * FROM message WHERE conversation_id = #{conversationId} AND deleted = 0 ORDER BY create_time ASC")
|
||||
List<Message> selectAllMessages(@Param("conversationId") String conversationId);
|
||||
|
||||
/**
|
||||
* 统计会话消息数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM message WHERE conversation_id = #{conversationId} AND deleted = 0")
|
||||
int countByConversationId(@Param("conversationId") String conversationId);
|
||||
|
||||
/**
|
||||
* 统计用户未读消息数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM message m JOIN conversation c ON m.conversation_id = c.id WHERE c.user_id = #{userId} AND m.sender_type = 'AI' AND m.status = 0 AND m.deleted = 0")
|
||||
int countUnreadMessages(@Param("userId") String userId);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.emotionmuseum.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.emotionmuseum.entity.UserFollow;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* 用户关注Mapper接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserFollowMapper extends BaseMapper<UserFollow> {
|
||||
|
||||
/**
|
||||
* 分页查询用户的关注列表
|
||||
*/
|
||||
@Select("SELECT * FROM user_follow WHERE follower_id = #{followerId} AND status = 1 AND deleted = 0 ORDER BY create_time DESC")
|
||||
IPage<UserFollow> selectUserFollowings(Page<UserFollow> page, @Param("followerId") String followerId);
|
||||
|
||||
/**
|
||||
* 分页查询用户的粉丝列表
|
||||
*/
|
||||
@Select("SELECT * FROM user_follow WHERE following_id = #{followingId} AND status = 1 AND deleted = 0 ORDER BY create_time DESC")
|
||||
IPage<UserFollow> selectUserFollowers(Page<UserFollow> page, @Param("followingId") String followingId);
|
||||
|
||||
/**
|
||||
* 检查是否已关注
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM user_follow WHERE follower_id = #{followerId} AND following_id = #{followingId} AND status = 1 AND deleted = 0")
|
||||
int checkIsFollowing(@Param("followerId") String followerId, @Param("followingId") String followingId);
|
||||
|
||||
/**
|
||||
* 统计用户关注数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM user_follow WHERE follower_id = #{followerId} AND status = 1 AND deleted = 0")
|
||||
int countFollowings(@Param("followerId") String followerId);
|
||||
|
||||
/**
|
||||
* 统计用户粉丝数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM user_follow WHERE following_id = #{followingId} AND status = 1 AND deleted = 0")
|
||||
int countFollowers(@Param("followingId") String followingId);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.emotionmuseum.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.emotionmuseum.entity.User;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* 用户Mapper接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserMapper extends BaseMapper<User> {
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户
|
||||
*/
|
||||
@Select("SELECT * FROM user WHERE username = #{username} AND deleted = 0")
|
||||
User findByUsername(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 根据邮箱查询用户
|
||||
*/
|
||||
@Select("SELECT * FROM user WHERE email = #{email} AND deleted = 0")
|
||||
User findByEmail(@Param("email") String email);
|
||||
|
||||
/**
|
||||
* 根据手机号查询用户
|
||||
*/
|
||||
@Select("SELECT * FROM user WHERE phone = #{phone} AND deleted = 0")
|
||||
User findByPhone(@Param("phone") String phone);
|
||||
|
||||
/**
|
||||
* 检查用户名是否存在
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM user WHERE username = #{username} AND deleted = 0")
|
||||
int countByUsername(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 检查邮箱是否存在
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM user WHERE email = #{email} AND deleted = 0")
|
||||
int countByEmail(@Param("email") String email);
|
||||
|
||||
/**
|
||||
* 检查手机号是否存在
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM user WHERE phone = #{phone} AND deleted = 0")
|
||||
int countByPhone(@Param("phone") String phone);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.emotionmuseum.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.entity.Conversation;
|
||||
import com.emotionmuseum.entity.Message;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AI聊天服务接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
public interface AiChatService {
|
||||
|
||||
/**
|
||||
* 发送消息并获取AI回复
|
||||
*/
|
||||
Result<Message> sendMessage(String userId, String content, String conversationId);
|
||||
|
||||
/**
|
||||
* 创建新会话
|
||||
*/
|
||||
Result<Conversation> createConversation(String userId, String title);
|
||||
|
||||
/**
|
||||
* 获取用户会话列表
|
||||
*/
|
||||
Result<IPage<Conversation>> getUserConversations(String userId, int page, int size);
|
||||
|
||||
/**
|
||||
* 获取会话消息列表
|
||||
*/
|
||||
Result<IPage<Message>> getConversationMessages(String conversationId, int page, int size);
|
||||
|
||||
/**
|
||||
* 删除会话
|
||||
*/
|
||||
Result<String> deleteConversation(String userId, String conversationId);
|
||||
|
||||
/**
|
||||
* 获取会话详情
|
||||
*/
|
||||
Result<Conversation> getConversationById(String conversationId);
|
||||
|
||||
/**
|
||||
* 清空会话消息
|
||||
*/
|
||||
Result<String> clearConversation(String userId, String conversationId);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package com.emotionmuseum.service;
|
||||
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.dto.auth.LoginRequest;
|
||||
import com.emotionmuseum.dto.auth.LoginResponse;
|
||||
import com.emotionmuseum.dto.auth.RegisterRequest;
|
||||
|
||||
/**
|
||||
* 认证服务接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
public interface AuthService {
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
Result<LoginResponse> login(LoginRequest request);
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*/
|
||||
Result<String> register(RegisterRequest request);
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*/
|
||||
Result<String> logout(String token);
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
Result<String> refreshToken(String refreshToken);
|
||||
|
||||
/**
|
||||
* 验证令牌
|
||||
*/
|
||||
boolean validateToken(String token);
|
||||
|
||||
/**
|
||||
* 从令牌中获取用户ID
|
||||
*/
|
||||
String getUserIdFromToken(String token);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.emotionmuseum.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.dto.comment.CommentRequest;
|
||||
import com.emotionmuseum.dto.comment.CommentResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论服务接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
public interface CommentService {
|
||||
|
||||
/**
|
||||
* 创建评论
|
||||
*/
|
||||
Result<CommentResponse> createComment(String userId, CommentRequest request);
|
||||
|
||||
/**
|
||||
* 获取内容评论列表
|
||||
*/
|
||||
Result<IPage<CommentResponse>> getContentComments(String contentType, String contentId, int page, int size);
|
||||
|
||||
/**
|
||||
* 获取评论详情
|
||||
*/
|
||||
Result<CommentResponse> getCommentById(String commentId);
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*/
|
||||
Result<String> deleteComment(String userId, String commentId);
|
||||
|
||||
/**
|
||||
* 点赞评论
|
||||
*/
|
||||
Result<String> likeComment(String userId, String commentId);
|
||||
|
||||
/**
|
||||
* 取消点赞评论
|
||||
*/
|
||||
Result<String> unlikeComment(String userId, String commentId);
|
||||
|
||||
/**
|
||||
* 获取用户评论列表
|
||||
*/
|
||||
Result<IPage<CommentResponse>> getUserComments(String userId, int page, int size);
|
||||
|
||||
/**
|
||||
* 获取评论回复列表
|
||||
*/
|
||||
Result<List<CommentResponse>> getCommentReplies(String commentId);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.emotionmuseum.service;
|
||||
|
||||
import com.emotionmuseum.dto.Result;
|
||||
|
||||
/**
|
||||
* Coze API服务接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
public interface CozeApiService {
|
||||
|
||||
/**
|
||||
* 发送消息到Coze Bot
|
||||
*/
|
||||
Result<String> sendMessage(String message, String userId);
|
||||
|
||||
/**
|
||||
* 发送消息到Coze Bot(带上下文)
|
||||
*/
|
||||
Result<String> sendMessageWithContext(String message, String userId, String conversationId);
|
||||
|
||||
/**
|
||||
* 获取Bot信息
|
||||
*/
|
||||
Result<String> getBotInfo();
|
||||
|
||||
/**
|
||||
* 检查API连接状态
|
||||
*/
|
||||
Result<Boolean> checkConnection();
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.emotionmuseum.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.dto.diary.DiaryPostRequest;
|
||||
import com.emotionmuseum.entity.DiaryPost;
|
||||
|
||||
/**
|
||||
* 日记服务接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
public interface DiaryPostService {
|
||||
|
||||
/**
|
||||
* 创建日记
|
||||
*/
|
||||
Result<DiaryPost> createDiary(String userId, DiaryPostRequest request);
|
||||
|
||||
/**
|
||||
* 更新日记
|
||||
*/
|
||||
Result<String> updateDiary(String userId, String diaryId, DiaryPostRequest request);
|
||||
|
||||
/**
|
||||
* 获取日记详情
|
||||
*/
|
||||
Result<DiaryPost> getDiaryById(String diaryId);
|
||||
|
||||
/**
|
||||
* 获取用户日记列表
|
||||
*/
|
||||
Result<IPage<DiaryPost>> getUserDiaries(String userId, int page, int size);
|
||||
|
||||
/**
|
||||
* 获取公开日记列表
|
||||
*/
|
||||
Result<IPage<DiaryPost>> getPublicDiaries(int page, int size);
|
||||
|
||||
/**
|
||||
* 根据情绪标签查询日记
|
||||
*/
|
||||
Result<IPage<DiaryPost>> getDiariesByEmotionTag(String emotionTag, int page, int size);
|
||||
|
||||
/**
|
||||
* 删除日记
|
||||
*/
|
||||
Result<String> deleteDiary(String userId, String diaryId);
|
||||
|
||||
/**
|
||||
* 点赞日记
|
||||
*/
|
||||
Result<String> likeDiary(String userId, String diaryId);
|
||||
|
||||
/**
|
||||
* 取消点赞
|
||||
*/
|
||||
Result<String> unlikeDiary(String userId, String diaryId);
|
||||
|
||||
/**
|
||||
* 获取AI点评
|
||||
*/
|
||||
Result<String> getAiComment(String userId, String diaryId);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.emotionmuseum.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.entity.UserFollow;
|
||||
|
||||
/**
|
||||
* 用户关注服务接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
public interface UserFollowService {
|
||||
|
||||
/**
|
||||
* 关注用户
|
||||
*/
|
||||
Result<String> followUser(String followerId, String followingId);
|
||||
|
||||
/**
|
||||
* 取消关注
|
||||
*/
|
||||
Result<String> unfollowUser(String followerId, String followingId);
|
||||
|
||||
/**
|
||||
* 检查是否已关注
|
||||
*/
|
||||
Result<Boolean> checkIsFollowing(String followerId, String followingId);
|
||||
|
||||
/**
|
||||
* 获取用户关注列表
|
||||
*/
|
||||
Result<IPage<UserFollow>> getUserFollowings(String userId, int page, int size);
|
||||
|
||||
/**
|
||||
* 获取用户粉丝列表
|
||||
*/
|
||||
Result<IPage<UserFollow>> getUserFollowers(String userId, int page, int size);
|
||||
|
||||
/**
|
||||
* 获取用户关注数量
|
||||
*/
|
||||
Result<Integer> getFollowingsCount(String userId);
|
||||
|
||||
/**
|
||||
* 获取用户粉丝数量
|
||||
*/
|
||||
Result<Integer> getFollowersCount(String userId);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.emotionmuseum.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.entity.User;
|
||||
|
||||
/**
|
||||
* 用户服务接口
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
public interface UserService {
|
||||
|
||||
/**
|
||||
* 根据ID获取用户信息
|
||||
*/
|
||||
Result<User> getUserById(String userId);
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*/
|
||||
Result<String> updateUser(String userId, User user);
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
Result<String> changePassword(String userId, String oldPassword, String newPassword);
|
||||
|
||||
/**
|
||||
* 分页查询用户列表
|
||||
*/
|
||||
Result<IPage<User>> getUserList(int page, int size);
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
Result<String> deleteUser(String userId);
|
||||
|
||||
/**
|
||||
* 启用/禁用用户
|
||||
*/
|
||||
Result<String> toggleUserStatus(String userId, Integer status);
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
package com.emotionmuseum.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.entity.Conversation;
|
||||
import com.emotionmuseum.entity.Message;
|
||||
import com.emotionmuseum.mapper.ConversationMapper;
|
||||
import com.emotionmuseum.mapper.MessageMapper;
|
||||
import com.emotionmuseum.service.AiChatService;
|
||||
import com.emotionmuseum.service.CozeApiService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AI聊天服务实现类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AiChatServiceImpl implements AiChatService {
|
||||
|
||||
@Autowired
|
||||
private ConversationMapper conversationMapper;
|
||||
|
||||
@Autowired
|
||||
private MessageMapper messageMapper;
|
||||
|
||||
@Autowired
|
||||
private CozeApiService cozeApiService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Message> sendMessage(String userId, String content, String conversationId) {
|
||||
try {
|
||||
// 验证会话是否存在
|
||||
Conversation conversation = null;
|
||||
if (StringUtils.hasText(conversationId)) {
|
||||
conversation = conversationMapper.selectById(conversationId);
|
||||
if (conversation == null) {
|
||||
return Result.error("会话不存在");
|
||||
}
|
||||
if (!conversation.getUserId().equals(userId)) {
|
||||
return Result.error("无权访问此会话");
|
||||
}
|
||||
} else {
|
||||
// 创建新会话
|
||||
conversation = new Conversation();
|
||||
conversation.setUserId(userId);
|
||||
conversation.setTitle("新对话");
|
||||
conversation.setConversationType("CHAT");
|
||||
conversation.setMessageCount(0);
|
||||
conversation.setStatus(0);
|
||||
conversation.setCreateTime(LocalDateTime.now());
|
||||
conversation.setUpdateTime(LocalDateTime.now());
|
||||
conversationMapper.insert(conversation);
|
||||
}
|
||||
|
||||
// 保存用户消息
|
||||
Message userMessage = new Message();
|
||||
userMessage.setConversationId(conversation.getId());
|
||||
userMessage.setSenderId(userId);
|
||||
userMessage.setSenderType("USER");
|
||||
userMessage.setContent(content);
|
||||
userMessage.setMessageType("TEXT");
|
||||
userMessage.setStatus(2); // 已发送
|
||||
userMessage.setCreateTime(LocalDateTime.now());
|
||||
userMessage.setUpdateTime(LocalDateTime.now());
|
||||
messageMapper.insert(userMessage);
|
||||
|
||||
// 异步调用AI获取回复
|
||||
String aiReply = getAiReply(userId, content, conversation.getId());
|
||||
|
||||
// 保存AI回复
|
||||
Message aiMessage = new Message();
|
||||
aiMessage.setConversationId(conversation.getId());
|
||||
aiMessage.setSenderId("AI");
|
||||
aiMessage.setSenderType("AI");
|
||||
aiMessage.setContent(aiReply);
|
||||
aiMessage.setMessageType("TEXT");
|
||||
aiMessage.setStatus(2); // 已发送
|
||||
aiMessage.setCreateTime(LocalDateTime.now());
|
||||
aiMessage.setUpdateTime(LocalDateTime.now());
|
||||
messageMapper.insert(aiMessage);
|
||||
|
||||
// 更新会话信息
|
||||
conversation.setMessageCount(conversation.getMessageCount() + 2);
|
||||
conversation.setLastMessageTime(LocalDateTime.now());
|
||||
conversation.setUpdateTime(LocalDateTime.now());
|
||||
conversationMapper.updateById(conversation);
|
||||
|
||||
log.info("消息发送成功,用户: {}, 会话: {}", userId, conversation.getId());
|
||||
return Result.success("消息发送成功", aiMessage);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("发送消息失败: {}", e.getMessage(), e);
|
||||
return Result.error("发送消息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Conversation> createConversation(String userId, String title) {
|
||||
try {
|
||||
Conversation conversation = new Conversation();
|
||||
conversation.setUserId(userId);
|
||||
conversation.setTitle(StringUtils.hasText(title) ? title : "新对话");
|
||||
conversation.setConversationType("CHAT");
|
||||
conversation.setMessageCount(0);
|
||||
conversation.setStatus(0);
|
||||
conversation.setCreateTime(LocalDateTime.now());
|
||||
conversation.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
conversationMapper.insert(conversation);
|
||||
|
||||
log.info("创建会话成功,用户: {}, 会话: {}", userId, conversation.getId());
|
||||
return Result.success("创建会话成功", conversation);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创建会话失败: {}", e.getMessage(), e);
|
||||
return Result.error("创建会话失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<IPage<Conversation>> getUserConversations(String userId, int page, int size) {
|
||||
try {
|
||||
Page<Conversation> pageParam = new Page<>(page, size);
|
||||
IPage<Conversation> conversations = conversationMapper.selectUserConversations(pageParam, userId);
|
||||
return Result.success("获取会话列表成功", conversations);
|
||||
} catch (Exception e) {
|
||||
log.error("获取会话列表失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取会话列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<IPage<Message>> getConversationMessages(String conversationId, int page, int size) {
|
||||
try {
|
||||
Page<Message> pageParam = new Page<>(page, size);
|
||||
IPage<Message> messages = messageMapper.selectConversationMessages(pageParam, conversationId);
|
||||
return Result.success("获取消息列表成功", messages);
|
||||
} catch (Exception e) {
|
||||
log.error("获取消息列表失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取消息列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> deleteConversation(String userId, String conversationId) {
|
||||
try {
|
||||
Conversation conversation = conversationMapper.selectById(conversationId);
|
||||
if (conversation == null) {
|
||||
return Result.error("会话不存在");
|
||||
}
|
||||
if (!conversation.getUserId().equals(userId)) {
|
||||
return Result.error("无权删除此会话");
|
||||
}
|
||||
|
||||
// 删除会话下的所有消息
|
||||
QueryWrapper<Message> messageWrapper = new QueryWrapper<>();
|
||||
messageWrapper.eq("conversation_id", conversationId);
|
||||
messageMapper.delete(messageWrapper);
|
||||
|
||||
// 删除会话
|
||||
conversationMapper.deleteById(conversationId);
|
||||
|
||||
log.info("删除会话成功,用户: {}, 会话: {}", userId, conversationId);
|
||||
return Result.success("删除会话成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("删除会话失败: {}", e.getMessage(), e);
|
||||
return Result.error("删除会话失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Conversation> getConversationById(String conversationId) {
|
||||
try {
|
||||
Conversation conversation = conversationMapper.selectById(conversationId);
|
||||
if (conversation == null) {
|
||||
return Result.error("会话不存在");
|
||||
}
|
||||
return Result.success("获取会话详情成功", conversation);
|
||||
} catch (Exception e) {
|
||||
log.error("获取会话详情失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取会话详情失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> clearConversation(String userId, String conversationId) {
|
||||
try {
|
||||
Conversation conversation = conversationMapper.selectById(conversationId);
|
||||
if (conversation == null) {
|
||||
return Result.error("会话不存在");
|
||||
}
|
||||
if (!conversation.getUserId().equals(userId)) {
|
||||
return Result.error("无权清空此会话");
|
||||
}
|
||||
|
||||
// 删除会话下的所有消息
|
||||
QueryWrapper<Message> messageWrapper = new QueryWrapper<>();
|
||||
messageWrapper.eq("conversation_id", conversationId);
|
||||
messageMapper.delete(messageWrapper);
|
||||
|
||||
// 重置会话消息数量
|
||||
conversation.setMessageCount(0);
|
||||
conversation.setUpdateTime(LocalDateTime.now());
|
||||
conversationMapper.updateById(conversation);
|
||||
|
||||
log.info("清空会话成功,用户: {}, 会话: {}", userId, conversationId);
|
||||
return Result.success("清空会话成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("清空会话失败: {}", e.getMessage(), e);
|
||||
return Result.error("清空会话失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AI回复
|
||||
*/
|
||||
private String getAiReply(String userId, String content, String conversationId) {
|
||||
try {
|
||||
Result<String> result = cozeApiService.sendMessageWithContext(content, userId, conversationId);
|
||||
if (result.getCode() == 200) {
|
||||
return result.getData();
|
||||
} else {
|
||||
log.error("AI回复失败: {}", result.getMessage());
|
||||
return "抱歉,我现在无法回复,请稍后再试。";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取AI回复时发生错误: {}", e.getMessage(), e);
|
||||
return "抱歉,系统出现错误,请稍后再试。";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
package com.emotionmuseum.service.impl;
|
||||
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.dto.auth.LoginRequest;
|
||||
import com.emotionmuseum.dto.auth.LoginResponse;
|
||||
import com.emotionmuseum.dto.auth.RegisterRequest;
|
||||
import com.emotionmuseum.entity.User;
|
||||
import com.emotionmuseum.mapper.UserMapper;
|
||||
import com.emotionmuseum.service.AuthService;
|
||||
import com.emotionmuseum.util.JwtUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 认证服务实现类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AuthServiceImpl implements AuthService {
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Override
|
||||
public Result<LoginResponse> login(LoginRequest request) {
|
||||
try {
|
||||
// 参数验证
|
||||
if (!StringUtils.hasText(request.getUsername()) || !StringUtils.hasText(request.getPassword())) {
|
||||
return Result.error("用户名和密码不能为空");
|
||||
}
|
||||
|
||||
// 查找用户
|
||||
User user = userMapper.findByUsername(request.getUsername());
|
||||
if (user == null) {
|
||||
user = userMapper.findByEmail(request.getUsername());
|
||||
}
|
||||
if (user == null) {
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
|
||||
return Result.error("密码错误");
|
||||
}
|
||||
|
||||
// 检查用户状态
|
||||
if (user.getStatus() != 1) {
|
||||
return Result.error("用户已被禁用");
|
||||
}
|
||||
|
||||
// 生成令牌
|
||||
String accessToken = jwtUtil.generateToken(user.getId(), user.getUsername());
|
||||
String refreshToken = jwtUtil.generateToken(user.getId(), user.getUsername());
|
||||
|
||||
// 更新最后登录时间
|
||||
user.setLastLoginTime(LocalDateTime.now());
|
||||
userMapper.updateById(user);
|
||||
|
||||
// 构建响应
|
||||
LoginResponse response = new LoginResponse();
|
||||
response.setAccessToken(accessToken);
|
||||
response.setRefreshToken(refreshToken);
|
||||
response.setExpiresIn(86400L); // 24小时
|
||||
|
||||
LoginResponse.UserInfo userInfo = new LoginResponse.UserInfo();
|
||||
userInfo.setId(user.getId());
|
||||
userInfo.setUsername(user.getUsername());
|
||||
userInfo.setNickname(user.getNickname());
|
||||
userInfo.setEmail(user.getEmail());
|
||||
userInfo.setAvatar(user.getAvatar());
|
||||
userInfo.setUserType(user.getUserType());
|
||||
response.setUserInfo(userInfo);
|
||||
|
||||
// 将令牌存储到Redis
|
||||
String tokenKey = "token:" + user.getId();
|
||||
redisTemplate.opsForValue().set(tokenKey, accessToken, 24, TimeUnit.HOURS);
|
||||
|
||||
log.info("用户登录成功: {}", user.getUsername());
|
||||
return Result.success("登录成功", response);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("用户登录失败: {}", e.getMessage(), e);
|
||||
return Result.error("登录失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> register(RegisterRequest request) {
|
||||
try {
|
||||
// 参数验证
|
||||
if (!StringUtils.hasText(request.getUsername()) || !StringUtils.hasText(request.getPassword())) {
|
||||
return Result.error("用户名和密码不能为空");
|
||||
}
|
||||
|
||||
if (!request.getPassword().equals(request.getConfirmPassword())) {
|
||||
return Result.error("两次输入的密码不一致");
|
||||
}
|
||||
|
||||
// 检查用户名是否已存在
|
||||
if (userMapper.countByUsername(request.getUsername()) > 0) {
|
||||
return Result.error("用户名已存在");
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
if (StringUtils.hasText(request.getEmail()) && userMapper.countByEmail(request.getEmail()) > 0) {
|
||||
return Result.error("邮箱已被注册");
|
||||
}
|
||||
|
||||
// 检查手机号是否已存在
|
||||
if (StringUtils.hasText(request.getPhone()) && userMapper.countByPhone(request.getPhone()) > 0) {
|
||||
return Result.error("手机号已被注册");
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
User user = new User();
|
||||
user.setUsername(request.getUsername());
|
||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
||||
user.setEmail(request.getEmail());
|
||||
user.setPhone(request.getPhone());
|
||||
user.setNickname(StringUtils.hasText(request.getNickname()) ? request.getNickname() : request.getUsername());
|
||||
user.setStatus(1);
|
||||
user.setUserType(0);
|
||||
user.setCreateTime(LocalDateTime.now());
|
||||
user.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
userMapper.insert(user);
|
||||
|
||||
log.info("用户注册成功: {}", user.getUsername());
|
||||
return Result.success("注册成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("用户注册失败: {}", e.getMessage(), e);
|
||||
return Result.error("注册失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> logout(String token) {
|
||||
try {
|
||||
if (StringUtils.hasText(token)) {
|
||||
String userId = jwtUtil.getUserIdFromToken(token);
|
||||
if (StringUtils.hasText(userId)) {
|
||||
// 从Redis中删除令牌
|
||||
String tokenKey = "token:" + userId;
|
||||
redisTemplate.delete(tokenKey);
|
||||
}
|
||||
}
|
||||
return Result.success("登出成功");
|
||||
} catch (Exception e) {
|
||||
log.error("用户登出失败: {}", e.getMessage(), e);
|
||||
return Result.error("登出失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> refreshToken(String refreshToken) {
|
||||
try {
|
||||
if (!StringUtils.hasText(refreshToken)) {
|
||||
return Result.error("刷新令牌不能为空");
|
||||
}
|
||||
|
||||
if (!jwtUtil.validateToken(refreshToken)) {
|
||||
return Result.error("刷新令牌无效或已过期");
|
||||
}
|
||||
|
||||
String userId = jwtUtil.getUserIdFromToken(refreshToken);
|
||||
String username = jwtUtil.getUsernameFromToken(refreshToken);
|
||||
|
||||
// 生成新的访问令牌
|
||||
String newAccessToken = jwtUtil.generateToken(userId, username);
|
||||
|
||||
// 更新Redis中的令牌
|
||||
String tokenKey = "token:" + userId;
|
||||
redisTemplate.opsForValue().set(tokenKey, newAccessToken, 24, TimeUnit.HOURS);
|
||||
|
||||
return Result.success("令牌刷新成功", newAccessToken);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("刷新令牌失败: {}", e.getMessage(), e);
|
||||
return Result.error("刷新令牌失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateToken(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 验证JWT令牌
|
||||
if (!jwtUtil.validateToken(token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查Redis中是否存在令牌
|
||||
String userId = jwtUtil.getUserIdFromToken(token);
|
||||
String tokenKey = "token:" + userId;
|
||||
String storedToken = (String) redisTemplate.opsForValue().get(tokenKey);
|
||||
|
||||
return token.equals(storedToken);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("验证令牌失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserIdFromToken(String token) {
|
||||
try {
|
||||
return jwtUtil.getUserIdFromToken(token);
|
||||
} catch (Exception e) {
|
||||
log.error("从令牌中获取用户ID失败: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
package com.emotionmuseum.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.dto.comment.CommentRequest;
|
||||
import com.emotionmuseum.dto.comment.CommentResponse;
|
||||
import com.emotionmuseum.entity.Comment;
|
||||
import com.emotionmuseum.mapper.CommentMapper;
|
||||
import com.emotionmuseum.service.CommentService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 评论服务实现类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class CommentServiceImpl implements CommentService {
|
||||
|
||||
@Autowired
|
||||
private CommentMapper commentMapper;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<CommentResponse> createComment(String userId, CommentRequest request) {
|
||||
try {
|
||||
Comment comment = new Comment();
|
||||
comment.setParentId(request.getParentId());
|
||||
comment.setContentType(request.getContentType());
|
||||
comment.setContentId(request.getContentId());
|
||||
comment.setUserId(userId);
|
||||
comment.setContent(request.getContent());
|
||||
comment.setLikeCount(0);
|
||||
comment.setReplyCount(0);
|
||||
comment.setStatus(1);
|
||||
comment.setCreateTime(LocalDateTime.now());
|
||||
comment.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
commentMapper.insert(comment);
|
||||
|
||||
CommentResponse response = buildCommentResponse(comment);
|
||||
log.info("创建评论成功,用户: {}, 评论: {}", userId, comment.getId());
|
||||
return Result.success("创建评论成功", response);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创建评论失败: {}", e.getMessage(), e);
|
||||
return Result.error("创建评论失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<IPage<CommentResponse>> getContentComments(String contentType, String contentId, int page, int size) {
|
||||
try {
|
||||
Page<Comment> pageParam = new Page<>(page, size);
|
||||
IPage<Comment> comments = commentMapper.selectContentComments(pageParam, contentType, contentId);
|
||||
|
||||
IPage<CommentResponse> responsePage = new Page<>(page, size);
|
||||
responsePage.setTotal(comments.getTotal());
|
||||
responsePage.setPages(comments.getPages());
|
||||
responsePage.setCurrent(comments.getCurrent());
|
||||
responsePage.setSize(comments.getSize());
|
||||
|
||||
List<CommentResponse> responses = comments.getRecords().stream()
|
||||
.map(this::buildCommentResponse)
|
||||
.collect(Collectors.toList());
|
||||
responsePage.setRecords(responses);
|
||||
|
||||
return Result.success("获取评论列表成功", responsePage);
|
||||
} catch (Exception e) {
|
||||
log.error("获取评论列表失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取评论列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<CommentResponse> getCommentById(String commentId) {
|
||||
try {
|
||||
Comment comment = commentMapper.selectById(commentId);
|
||||
if (comment == null) {
|
||||
return Result.error("评论不存在");
|
||||
}
|
||||
|
||||
CommentResponse response = buildCommentResponse(comment);
|
||||
return Result.success("获取评论详情成功", response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取评论详情失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取评论详情失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> deleteComment(String userId, String commentId) {
|
||||
try {
|
||||
Comment comment = commentMapper.selectById(commentId);
|
||||
if (comment == null) {
|
||||
return Result.error("评论不存在");
|
||||
}
|
||||
if (!comment.getUserId().equals(userId)) {
|
||||
return Result.error("无权删除此评论");
|
||||
}
|
||||
|
||||
commentMapper.deleteById(commentId);
|
||||
log.info("删除评论成功,用户: {}, 评论: {}", userId, commentId);
|
||||
return Result.success("删除评论成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("删除评论失败: {}", e.getMessage(), e);
|
||||
return Result.error("删除评论失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> likeComment(String userId, String commentId) {
|
||||
try {
|
||||
Comment comment = commentMapper.selectById(commentId);
|
||||
if (comment == null) {
|
||||
return Result.error("评论不存在");
|
||||
}
|
||||
|
||||
comment.setLikeCount(comment.getLikeCount() + 1);
|
||||
comment.setUpdateTime(LocalDateTime.now());
|
||||
commentMapper.updateById(comment);
|
||||
|
||||
log.info("点赞评论成功,用户: {}, 评论: {}", userId, commentId);
|
||||
return Result.success("点赞成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("点赞评论失败: {}", e.getMessage(), e);
|
||||
return Result.error("点赞失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> unlikeComment(String userId, String commentId) {
|
||||
try {
|
||||
Comment comment = commentMapper.selectById(commentId);
|
||||
if (comment == null) {
|
||||
return Result.error("评论不存在");
|
||||
}
|
||||
|
||||
if (comment.getLikeCount() > 0) {
|
||||
comment.setLikeCount(comment.getLikeCount() - 1);
|
||||
comment.setUpdateTime(LocalDateTime.now());
|
||||
commentMapper.updateById(comment);
|
||||
}
|
||||
|
||||
log.info("取消点赞成功,用户: {}, 评论: {}", userId, commentId);
|
||||
return Result.success("取消点赞成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("取消点赞失败: {}", e.getMessage(), e);
|
||||
return Result.error("取消点赞失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<IPage<CommentResponse>> getUserComments(String userId, int page, int size) {
|
||||
try {
|
||||
Page<Comment> pageParam = new Page<>(page, size);
|
||||
IPage<Comment> comments = commentMapper.selectPage(pageParam, null);
|
||||
|
||||
IPage<CommentResponse> responsePage = new Page<>(page, size);
|
||||
responsePage.setTotal(comments.getTotal());
|
||||
responsePage.setPages(comments.getPages());
|
||||
responsePage.setCurrent(comments.getCurrent());
|
||||
responsePage.setSize(comments.getSize());
|
||||
|
||||
List<CommentResponse> responses = comments.getRecords().stream()
|
||||
.map(this::buildCommentResponse)
|
||||
.collect(Collectors.toList());
|
||||
responsePage.setRecords(responses);
|
||||
|
||||
return Result.success("获取用户评论列表成功", responsePage);
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户评论列表失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取用户评论列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<CommentResponse>> getCommentReplies(String commentId) {
|
||||
try {
|
||||
List<Comment> replies = commentMapper.selectCommentReplies(commentId);
|
||||
List<CommentResponse> responses = replies.stream()
|
||||
.map(this::buildCommentResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Result.success("获取评论回复列表成功", responses);
|
||||
} catch (Exception e) {
|
||||
log.error("获取评论回复列表失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取评论回复列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建评论响应DTO
|
||||
*/
|
||||
private CommentResponse buildCommentResponse(Comment comment) {
|
||||
CommentResponse response = new CommentResponse();
|
||||
response.setId(comment.getId());
|
||||
response.setParentId(comment.getParentId());
|
||||
response.setContentType(comment.getContentType());
|
||||
response.setContentId(comment.getContentId());
|
||||
response.setUserId(comment.getUserId());
|
||||
response.setContent(comment.getContent());
|
||||
response.setLikeCount(comment.getLikeCount());
|
||||
response.setReplyCount(comment.getReplyCount());
|
||||
response.setStatus(comment.getStatus());
|
||||
response.setCreateTime(comment.getCreateTime());
|
||||
response.setIsLiked(false);
|
||||
response.setReplies(new ArrayList<>());
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
package com.emotionmuseum.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.emotionmuseum.config.CozeConfig;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.service.CozeApiService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Coze API服务实现类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class CozeApiServiceImpl implements CozeApiService {
|
||||
|
||||
@Autowired
|
||||
private CozeConfig cozeConfig;
|
||||
|
||||
@Autowired
|
||||
private WebClient.Builder webClientBuilder;
|
||||
|
||||
@Override
|
||||
public Result<String> sendMessage(String message, String userId) {
|
||||
try {
|
||||
if (!StrUtil.isNotBlank(cozeConfig.getApiKey()) || !StrUtil.isNotBlank(cozeConfig.getBotId())) {
|
||||
return Result.error("Coze API配置不完整");
|
||||
}
|
||||
|
||||
if (!StrUtil.isNotBlank(message)) {
|
||||
return Result.error("消息内容不能为空");
|
||||
}
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("bot_id", cozeConfig.getBotId());
|
||||
requestBody.put("user_id", userId);
|
||||
requestBody.put("query", message);
|
||||
requestBody.put("stream", false);
|
||||
|
||||
String response = webClientBuilder.build()
|
||||
.post()
|
||||
.uri(cozeConfig.getBaseUrl() + "/bot/chat")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + cozeConfig.getApiKey())
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.bodyValue(requestBody)
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.timeout(Duration.ofMillis(cozeConfig.getTimeout()))
|
||||
.block();
|
||||
|
||||
if (StrUtil.isNotBlank(response)) {
|
||||
JSONObject jsonResponse = JSONUtil.parseObj(response);
|
||||
if (jsonResponse.getInt("code", -1) == 0) {
|
||||
JSONObject data = jsonResponse.getJSONObject("data");
|
||||
String reply = data.getStr("reply");
|
||||
log.info("Coze API调用成功,用户: {}, 消息: {}", userId, message);
|
||||
return Result.success("AI回复成功", reply);
|
||||
} else {
|
||||
String errorMsg = jsonResponse.getStr("message", "未知错误");
|
||||
log.error("Coze API调用失败: {}", errorMsg);
|
||||
return Result.error("AI回复失败: " + errorMsg);
|
||||
}
|
||||
} else {
|
||||
log.error("Coze API返回空响应");
|
||||
return Result.error("AI回复失败: 空响应");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("调用Coze API时发生错误: {}", e.getMessage(), e);
|
||||
return Result.error("AI回复失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> sendMessageWithContext(String message, String userId, String conversationId) {
|
||||
try {
|
||||
if (!StrUtil.isNotBlank(cozeConfig.getApiKey()) || !StrUtil.isNotBlank(cozeConfig.getBotId())) {
|
||||
return Result.error("Coze API配置不完整");
|
||||
}
|
||||
|
||||
if (!StrUtil.isNotBlank(message)) {
|
||||
return Result.error("消息内容不能为空");
|
||||
}
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("bot_id", cozeConfig.getBotId());
|
||||
requestBody.put("user_id", userId);
|
||||
requestBody.put("query", message);
|
||||
requestBody.put("conversation_id", conversationId);
|
||||
requestBody.put("stream", false);
|
||||
|
||||
String response = webClientBuilder.build()
|
||||
.post()
|
||||
.uri(cozeConfig.getBaseUrl() + "/bot/chat")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + cozeConfig.getApiKey())
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.bodyValue(requestBody)
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.timeout(Duration.ofMillis(cozeConfig.getTimeout()))
|
||||
.block();
|
||||
|
||||
if (StrUtil.isNotBlank(response)) {
|
||||
JSONObject jsonResponse = JSONUtil.parseObj(response);
|
||||
if (jsonResponse.getInt("code", -1) == 0) {
|
||||
JSONObject data = jsonResponse.getJSONObject("data");
|
||||
String reply = data.getStr("reply");
|
||||
log.info("Coze API调用成功,用户: {}, 会话: {}, 消息: {}", userId, conversationId, message);
|
||||
return Result.success("AI回复成功", reply);
|
||||
} else {
|
||||
String errorMsg = jsonResponse.getStr("message", "未知错误");
|
||||
log.error("Coze API调用失败: {}", errorMsg);
|
||||
return Result.error("AI回复失败: " + errorMsg);
|
||||
}
|
||||
} else {
|
||||
log.error("Coze API返回空响应");
|
||||
return Result.error("AI回复失败: 空响应");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("调用Coze API时发生错误: {}", e.getMessage(), e);
|
||||
return Result.error("AI回复失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> getBotInfo() {
|
||||
try {
|
||||
if (!StrUtil.isNotBlank(cozeConfig.getApiKey()) || !StrUtil.isNotBlank(cozeConfig.getBotId())) {
|
||||
return Result.error("Coze API配置不完整");
|
||||
}
|
||||
|
||||
String response = webClientBuilder.build()
|
||||
.get()
|
||||
.uri(cozeConfig.getBaseUrl() + "/bot/" + cozeConfig.getBotId())
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + cozeConfig.getApiKey())
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.timeout(Duration.ofMillis(cozeConfig.getTimeout()))
|
||||
.block();
|
||||
|
||||
if (StrUtil.isNotBlank(response)) {
|
||||
JSONObject jsonResponse = JSONUtil.parseObj(response);
|
||||
if (jsonResponse.getInt("code", -1) == 0) {
|
||||
JSONObject data = jsonResponse.getJSONObject("data");
|
||||
log.info("获取Bot信息成功");
|
||||
return Result.success("获取Bot信息成功", data.toString());
|
||||
} else {
|
||||
String errorMsg = jsonResponse.getStr("message", "未知错误");
|
||||
log.error("获取Bot信息失败: {}", errorMsg);
|
||||
return Result.error("获取Bot信息失败: " + errorMsg);
|
||||
}
|
||||
} else {
|
||||
log.error("获取Bot信息返回空响应");
|
||||
return Result.error("获取Bot信息失败: 空响应");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取Bot信息时发生错误: {}", e.getMessage(), e);
|
||||
return Result.error("获取Bot信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Boolean> checkConnection() {
|
||||
try {
|
||||
if (!StrUtil.isNotBlank(cozeConfig.getApiKey()) || !StrUtil.isNotBlank(cozeConfig.getBotId())) {
|
||||
return Result.error("Coze API配置不完整");
|
||||
}
|
||||
|
||||
// 尝试获取Bot信息来检查连接
|
||||
Result<String> botInfoResult = getBotInfo();
|
||||
if (botInfoResult.getCode() == 200) {
|
||||
log.info("Coze API连接正常");
|
||||
return Result.success("连接正常", true);
|
||||
} else {
|
||||
log.error("Coze API连接失败: {}", botInfoResult.getMessage());
|
||||
return Result.error("连接失败: " + botInfoResult.getMessage());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("检查Coze API连接时发生错误: {}", e.getMessage(), e);
|
||||
return Result.error("连接检查失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
package com.emotionmuseum.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.dto.diary.DiaryPostRequest;
|
||||
import com.emotionmuseum.entity.DiaryPost;
|
||||
import com.emotionmuseum.mapper.DiaryPostMapper;
|
||||
import com.emotionmuseum.service.CozeApiService;
|
||||
import com.emotionmuseum.service.DiaryPostService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 日记服务实现类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class DiaryPostServiceImpl implements DiaryPostService {
|
||||
|
||||
@Autowired
|
||||
private DiaryPostMapper diaryPostMapper;
|
||||
|
||||
@Autowired
|
||||
private CozeApiService cozeApiService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<DiaryPost> createDiary(String userId, DiaryPostRequest request) {
|
||||
try {
|
||||
DiaryPost diaryPost = new DiaryPost();
|
||||
diaryPost.setUserId(userId);
|
||||
diaryPost.setTitle(request.getTitle());
|
||||
diaryPost.setContent(request.getContent());
|
||||
diaryPost.setEmotionTags(request.getEmotionTags());
|
||||
diaryPost.setEmotionScore(request.getEmotionScore());
|
||||
diaryPost.setWeather(request.getWeather());
|
||||
diaryPost.setLocation(request.getLocation());
|
||||
diaryPost.setImages(request.getImages());
|
||||
diaryPost.setIsPublic(request.getIsPublic());
|
||||
diaryPost.setLikeCount(0);
|
||||
diaryPost.setCommentCount(0);
|
||||
diaryPost.setShareCount(0);
|
||||
diaryPost.setStatus(1); // 已发布
|
||||
diaryPost.setCreateTime(LocalDateTime.now());
|
||||
diaryPost.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
diaryPostMapper.insert(diaryPost);
|
||||
|
||||
// 异步生成AI点评
|
||||
generateAiComment(diaryPost);
|
||||
|
||||
log.info("创建日记成功,用户: {}, 日记: {}", userId, diaryPost.getId());
|
||||
return Result.success("创建日记成功", diaryPost);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创建日记失败: {}", e.getMessage(), e);
|
||||
return Result.error("创建日记失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> updateDiary(String userId, String diaryId, DiaryPostRequest request) {
|
||||
try {
|
||||
DiaryPost diaryPost = diaryPostMapper.selectById(diaryId);
|
||||
if (diaryPost == null) {
|
||||
return Result.error("日记不存在");
|
||||
}
|
||||
if (!diaryPost.getUserId().equals(userId)) {
|
||||
return Result.error("无权修改此日记");
|
||||
}
|
||||
|
||||
diaryPost.setTitle(request.getTitle());
|
||||
diaryPost.setContent(request.getContent());
|
||||
diaryPost.setEmotionTags(request.getEmotionTags());
|
||||
diaryPost.setEmotionScore(request.getEmotionScore());
|
||||
diaryPost.setWeather(request.getWeather());
|
||||
diaryPost.setLocation(request.getLocation());
|
||||
diaryPost.setImages(request.getImages());
|
||||
diaryPost.setIsPublic(request.getIsPublic());
|
||||
diaryPost.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
diaryPostMapper.updateById(diaryPost);
|
||||
|
||||
log.info("更新日记成功,用户: {}, 日记: {}", userId, diaryId);
|
||||
return Result.success("更新日记成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("更新日记失败: {}", e.getMessage(), e);
|
||||
return Result.error("更新日记失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<DiaryPost> getDiaryById(String diaryId) {
|
||||
try {
|
||||
DiaryPost diaryPost = diaryPostMapper.selectById(diaryId);
|
||||
if (diaryPost == null) {
|
||||
return Result.error("日记不存在");
|
||||
}
|
||||
return Result.success("获取日记详情成功", diaryPost);
|
||||
} catch (Exception e) {
|
||||
log.error("获取日记详情失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取日记详情失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<IPage<DiaryPost>> getUserDiaries(String userId, int page, int size) {
|
||||
try {
|
||||
Page<DiaryPost> pageParam = new Page<>(page, size);
|
||||
IPage<DiaryPost> diaries = diaryPostMapper.selectUserDiaries(pageParam, userId);
|
||||
return Result.success("获取用户日记列表成功", diaries);
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户日记列表失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取用户日记列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<IPage<DiaryPost>> getPublicDiaries(int page, int size) {
|
||||
try {
|
||||
Page<DiaryPost> pageParam = new Page<>(page, size);
|
||||
IPage<DiaryPost> diaries = diaryPostMapper.selectPublicDiaries(pageParam);
|
||||
return Result.success("获取公开日记列表成功", diaries);
|
||||
} catch (Exception e) {
|
||||
log.error("获取公开日记列表失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取公开日记列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<IPage<DiaryPost>> getDiariesByEmotionTag(String emotionTag, int page, int size) {
|
||||
try {
|
||||
Page<DiaryPost> pageParam = new Page<>(page, size);
|
||||
IPage<DiaryPost> diaries = diaryPostMapper.selectDiariesByEmotionTag(pageParam, emotionTag);
|
||||
return Result.success("获取情绪标签日记列表成功", diaries);
|
||||
} catch (Exception e) {
|
||||
log.error("获取情绪标签日记列表失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取情绪标签日记列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> deleteDiary(String userId, String diaryId) {
|
||||
try {
|
||||
DiaryPost diaryPost = diaryPostMapper.selectById(diaryId);
|
||||
if (diaryPost == null) {
|
||||
return Result.error("日记不存在");
|
||||
}
|
||||
if (!diaryPost.getUserId().equals(userId)) {
|
||||
return Result.error("无权删除此日记");
|
||||
}
|
||||
|
||||
diaryPostMapper.deleteById(diaryId);
|
||||
|
||||
log.info("删除日记成功,用户: {}, 日记: {}", userId, diaryId);
|
||||
return Result.success("删除日记成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("删除日记失败: {}", e.getMessage(), e);
|
||||
return Result.error("删除日记失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> likeDiary(String userId, String diaryId) {
|
||||
try {
|
||||
DiaryPost diaryPost = diaryPostMapper.selectById(diaryId);
|
||||
if (diaryPost == null) {
|
||||
return Result.error("日记不存在");
|
||||
}
|
||||
|
||||
diaryPost.setLikeCount(diaryPost.getLikeCount() + 1);
|
||||
diaryPost.setUpdateTime(LocalDateTime.now());
|
||||
diaryPostMapper.updateById(diaryPost);
|
||||
|
||||
log.info("点赞日记成功,用户: {}, 日记: {}", userId, diaryId);
|
||||
return Result.success("点赞成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("点赞日记失败: {}", e.getMessage(), e);
|
||||
return Result.error("点赞失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> unlikeDiary(String userId, String diaryId) {
|
||||
try {
|
||||
DiaryPost diaryPost = diaryPostMapper.selectById(diaryId);
|
||||
if (diaryPost == null) {
|
||||
return Result.error("日记不存在");
|
||||
}
|
||||
|
||||
if (diaryPost.getLikeCount() > 0) {
|
||||
diaryPost.setLikeCount(diaryPost.getLikeCount() - 1);
|
||||
diaryPost.setUpdateTime(LocalDateTime.now());
|
||||
diaryPostMapper.updateById(diaryPost);
|
||||
}
|
||||
|
||||
log.info("取消点赞成功,用户: {}, 日记: {}", userId, diaryId);
|
||||
return Result.success("取消点赞成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("取消点赞失败: {}", e.getMessage(), e);
|
||||
return Result.error("取消点赞失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> getAiComment(String userId, String diaryId) {
|
||||
try {
|
||||
DiaryPost diaryPost = diaryPostMapper.selectById(diaryId);
|
||||
if (diaryPost == null) {
|
||||
return Result.error("日记不存在");
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(diaryPost.getAiComment())) {
|
||||
return Result.success("获取AI点评成功", diaryPost.getAiComment());
|
||||
}
|
||||
|
||||
// 生成AI点评
|
||||
String aiComment = generateAiComment(diaryPost);
|
||||
return Result.success("获取AI点评成功", aiComment);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取AI点评失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取AI点评失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成AI点评
|
||||
*/
|
||||
private String generateAiComment(DiaryPost diaryPost) {
|
||||
try {
|
||||
String prompt = String.format(
|
||||
"请对以下日记进行情感分析和点评,要求:\n" +
|
||||
"1. 分析作者的情感状态\n" +
|
||||
"2. 提供积极正面的建议\n" +
|
||||
"3. 字数控制在200字以内\n" +
|
||||
"4. 语言温暖友善\n\n" +
|
||||
"日记标题:%s\n" +
|
||||
"日记内容:%s\n" +
|
||||
"情绪标签:%s\n" +
|
||||
"情绪评分:%d/10",
|
||||
diaryPost.getTitle(),
|
||||
diaryPost.getContent(),
|
||||
diaryPost.getEmotionTags(),
|
||||
diaryPost.getEmotionScore()
|
||||
);
|
||||
|
||||
Result<String> result = cozeApiService.sendMessage(prompt, diaryPost.getUserId());
|
||||
if (result.getCode() == 200) {
|
||||
String aiComment = result.getData();
|
||||
diaryPost.setAiComment(aiComment);
|
||||
diaryPost.setUpdateTime(LocalDateTime.now());
|
||||
diaryPostMapper.updateById(diaryPost);
|
||||
return aiComment;
|
||||
} else {
|
||||
log.error("生成AI点评失败: {}", result.getMessage());
|
||||
return "AI正在思考中,请稍后再试。";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("生成AI点评时发生错误: {}", e.getMessage(), e);
|
||||
return "AI点评生成失败,请稍后再试。";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
package com.emotionmuseum.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.emotionmuseum.dto.Result;
|
||||
import com.emotionmuseum.entity.User;
|
||||
import com.emotionmuseum.mapper.UserMapper;
|
||||
import com.emotionmuseum.service.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户服务实现类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public Result<User> getUserById(String userId) {
|
||||
try {
|
||||
User user = userMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
// 清除敏感信息
|
||||
user.setPassword(null);
|
||||
return Result.success("获取用户信息成功", user);
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户信息失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取用户信息失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> updateUser(String userId, User user) {
|
||||
try {
|
||||
User existingUser = userMapper.selectById(userId);
|
||||
if (existingUser == null) {
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
|
||||
// 只允许更新特定字段
|
||||
if (StringUtils.hasText(user.getNickname())) {
|
||||
existingUser.setNickname(user.getNickname());
|
||||
}
|
||||
if (StringUtils.hasText(user.getEmail())) {
|
||||
existingUser.setEmail(user.getEmail());
|
||||
}
|
||||
if (StringUtils.hasText(user.getPhone())) {
|
||||
existingUser.setPhone(user.getPhone());
|
||||
}
|
||||
if (StringUtils.hasText(user.getAvatar())) {
|
||||
existingUser.setAvatar(user.getAvatar());
|
||||
}
|
||||
if (StringUtils.hasText(user.getBio())) {
|
||||
existingUser.setBio(user.getBio());
|
||||
}
|
||||
if (user.getGender() != null) {
|
||||
existingUser.setGender(user.getGender());
|
||||
}
|
||||
if (user.getBirthday() != null) {
|
||||
existingUser.setBirthday(user.getBirthday());
|
||||
}
|
||||
|
||||
existingUser.setUpdateTime(LocalDateTime.now());
|
||||
userMapper.updateById(existingUser);
|
||||
|
||||
log.info("用户信息更新成功: {}", userId);
|
||||
return Result.success("用户信息更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("更新用户信息失败: {}", e.getMessage(), e);
|
||||
return Result.error("更新用户信息失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> changePassword(String userId, String oldPassword, String newPassword) {
|
||||
try {
|
||||
User user = userMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
|
||||
// 验证旧密码
|
||||
if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
|
||||
return Result.error("原密码错误");
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
user.setPassword(passwordEncoder.encode(newPassword));
|
||||
user.setUpdateTime(LocalDateTime.now());
|
||||
userMapper.updateById(user);
|
||||
|
||||
log.info("用户密码修改成功: {}", userId);
|
||||
return Result.success("密码修改成功");
|
||||
} catch (Exception e) {
|
||||
log.error("修改密码失败: {}", e.getMessage(), e);
|
||||
return Result.error("修改密码失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<IPage<User>> getUserList(int page, int size) {
|
||||
try {
|
||||
Page<User> pageParam = new Page<>(page, size);
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.orderByDesc("create_time");
|
||||
|
||||
IPage<User> userPage = userMapper.selectPage(pageParam, queryWrapper);
|
||||
|
||||
// 清除敏感信息
|
||||
userPage.getRecords().forEach(user -> user.setPassword(null));
|
||||
|
||||
return Result.success("获取用户列表成功", userPage);
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户列表失败: {}", e.getMessage(), e);
|
||||
return Result.error("获取用户列表失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> deleteUser(String userId) {
|
||||
try {
|
||||
User user = userMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
|
||||
userMapper.deleteById(userId);
|
||||
log.info("用户删除成功: {}", userId);
|
||||
return Result.success("用户删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除用户失败: {}", e.getMessage(), e);
|
||||
return Result.error("删除用户失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> toggleUserStatus(String userId, Integer status) {
|
||||
try {
|
||||
User user = userMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
|
||||
user.setStatus(status);
|
||||
user.setUpdateTime(LocalDateTime.now());
|
||||
userMapper.updateById(user);
|
||||
|
||||
String message = status == 1 ? "用户启用成功" : "用户禁用成功";
|
||||
log.info("用户状态更新成功: {} -> {}", userId, status);
|
||||
return Result.success(message);
|
||||
} catch (Exception e) {
|
||||
log.error("更新用户状态失败: {}", e.getMessage(), e);
|
||||
return Result.error("更新用户状态失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package com.emotionmuseum.util;
|
||||
|
||||
import io.jsonwebtoken.*;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JWT工具类
|
||||
*
|
||||
* @author emotion-museum
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class JwtUtil {
|
||||
|
||||
@Value("${emotion.jwt.secret}")
|
||||
private String secret;
|
||||
|
||||
@Value("${emotion.jwt.expiration}")
|
||||
private Long expiration;
|
||||
|
||||
/**
|
||||
* 生成JWT令牌
|
||||
*/
|
||||
public String generateToken(String userId, String username) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("userId", userId);
|
||||
claims.put("username", username);
|
||||
return createToken(claims, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建令牌
|
||||
*/
|
||||
private String createToken(Map<String, Object> claims, String subject) {
|
||||
Date now = new Date();
|
||||
Date expiryDate = new Date(now.getTime() + expiration);
|
||||
|
||||
SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
|
||||
|
||||
return Jwts.builder()
|
||||
.claims(claims)
|
||||
.subject(subject)
|
||||
.issuedAt(now)
|
||||
.expiration(expiryDate)
|
||||
.signWith(key, Jwts.SIG.HS512)
|
||||
.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取用户ID
|
||||
*/
|
||||
public String getUserIdFromToken(String token) {
|
||||
return getClaimFromToken(token, "userId", String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取用户名
|
||||
*/
|
||||
public String getUsernameFromToken(String token) {
|
||||
return getClaimFromToken(token, "username", String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取过期时间
|
||||
*/
|
||||
public Date getExpirationDateFromToken(String token) {
|
||||
return getClaimFromToken(token, Claims::getExpiration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取指定声明
|
||||
*/
|
||||
public <T> T getClaimFromToken(String token, String claimName, Class<T> requiredType) {
|
||||
final Claims claims = getAllClaimsFromToken(token);
|
||||
return claims.get(claimName, requiredType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取指定声明
|
||||
*/
|
||||
public <T> T getClaimFromToken(String token, java.util.function.Function<Claims, T> claimsResolver) {
|
||||
final Claims claims = getAllClaimsFromToken(token);
|
||||
return claimsResolver.apply(claims);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取所有声明
|
||||
*/
|
||||
private Claims getAllClaimsFromToken(String token) {
|
||||
SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
|
||||
return Jwts.parser()
|
||||
.verifyWith(key)
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.getPayload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查令牌是否过期
|
||||
*/
|
||||
public Boolean isTokenExpired(String token) {
|
||||
try {
|
||||
final Date expiration = getExpirationDateFromToken(token);
|
||||
return expiration.before(new Date());
|
||||
} catch (Exception e) {
|
||||
log.error("检查令牌过期时发生错误: {}", e.getMessage());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证令牌
|
||||
*/
|
||||
public Boolean validateToken(String token) {
|
||||
try {
|
||||
SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
|
||||
Jwts.parser()
|
||||
.verifyWith(key)
|
||||
.build()
|
||||
.parseSignedClaims(token);
|
||||
return !isTokenExpired(token);
|
||||
} catch (JwtException | IllegalArgumentException e) {
|
||||
log.error("验证令牌时发生错误: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
public String refreshToken(String token) {
|
||||
try {
|
||||
final Claims claims = getAllClaimsFromToken(token);
|
||||
// 创建新的声明,因为Claims是不可变的
|
||||
Map<String, Object> newClaims = new HashMap<>(claims);
|
||||
newClaims.put("iat", new Date().getTime() / 1000);
|
||||
return createToken(newClaims, claims.getSubject());
|
||||
} catch (Exception e) {
|
||||
log.error("刷新令牌时发生错误: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user