Skip to content

Commit 74397d5

Browse files
committed
Merge remote-tracking branch 'origin/fix-codeql-scanning' into fix-codeql-scanning
# Conflicts: # app/src/main/java/com/tinyengine/it/task/DatabaseCleanupService.java # base/src/main/java/com/tinyengine/it/common/utils/SqlIdentifierValidator.java
2 parents 42662e2 + 6613573 commit 74397d5

13 files changed

Lines changed: 391 additions & 289 deletions

File tree

‎app/src/main/java/com/tinyengine/it/task/DatabaseCleanupService.java‎

Lines changed: 94 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,29 @@
2626
import java.time.LocalDateTime;
2727
import java.time.ZoneId;
2828
import java.time.format.DateTimeFormatter;
29+
import java.security.SecureRandom;
2930
import java.util.Arrays;
3031
import java.util.LinkedHashMap;
3132
import java.util.List;
3233
import java.util.Locale;
3334
import java.util.Map;
34-
import java.util.UUID;
3535
import java.util.concurrent.ConcurrentHashMap;
3636
import java.util.concurrent.atomic.AtomicInteger;
3737

38-
@SuppressWarnings({"PMD.DataflowAnomalyAnalysis", "PMD.TooManyMethods"})
39-
@NoArgsConstructor
4038
@Service
39+
@SuppressWarnings("PMD.TooManyMethods")
4140
public class DatabaseCleanupService {
4241
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseCleanupService.class);
4342
private static final DateTimeFormatter FORMATTER =
4443
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
4544
private static final int EXEC_ID_LENGTH = 8;
4645

47-
@Autowired private JdbcTemplate jdbcTemplate;
48-
49-
@Autowired private CleanupProperties cleanupProperties;
46+
private final boolean cleanupEnabled;
47+
private final boolean useTruncate;
48+
private final boolean sendWarning;
49+
private final String cronExpression;
50+
private final List<String> whitelistTables;
51+
private final JdbcTemplate jdbcTemplate;
5052

5153
private final Map<String, ExecutionStats> executionStats = new ConcurrentHashMap<>();
5254

@@ -73,10 +75,25 @@ public class DatabaseCleanupService {
7375
"t_page_history",
7476
"t_page_template");
7577

