Files
MaliangAINovalWriter/AINovalServer/src/main/java/com/ainovel/server/config/MongoConfig.java
2025-09-10 00:07:52 +08:00

203 lines
7.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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发出非存储类型转换的警告。
}