diff --git a/pom.xml b/pom.xml index 1c6ccd6..4df16b9 100644 --- a/pom.xml +++ b/pom.xml @@ -126,6 +126,10 @@ test + + org.springframework.boot + spring-boot-starter-amqp + org.dromara.x-file-storage x-file-storage-spring diff --git a/src/main/java/com/yupi/springbootinit/config/RabbitMQConfig.java b/src/main/java/com/yupi/springbootinit/config/RabbitMQConfig.java new file mode 100644 index 0000000..9840dee --- /dev/null +++ b/src/main/java/com/yupi/springbootinit/config/RabbitMQConfig.java @@ -0,0 +1,64 @@ +package com.yupi.springbootinit.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.amqp.core.ExchangeBuilder; +import org.springframework.amqp.core.HeadersExchange; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RabbitMQConfig { + + private static final String QUEUE = "HOST_INFO_QUEUE"; + + public static final String EXCHANGE_NAME = "user.headers.exchange"; + + //创建队列 + //true:表示持久化 + //队列在默认情况下放到内存,rabbitmq重启后就丢失了,如果希望重启后,队列 + //数据还能使用,就需要持久化 + @Bean + public Queue hostInfoQueue(){ + return new Queue(QUEUE,true); + } + +// +// @Bean +// public MessageConverter messageConverter(){ +// return new Jackson2JsonMessageConverter(); +// } + + + + @Bean + public HeadersExchange userHeadersExchange() { + return ExchangeBuilder.headersExchange(EXCHANGE_NAME) + .durable(true) + .build(); + } + + @Bean + public RabbitAdmin rabbitAdmin(ConnectionFactory cf) { + return new RabbitAdmin(cf); + } + + + @Bean + public MessageConverter messageConverter() { + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + om.registerModule(new JavaTimeModule()); + return new Jackson2JsonMessageConverter(om); + } + +} \ No newline at end of file diff --git a/src/main/java/com/yupi/springbootinit/config/RedisConfig.java b/src/main/java/com/yupi/springbootinit/config/RedisConfig.java index 287c1d5..a7e848e 100644 --- a/src/main/java/com/yupi/springbootinit/config/RedisConfig.java +++ b/src/main/java/com/yupi/springbootinit/config/RedisConfig.java @@ -1,37 +1,49 @@ package com.yupi.springbootinit.config; - import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { - @Bean(name="redisTemplate") - public RedisTemplate redisTemplate(RedisConnectionFactory factory) { - RedisTemplate template = new RedisTemplate<>(); - RedisSerializer redisSerializer = new StringRedisSerializer(); + // @Bean(name="redisTemplate") +// public RedisTemplate redisTemplate(RedisConnectionFactory factory) { +// RedisTemplate template = new RedisTemplate<>(); +// RedisSerializer redisSerializer = new StringRedisSerializer(); +// template.setConnectionFactory(factory); +// //key序列化方式 +// template.setKeySerializer(redisSerializer); +// //value序列化 +// template.setValueSerializer(redisSerializer); +// //value hashmap序列化 +// template.setHashValueSerializer(redisSerializer); +// //key haspmap序列化 +// template.setHashKeySerializer(redisSerializer); +// // +// return template; +// } + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory factory) { + RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(factory); - //key序列化方式 - template.setKeySerializer(redisSerializer); - //value序列化 - template.setValueSerializer(redisSerializer); - //value hashmap序列化 - template.setHashValueSerializer(redisSerializer); - //key haspmap序列化 - template.setHashKeySerializer(redisSerializer); - // + + // 使用 JSON 序列化器 + GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(); + + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(jsonSerializer); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(jsonSerializer); + + template.afterPropertiesSet(); return template; } - - - } diff --git a/src/main/java/com/yupi/springbootinit/config/SaTokenConfigure.java b/src/main/java/com/yupi/springbootinit/config/SaTokenConfigure.java index 477d045..9e160ab 100644 --- a/src/main/java/com/yupi/springbootinit/config/SaTokenConfigure.java +++ b/src/main/java/com/yupi/springbootinit/config/SaTokenConfigure.java @@ -47,7 +47,8 @@ public class SaTokenConfigure implements WebMvcConfigurer { "/tenant/get-id-by-name", "/user/bigbrother-doLogin", "/user/aiChat-doLogin", - "/error" + "/user/aiChat-logout", + "/error", }; } diff --git a/src/main/java/com/yupi/springbootinit/controller/UserController.java b/src/main/java/com/yupi/springbootinit/controller/UserController.java index ec2d9ea..65c5caf 100644 --- a/src/main/java/com/yupi/springbootinit/controller/UserController.java +++ b/src/main/java/com/yupi/springbootinit/controller/UserController.java @@ -57,6 +57,10 @@ public class UserController { // return ResultUtils.success(systemUsersVO); } + @PostMapping("aiChat-logout") + public BaseResponse aiChatLogout(@RequestBody SystemUsersDTO usersDTO){ + return ResultUtils.success(loginService.aiChatLogout(usersDTO)); + } // // private SystemUsers getUserByName(@RequestBody SystemUsersDTO usersDTO) { diff --git a/src/main/java/com/yupi/springbootinit/model/dto/user/SystemUsersDTO.java b/src/main/java/com/yupi/springbootinit/model/dto/user/SystemUsersDTO.java index 7948950..55f9299 100644 --- a/src/main/java/com/yupi/springbootinit/model/dto/user/SystemUsersDTO.java +++ b/src/main/java/com/yupi/springbootinit/model/dto/user/SystemUsersDTO.java @@ -29,4 +29,6 @@ public class SystemUsersDTO { * 租户编号 */ private Long tenantId; + + private Long userId; } \ No newline at end of file diff --git a/src/main/java/com/yupi/springbootinit/service/impl/LoginService.java b/src/main/java/com/yupi/springbootinit/service/impl/LoginService.java index 5b536b8..72b10ac 100644 --- a/src/main/java/com/yupi/springbootinit/service/impl/LoginService.java +++ b/src/main/java/com/yupi/springbootinit/service/impl/LoginService.java @@ -11,17 +11,51 @@ import com.yupi.springbootinit.model.enums.LoginSceneEnum; import com.yupi.springbootinit.model.vo.user.SystemUsersVO; import com.yupi.springbootinit.service.SystemUsersService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.*; +import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j @Service @RequiredArgsConstructor public class LoginService { private final SystemUsersService usersService; + @Resource + private RedisTemplate redisTemplate; + + private final Set created = ConcurrentHashMap.newKeySet(); + private final HeadersExchange userHeadersExchange; + @Resource + private RabbitAdmin rabbitAdmin; + public SystemUsersVO login(LoginSceneEnum scene, SystemUsersDTO dto) { SystemUsers user = validateUser(dto); // 校验用户名、密码、状态、租户过期 checkRole(scene, user.getId()); // 按场景做角色校验 + if (scene.equals(LoginSceneEnum.AI_CHAT)) { + redisTemplate.opsForValue().set("ai_login:"+user.getTenantId() ,true); + String queueName = "q.tenant." + user.getTenantId(); + if (created.add(String.valueOf(user.getTenantId()))) { + Queue queue = QueueBuilder.durable(queueName).build(); + rabbitAdmin.declareQueue(queue); + + Map headers = Map.of("tenantId", user.getTenantId(), "x-match", "all"); + Binding binding = BindingBuilder + .bind(queue) + .to(userHeadersExchange) // ← 传 Exchange 对象 + .whereAll(headers) + .match(); + rabbitAdmin.declareBinding(binding); + } + } Long second = usersService.getTenantExpiredTime(dto.getTenantId()); // Sa-Token 登录 StpUtil.login(user.getId(), scene.getSaMode()); @@ -53,4 +87,12 @@ public class LoginService { }; if (!pass) throw new BusinessException(ErrorCode.LOGIN_NOW_ALLOWED); } + + public Boolean aiChatLogout(SystemUsersDTO usersDTO) { + Boolean delete = redisTemplate.delete("ai_login:"+usersDTO.getTenantId()); + created.remove(String.valueOf(usersDTO.getTenantId())); + log.info("删除租户:{}登录状态:{}",usersDTO.getTenantId(),delete); + StpUtil.logout(usersDTO.getUserId()); + return true; + } } \ No newline at end of file