78+
@Autowired
79+
public DatabaseCleanupService(
80+
final JdbcTemplate jdbcTemplate, final CleanupProperties cleanupProperties) {
81+
this.jdbcTemplate = jdbcTemplate;
82+
cleanupEnabled = cleanupProperties.isEnabled();
83+
useTruncate = cleanupProperties.isUseTruncate();
84+
sendWarning = cleanupProperties.isSendWarning();
85+
cronExpression = cleanupProperties.getCronExpression();
86+
final List<String> configuredTables = cleanupProperties.getWhitelistTables();
87+
whitelistTables =
88+
configuredTables == null || configuredTables.isEmpty()
89+
? DEFAULT_TABLES
90+
: List.copyOf(configuredTables);
91+
}
92+
7693
/** 每天24:00自动执行清空操作 */
7794
@Scheduled(cron = "${cleanup.cron-expression:0 0 0 * * ?}")
7895
public void autoCleanupAtMidnight() {
79-
if (!cleanupProperties.isEnabled()) {
96+
if (!cleanupEnabled) {
8097
logInfo("⏸️ Clearing tasks is disabled, skipping execution");
8198
return;
8299
}
@@ -114,7 +131,7 @@ public void autoCleanupAtMidnight() {
114131
/** 每天23:55发送预警通知 */
115132
@Scheduled(cron = "0 55 23 * * ?")
116133
public void sendCleanupWarning() {
117-
if (!cleanupProperties.isEnabled() || !cleanupProperties.isSendWarning()) {
134+
if (!cleanupEnabled || !sendWarning) {
118135
return;
119136
}
120137

@@ -132,10 +149,9 @@ public void sendCleanupWarning() {
132149
public void init() {
133150
logInfo("🚀 Database auto-clear service initialization completed");
134151
logInfo("📋 Configuration table: {}", getWhitelistTables());
135-
logInfo("⏰ Execution time: {}", cleanupProperties.getCronExpression());
136-
logInfo(
137-
"🔧 Mode in use: {}", cleanupProperties.isUseTruncate() ? "TRUNCATE" : "DELETE");
138-
logInfo("✅ Service status: {}", cleanupProperties.isEnabled() ? "Enabled" : "Disabled");
152+
logInfo("⏰ Execution time: {}", cronExpression);
153+
logInfo("🔧 Mode in use: {}", useTruncate ? "TRUNCATE" : "DELETE");
154+
logInfo("✅ Service status: {}", cleanupEnabled ? "Enabled" : "Disabled");
139155
logInfo("==========================================");
140156
}
141157

@@ -145,20 +161,19 @@ public void init() {
145161
*
146162
* @return whitelist table names
147163
*/
148-
@SuppressWarnings("PMD.LawOfDemeter")
149164
public List<String> getWhitelistTables() {
150-
final List<String> tables = cleanupProperties.getWhitelistTables();
151-
return tables != null && !tables.isEmpty() ? tables : DEFAULT_TABLES;
165+
return whitelistTables;
152166
}
153-
@SuppressWarnings("PMD.LawOfDemeter")
154167
private static String createExecutionId() {
155-
final UUID uuid = UUID.randomUUID();
156-
final String fullUuid = uuid.toString();
157-
return truncateUuid(fullUuid);
158-
}
159-
160-
private static String truncateUuid(final String uuid) {
161-
return uuid.substring(0, EXEC_ID_LENGTH);
168+
final SecureRandom random = new SecureRandom();
169+
final byte[] randomBytes = new byte[(EXEC_ID_LENGTH + 1) / 2];
170+
random.nextBytes(randomBytes);
171+
final StringBuilder identifier = new StringBuilder(randomBytes.length * 2);
172+
for (final byte randomByte : randomBytes) {
173+
identifier.append(Character.forDigit((randomByte >>> 4) & 0x0F, 16));
174+
identifier.append(Character.forDigit(randomByte & 0x0F, 16));
175+
}
176+
return identifier.substring(0, EXEC_ID_LENGTH);
162177
}
163178

164179
private static String currentTime() {
@@ -172,7 +187,6 @@ private void cleanTable(
172187
final ExecutionStats stats,
173188
final CleanupSummary cleanupSummary) {
174189
try {
175-
validateTableName(tableName);
176190
if (!tableExists(tableName)) {
177191
logWarn("⚠️ Table {} does not exist, skip", tableName);
178192
stats.recordSkipped(tableName, "Table does not exist");
@@ -193,88 +207,6 @@ private void cleanTable(
193207
stats.recordFailure(tableName, exception.getMessage());
194208
}
195209
}
196-
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
197-
private long clearTable(final String tableName) {
198-
long result; // 存储最终返回值
199-
if (cleanupProperties.isUseTruncate()) {
200-
final long recordCount = getTableRecordCount(tableName);
201-
truncateTable(tableName);
202-
result = recordCount;
203-
} else {
204-
result = clearTableData(tableName);
205-
206-
}
207-
return result; // 唯一的返回语句
208-
}
209-
210-
/**
211-
* 清空表数据(DELETE方式).
212-
*
213-
* @return number of deleted rows
214-
*/
215-
private long clearTableData(final String tableName) {
216-
validateTableName(tableName);
217-
final String sql = "DELETE FROM " + tableName;
218-
return jdbcTemplate.update(sql);
219-
}
220-
221-
/** 清空表数据(TRUNCATE方式) */
222-
private void truncateTable(final String tableName) {
223-
validateTableName(tableName);
224-
final String sql = "TRUNCATE TABLE " + tableName;
225-
jdbcTemplate.execute(sql);
226-
}
227-
228-
/**
229-
* 检查表是否存在.
230-
*
231-
* @return whether the table exists
232-
*/
233-
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
234-
public boolean tableExists(final String tableName) {
235-
boolean exists = false;
236-
try {
237-
final String sql =
238-
"SELECT COUNT(*) FROM information_schema.tables "
239-
+ "WHERE table_schema = DATABASE() AND table_name = ?";
240-
final Integer count =
241-
jdbcTemplate.queryForObject(
242-
sql, Integer.class, tableName.toUpperCase(Locale.ROOT));
243-
exists = count != null && count > 0;
244-
} catch (DataAccessException | IllegalArgumentException exception) {
245-
logWarn("The checklist has failed: {}", exception.getMessage());
246-
}
247-
return exists;
248-
}
249-
/**
250-
* 获取表记录数量.
251-
*
252-
* @return record count in the table
253-
*/
254-
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
255-
public long getTableRecordCount(final String tableName) {
256-
long result = -1L;
257-
try {
258-
validateTableName(tableName);
259-
final String sql = "SELECT COUNT(*) FROM " + tableName;
260-
final Long count = jdbcTemplate.queryForObject(sql, Long.class);
261-
result = count == null ? 0L : count;
262-
} catch (DataAccessException | IllegalArgumentException exception) {
263-
logError("Table record count failed: {}", exception.getMessage());
264-
}
265-
return result;
266-
}
267-
268-
/** 验证表名安全性 */
269-
private void validateTableName(final String tableName) {
270-
if (tableName == null || tableName.isBlank()) {
271-
throw new IllegalArgumentException("Table name cannot be empty");
272-
}
273-
if (!tableName.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
274-
throw new IllegalArgumentException("Invalid table name format: " + tableName);
275-
}
276-
}
277-
278210
/**
279211
* 获取执行统计.
280212
*
@@ -386,17 +318,71 @@ public void setTotalRowsCleaned(final long totalRowsCleaned) {
386318
public Map<String, TableResult> getTableResults() {
387319
return tableResults;
388320
}
389-
@ SuppressWarnings({"PMD.DataflowAnomalyAnalysis", "PMD.LawOfDemeter"})
390321
public long getDurationSeconds() {
391322
long result = 0; // 默认值对应 startTime 或 endTime 为空的情况
392323
if (startTime != null && endTime != null) {
393324
final LocalDateTime start = LocalDateTime.parse(startTime, FORMATTER);
394325
final LocalDateTime end = LocalDateTime.parse(endTime, FORMATTER);
395326
final Duration duration = between(start, end); // 使用静态导入的方法
396-
result = duration.getSeconds(); // 单独调用,无链式
327+
result = durationInSeconds(duration);
397328
}
398329
return result; // 唯一的退出点
399330
}
331+
332+
private static long durationInSeconds(final Duration duration) {
333+
return duration.getSeconds();
334+
}
335+
}
336+
337+
private long clearTable(final String tableName) {
338+
validateTableName(tableName);
339+
long rowsCleaned;
340+
if (useTruncate) {
341+
final long recordCount = getTableRecordCount(tableName);
342+
jdbcTemplate.execute("TRUNCATE TABLE " + tableName);
343+
rowsCleaned = recordCount;
344+
} else {
345+
rowsCleaned = jdbcTemplate.update("DELETE FROM " + tableName);
346+
}
347+
return rowsCleaned;
348+
}
349+
350+
public boolean tableExists(final String tableName) {
351+
boolean exists = false;
352+
try {
353+
final String sql =
354+
"SELECT COUNT(*) FROM information_schema.tables "
355+
+ "WHERE table_schema = DATABASE() AND table_name = ?";
356+
final Integer count =
357+
jdbcTemplate.queryForObject(
358+
sql, Integer.class, tableName.toUpperCase(Locale.ROOT));
359+
exists = count != null && count > 0;
360+
} catch (DataAccessException | IllegalArgumentException exception) {
361+
logWarn("The checklist has failed: {}", exception.getMessage());
362+
}
363+
return exists;
364+
}
365+
366+
public long getTableRecordCount(final String tableName) {
367+
long count = -1L;
368+
try {
369+
validateTableName(tableName);
370+
final Long queriedCount =
371+
jdbcTemplate.queryForObject("SELECT COUNT(*) FROM " + tableName, Long.class);
372+
count = queriedCount == null ? 0L : queriedCount;
373+
} catch (DataAccessException | IllegalArgumentException exception) {
374+
logError("Table record count failed: {}", exception.getMessage());
375+
}
376+
return count;
377+
}
378+
379+
private void validateTableName(final String tableName) {
380+
if (tableName == null || tableName.isBlank()) {
381+
throw new IllegalArgumentException("Table name cannot be empty");
382+
}
383+
if (!tableName.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
384+
throw new IllegalArgumentException("Invalid table name format: " + tableName);
385+
}
400386
}
401387

402388
/** 表结果内部类 */

0 commit comments

Comments
 (0)