203 lines
7.6 KiB
Java
203 lines
7.6 KiB
Java
|
||
|
||
package com.ainovel.server.config;
|
||
|
||
import org.slf4j.Logger;
|
||
import org.slf4j.LoggerFactory;
|
||
import org.springframework.beans.factory.annotation.Value;
|
||
import org.springframework.context.annotation.Bean;
|
||
import org.springframework.context.annotation.Configuration;
|
||
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
|
||
import org.springframework.data.mongodb.ReactiveMongoTransactionManager;
|
||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
|
||
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
|
||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
|
||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||
|
||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||
import com.mongodb.ConnectionString;
|
||
import com.mongodb.MongoClientSettings;
|
||
import com.mongodb.reactivestreams.client.MongoClient;
|
||
import com.mongodb.reactivestreams.client.MongoClients;
|
||
import org.springframework.core.convert.converter.Converter;
|
||
import org.springframework.data.convert.ReadingConverter;
|
||
import org.springframework.data.convert.WritingConverter;
|
||
|
||
import java.time.Instant;
|
||
import java.util.ArrayList;
|
||
import java.util.Date;
|
||
import java.util.List;
|
||
// import java.util.Map; // 移除通用Map转换后不再需要
|
||
|
||
/**
|
||
* MongoDB配置类
|
||
* 配置MongoDB连接、响应式支持、日志和统计功能
|
||
*/
|
||
@Configuration
|
||
@EnableReactiveMongoRepositories(basePackages = "com.ainovel.server.repository")
|
||
@EnableMongoRepositories(basePackages = "com.ainovel.server.repository")
|
||
public class MongoConfig {
|
||
|
||
private static final Logger logger = LoggerFactory.getLogger(MongoConfig.class);
|
||
|
||
@Value("${spring.data.mongodb.uri}")
|
||
private String mongoUri;
|
||
|
||
@Value("${spring.data.mongodb.database}")
|
||
private String database;
|
||
|
||
// 注意:这里不注入全局 ObjectMapper 以避免误用于通用 Map 转换
|
||
public MongoConfig() {}
|
||
|
||
/**
|
||
* 创建MongoDB事件监听器,用于记录MongoDB操作日志
|
||
* 注释掉以减少日志输出
|
||
*/
|
||
// @Bean
|
||
// public LoggingEventListener mongoEventListener() {
|
||
// return new LoggingEventListener();
|
||
// }
|
||
|
||
/**
|
||
* 创建MongoDB映射调试监听器
|
||
* 注释掉以减少日志输出
|
||
*/
|
||
// @Bean
|
||
// public AbstractMongoEventListener<Object> mongoMappingDebugListener() {
|
||
// return new AbstractMongoEventListener<Object>() {
|
||
// @Override
|
||
// public void onAfterLoad(AfterLoadEvent<Object> event) {
|
||
// if (logger.isTraceEnabled()) {
|
||
// logger.trace("📥 MongoDB加载文档: collection={}, document={}",
|
||
// event.getCollectionName(), event.getDocument().keySet());
|
||
// }
|
||
// }
|
||
// };
|
||
// }
|
||
|
||
/**
|
||
* 自定义ReactiveMongoTemplate,添加查询统计和日志功能
|
||
* @param factory MongoDB数据库工厂
|
||
* @param mappingMongoConverter 自定义的映射转换器(包含点号替换配置)
|
||
* @return 自定义的ReactiveMongoTemplate
|
||
*/
|
||
@Bean
|
||
public ReactiveMongoTemplate reactiveMongoTemplate(ReactiveMongoDatabaseFactory factory,
|
||
MappingMongoConverter mappingMongoConverter) {
|
||
// 使用构造函数直接传入自定义的MappingMongoConverter
|
||
ReactiveMongoTemplate template = new ReactiveMongoTemplate(factory, mappingMongoConverter);
|
||
|
||
// 启用日志记录
|
||
logger.info("✅ 已配置ReactiveMongoTemplate,使用自定义MappingMongoConverter(支持点号替换)");
|
||
return template;
|
||
}
|
||
|
||
/**
|
||
* 创建MongoDB客户端,添加性能监控
|
||
* @return MongoDB客户端
|
||
*/
|
||
@Bean
|
||
public MongoClient reactiveMongoClient() {
|
||
ConnectionString connectionString = new ConnectionString(mongoUri);
|
||
|
||
MongoClientSettings settings = MongoClientSettings.builder()
|
||
.applyConnectionString(connectionString)
|
||
.applicationName("AINovalWriter")
|
||
.build();
|
||
|
||
logger.info("创建MongoDB客户端,连接到: {}", database);
|
||
return MongoClients.create(settings);
|
||
}
|
||
|
||
/**
|
||
* 创建MongoDB数据库工厂
|
||
* @param mongoClient MongoDB客户端
|
||
* @return MongoDB数据库工厂
|
||
*/
|
||
@Bean
|
||
public ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory(MongoClient mongoClient) {
|
||
return new SimpleReactiveMongoDatabaseFactory(mongoClient, database);
|
||
}
|
||
|
||
/**
|
||
* 创建MongoDB事务管理器
|
||
* @param dbFactory MongoDB数据库工厂
|
||
* @return MongoDB事务管理器
|
||
*/
|
||
@Bean
|
||
public ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory dbFactory) {
|
||
return new ReactiveMongoTransactionManager(dbFactory);
|
||
}
|
||
|
||
/**
|
||
* 配置自定义MongoDB转换器
|
||
* @return 自定义转换器配置
|
||
*/
|
||
@Bean
|
||
public MongoCustomConversions mongoCustomConversions(SafeMapConverter safeMapConverter) {
|
||
List<Converter<?, ?>> converters = new ArrayList<>();
|
||
|
||
// 日期/时间转换器
|
||
converters.add(new DateToInstantConverter());
|
||
converters.add(new InstantToDateConverter());
|
||
|
||
// 仅保留安全的Map读取与时间类型转换,避免过于宽泛的 Map<->Object 转换导致的Spring Data WARN
|
||
|
||
// 安全的Map转换器 - 处理类型不匹配问题
|
||
converters.add(safeMapConverter);
|
||
|
||
logger.info("MongoDB自定义转换器配置完成,总计 {} 个转换器", converters.size());
|
||
|
||
return new MongoCustomConversions(converters);
|
||
}
|
||
|
||
/**
|
||
* 配置专门的MongoDB ObjectMapper来处理序列化/反序列化
|
||
* 确保与JsonCreator注解配合工作,解决复杂嵌套对象映射问题
|
||
*/
|
||
@Bean("mongoObjectMapper")
|
||
public ObjectMapper mongoObjectMapper() {
|
||
ObjectMapper mapper = new ObjectMapper();
|
||
|
||
// 注册JavaTime模块
|
||
mapper.registerModule(new JavaTimeModule());
|
||
|
||
// 配置反序列化行为
|
||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||
mapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
|
||
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
|
||
|
||
logger.info("MongoDB ObjectMapper配置完成,支持JsonCreator构造函数映射");
|
||
|
||
return mapper;
|
||
}
|
||
|
||
/**
|
||
* Date到Instant的转换器
|
||
*/
|
||
@ReadingConverter
|
||
public static class DateToInstantConverter implements Converter<Date, Instant> {
|
||
@Override
|
||
public Instant convert(Date source) {
|
||
return source == null ? null : source.toInstant();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Instant到Date的转换器
|
||
*/
|
||
@WritingConverter
|
||
public static class InstantToDateConverter implements Converter<Instant, Date> {
|
||
@Override
|
||
public Date convert(Instant source) {
|
||
return source == null ? null : Date.from(source);
|
||
}
|
||
}
|
||
|
||
// 注意:通用的 Map<->Object 转换改由业务层的 TaskConversionConfig 控制,
|
||
// 避免在全局转换器中过于宽泛,导致Spring Data发出非存储类型转换的警告。
|
||
} |