Compare commits
3 Commits
5ea65a8d25
...
a44651dd2f
| Author | SHA1 | Date | |
|---|---|---|---|
| a44651dd2f | |||
| 43cbd262ea | |||
| 07a4142818 |
4
pom.xml
4
pom.xml
@@ -126,6 +126,10 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara.x-file-storage</groupId>
|
<groupId>org.dromara.x-file-storage</groupId>
|
||||||
<artifactId>x-file-storage-spring</artifactId>
|
<artifactId>x-file-storage-spring</artifactId>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,37 +1,49 @@
|
|||||||
package com.yupi.springbootinit.config;
|
package com.yupi.springbootinit.config;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
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;
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class RedisConfig {
|
public class RedisConfig {
|
||||||
|
|
||||||
|
|
||||||
@Bean(name="redisTemplate")
|
// @Bean(name="redisTemplate")
|
||||||
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
|
// public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
|
||||||
RedisTemplate<String, String> template = new RedisTemplate<>();
|
// RedisTemplate<String, String> template = new RedisTemplate<>();
|
||||||
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
|
// RedisSerializer<String> 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<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||||
|
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||||
template.setConnectionFactory(factory);
|
template.setConnectionFactory(factory);
|
||||||
//key序列化方式
|
|
||||||
template.setKeySerializer(redisSerializer);
|
// 使用 JSON 序列化器
|
||||||
//value序列化
|
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
|
||||||
template.setValueSerializer(redisSerializer);
|
|
||||||
//value hashmap序列化
|
template.setKeySerializer(new StringRedisSerializer());
|
||||||
template.setHashValueSerializer(redisSerializer);
|
template.setValueSerializer(jsonSerializer);
|
||||||
//key haspmap序列化
|
template.setHashKeySerializer(new StringRedisSerializer());
|
||||||
template.setHashKeySerializer(redisSerializer);
|
template.setHashValueSerializer(jsonSerializer);
|
||||||
//
|
|
||||||
|
template.afterPropertiesSet();
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
|||||||
"/tenant/get-id-by-name",
|
"/tenant/get-id-by-name",
|
||||||
"/user/bigbrother-doLogin",
|
"/user/bigbrother-doLogin",
|
||||||
"/user/aiChat-doLogin",
|
"/user/aiChat-doLogin",
|
||||||
"/error"
|
"/user/aiChat-logout",
|
||||||
|
"/error",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ public class UserController {
|
|||||||
// return ResultUtils.success(systemUsersVO);
|
// return ResultUtils.success(systemUsersVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("aiChat-logout")
|
||||||
|
public BaseResponse<Boolean> aiChatLogout(@RequestBody SystemUsersDTO usersDTO){
|
||||||
|
return ResultUtils.success(loginService.aiChatLogout(usersDTO));
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// private SystemUsers getUserByName(@RequestBody SystemUsersDTO usersDTO) {
|
// private SystemUsers getUserByName(@RequestBody SystemUsersDTO usersDTO) {
|
||||||
|
|||||||
@@ -29,4 +29,6 @@ public class SystemUsersDTO {
|
|||||||
* 租户编号
|
* 租户编号
|
||||||
*/
|
*/
|
||||||
private Long tenantId;
|
private Long tenantId;
|
||||||
|
|
||||||
|
private Long userId;
|
||||||
}
|
}
|
||||||
@@ -10,18 +10,56 @@ import com.yupi.springbootinit.model.enums.CommonStatusEnum;
|
|||||||
import com.yupi.springbootinit.model.enums.LoginSceneEnum;
|
import com.yupi.springbootinit.model.enums.LoginSceneEnum;
|
||||||
import com.yupi.springbootinit.model.vo.user.SystemUsersVO;
|
import com.yupi.springbootinit.model.vo.user.SystemUsersVO;
|
||||||
import com.yupi.springbootinit.service.SystemUsersService;
|
import com.yupi.springbootinit.service.SystemUsersService;
|
||||||
|
import com.yupi.springbootinit.utils.RedisUtils;
|
||||||
import lombok.RequiredArgsConstructor;
|
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 org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LoginService {
|
public class LoginService {
|
||||||
|
|
||||||
private final SystemUsersService usersService;
|
private final SystemUsersService usersService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String,Boolean> redisTemplate;
|
||||||
|
|
||||||
|
private final Set<String> created = ConcurrentHashMap.newKeySet();
|
||||||
|
private final HeadersExchange userHeadersExchange;
|
||||||
|
@Resource
|
||||||
|
private RabbitAdmin rabbitAdmin;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisUtils redisUtils;
|
||||||
|
|
||||||
public SystemUsersVO login(LoginSceneEnum scene, SystemUsersDTO dto) {
|
public SystemUsersVO login(LoginSceneEnum scene, SystemUsersDTO dto) {
|
||||||
SystemUsers user = validateUser(dto); // 校验用户名、密码、状态、租户过期
|
SystemUsers user = validateUser(dto); // 校验用户名、密码、状态、租户过期
|
||||||
checkRole(scene, user.getId()); // 按场景做角色校验
|
checkRole(scene, user.getId()); // 按场景做角色校验
|
||||||
|
if (scene.equals(LoginSceneEnum.AI_CHAT)) {
|
||||||
|
redisTemplate.opsForValue().set("ai_login:"+user.getTenantId()+":"+user.getId(),true);
|
||||||
|
String queueName = "q.tenant." + user.getTenantId();
|
||||||
|
if (created.add(String.valueOf(user.getTenantId()))) {
|
||||||
|
Queue queue = QueueBuilder.durable(queueName).build();
|
||||||
|
rabbitAdmin.declareQueue(queue);
|
||||||
|
|
||||||
|
Map<String, Object> 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());
|
Long second = usersService.getTenantExpiredTime(dto.getTenantId());
|
||||||
// Sa-Token 登录
|
// Sa-Token 登录
|
||||||
StpUtil.login(user.getId(), scene.getSaMode());
|
StpUtil.login(user.getId(), scene.getSaMode());
|
||||||
@@ -53,4 +91,16 @@ public class LoginService {
|
|||||||
};
|
};
|
||||||
if (!pass) throw new BusinessException(ErrorCode.LOGIN_NOW_ALLOWED);
|
if (!pass) throw new BusinessException(ErrorCode.LOGIN_NOW_ALLOWED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean aiChatLogout(SystemUsersDTO usersDTO) {
|
||||||
|
Boolean delete = redisTemplate.delete("ai_login:"+usersDTO.getTenantId()+":"+usersDTO.getUserId());
|
||||||
|
StpUtil.logout(usersDTO.getUserId());
|
||||||
|
log.info("删除租户:{}登录状态:{}",usersDTO.getTenantId(),delete);
|
||||||
|
if (!redisUtils.hasKeyByPrefix("ai_login:" + usersDTO.getTenantId())) {
|
||||||
|
created.remove(String.valueOf(usersDTO.getTenantId()));
|
||||||
|
boolean b = rabbitAdmin.deleteQueue("q.tenant." + usersDTO.getTenantId());
|
||||||
|
log.info("删除租户:{}队列删除状态:{}",usersDTO.getTenantId(),b);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
40
src/main/java/com/yupi/springbootinit/utils/RedisUtils.java
Normal file
40
src/main/java/com/yupi/springbootinit/utils/RedisUtils.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package com.yupi.springbootinit.utils;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author: ziin
|
||||||
|
* @date: 2025/8/27 20:35
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.springframework.data.redis.core.*;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class RedisUtils {
|
||||||
|
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String,Object> redisTemplate;
|
||||||
|
|
||||||
|
public boolean hasAiLoginKeys(String prefix) {
|
||||||
|
Set<String> keys = redisTemplate.keys(prefix); // 获取匹配的键集合
|
||||||
|
return !keys.isEmpty(); // 如果有键匹配,返回true,否则返回false
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasKeyByPrefix(String prefix) {
|
||||||
|
return Boolean.TRUE.equals(
|
||||||
|
redisTemplate.execute((RedisCallback<Boolean>) conn -> {
|
||||||
|
try (Cursor<byte[]> cursor = conn.scan(
|
||||||
|
ScanOptions.scanOptions()
|
||||||
|
.match(prefix + ":*")
|
||||||
|
.count(100) // 每次返回条数,可调整
|
||||||
|
.build())) {
|
||||||
|
return cursor.hasNext(); // 只要存在一条就返回 true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user