feat: 启动时检查数据库连接与AI评估表字符集,Druid固定characterEncoding防按平台默认GBK写库

main
chenyuan 2 days ago
parent 247669f648
commit 179d18e754

@ -0,0 +1,69 @@
package com.sztzjy.linkCommerce.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 启动时检查数据库连接和 AI 评估原始响应列的字符集。
* 字符集不是 utf8mb4 时,写入中文会报 "Incorrect string value";
* 这里只记录告警方便排查,不阻止应用启动。
*/
@Slf4j
@Component
public class DatabaseCharsetChecker {
private static final String SESSION_SQL =
"SELECT @@character_set_client AS client, @@character_set_connection AS conn";
private static final String COLUMN_SQL =
"SELECT CHARACTER_SET_NAME FROM information_schema.COLUMNS "
+ "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_training_evaluation' "
+ "AND COLUMN_NAME = 'assessment_raw_response'";
private final JdbcTemplate jdbcTemplate;
public DatabaseCharsetChecker(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@PostConstruct
public void check() {
try {
List<String> problems = findProblems();
if (problems.isEmpty()) {
log.info("Database charset check passed: connection and AI evaluation columns use utf8mb4");
return;
}
for (String problem : problems) {
log.warn("Database charset check: {}", problem);
}
} catch (RuntimeException e) {
log.warn("Database charset check skipped: {}", e.getMessage());
}
}
List<String> findProblems() {
List<String> problems = new ArrayList<String>();
Map<String, Object> session = jdbcTemplate.queryForMap(SESSION_SQL);
String client = String.valueOf(session.get("client"));
String connection = String.valueOf(session.get("conn"));
if (!"utf8mb4".equalsIgnoreCase(client)) {
problems.add("character_set_client=" + client + ", expected utf8mb4; Chinese text may fail to save");
}
if (!"utf8mb4".equalsIgnoreCase(connection)) {
problems.add("character_set_connection=" + connection + ", expected utf8mb4; Chinese text may fail to save");
}
List<String> columnCharsets = jdbcTemplate.queryForList(COLUMN_SQL, String.class);
for (String charset : columnCharsets) {
if (charset != null && !"utf8mb4".equalsIgnoreCase(charset)) {
problems.add("ai_training_evaluation.assessment_raw_response charset=" + charset
+ ", expected utf8mb4; AI raw responses may fail to save");
}
}
return problems;
}
}

@ -63,6 +63,9 @@ spring:
validation-query: SELECT 1 validation-query: SELECT 1
validation-query-timeout: 3 validation-query-timeout: 3
keep-alive: true keep-alive: true
# 固定驱动端编码:这里的参数优先于连接串,避免生产环境私有配置里的连接串
# 缺少或写错 characterEncoding 时,驱动按平台默认编码(GBK)发送中文
connection-properties: characterEncoding=utf8;connectionCollation=utf8mb4_unicode_ci
connection-init-sqls: connection-init-sqls:
- SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci - SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci
# 配置监控统计 # 配置监控统计

@ -0,0 +1,75 @@
package com.sztzjy.linkCommerce.config;
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class DatabaseCharsetCheckerTest {
@Test
void reportsNothingWhenConnectionAndColumnAreUtf8mb4() {
DatabaseCharsetChecker checker = checker("utf8mb4", "utf8mb4", Collections.singletonList("utf8mb4"));
assertTrue(checker.findProblems().isEmpty());
}
@Test
void reportsConnectionCharsetThatIsNotUtf8mb4() {
DatabaseCharsetChecker checker = checker("gbk", "gbk", Collections.singletonList("utf8mb4"));
List<String> problems = checker.findProblems();
assertEquals(2, problems.size());
assertTrue(problems.get(0).contains("character_set_client=gbk"));
assertTrue(problems.get(1).contains("character_set_connection=gbk"));
}
@Test
void reportsRawResponseColumnThatIsNotUtf8mb4() {
DatabaseCharsetChecker checker = checker("utf8mb4", "utf8mb4", Collections.singletonList("latin1"));
List<String> problems = checker.findProblems();
assertEquals(1, problems.size());
assertTrue(problems.get(0).contains("assessment_raw_response"));
assertTrue(problems.get(0).contains("latin1"));
}
@Test
void skipsColumnCheckWhenTableDoesNotExistYet() {
DatabaseCharsetChecker checker = checker("utf8mb4", "utf8mb4", Collections.<String>emptyList());
assertTrue(checker.findProblems().isEmpty());
}
@Test
void neverBlocksStartupWhenDatabaseIsUnavailable() {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
when(jdbcTemplate.queryForMap(anyString())).thenThrow(new DataAccessResourceFailureException("db down"));
assertDoesNotThrow(() -> new DatabaseCharsetChecker(jdbcTemplate).check());
}
private static DatabaseCharsetChecker checker(String client, String connection, List<String> columnCharsets) {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
Map<String, Object> session = new HashMap<String, Object>();
session.put("client", client);
session.put("conn", connection);
when(jdbcTemplate.queryForMap(anyString())).thenReturn(session);
when(jdbcTemplate.queryForList(anyString(), org.mockito.ArgumentMatchers.<Class<String>>any()))
.thenReturn(columnCharsets);
return new DatabaseCharsetChecker(jdbcTemplate);
}
}
Loading…
Cancel
Save