2626import java .time .LocalDateTime ;
2727import java .time .ZoneId ;
2828import java .time .format .DateTimeFormatter ;
29+ import java .security .SecureRandom ;
2930import java .util .Arrays ;
3031import java .util .LinkedHashMap ;
3132import java .util .List ;
3233import java .util .Locale ;
3334import java .util .Map ;
34- import java .util .UUID ;
3535import java .util .concurrent .ConcurrentHashMap ;
3636import java .util .concurrent .atomic .AtomicInteger ;
3737
38- @ SuppressWarnings ({"PMD.DataflowAnomalyAnalysis" , "PMD.TooManyMethods" })
39- @ NoArgsConstructor
4038@ Service
39+ @ SuppressWarnings ("PMD.TooManyMethods" )
4140public 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