新增数据迁移任务到定时任务白名单;优化默认收货时间范围逻辑

This commit is contained in:
wuweihua
2026-11-11 15:34:11 +08:00
parent f82d9c667f
commit 7869205afc
14 changed files with 1251 additions and 3 deletions
@@ -0,0 +1,28 @@
package com.bms.system.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.bms.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/** 数据迁移脚本 sys_data_migration */
@Data
@EqualsAndHashCode(callSuper = true)
public class SysDataMigration extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long migrationId;
private String migrationName;
private String sqlText;
private String cronExpression;
/** 0启用调度 / 1停用 */
private String status;
private Long jobId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastRunTime;
/** 0 ok / 1 fail */
private String lastRunStatus;
private String lastRunMessage;
}
@@ -0,0 +1,28 @@
package com.bms.system.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.bms.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/** 数据迁移执行日志 sys_data_migration_log */
@Data
@EqualsAndHashCode(callSuper = true)
public class SysDataMigrationLog extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long logId;
private Long migrationId;
/** MANUAL / CRON */
private String triggerType;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
/** 0 ok / 1 fail */
private String status;
private Integer affectedRows;
private String message;
}
@@ -0,0 +1,24 @@
package com.bms.system.mapper;
import java.util.List;
import com.bms.system.domain.SysDataMigration;
import com.bms.system.domain.SysDataMigrationLog;
public interface SysDataMigrationMapper
{
SysDataMigration selectSysDataMigrationById(Long migrationId);
List<SysDataMigration> selectSysDataMigrationList(SysDataMigration query);
int insertSysDataMigration(SysDataMigration row);
int updateSysDataMigration(SysDataMigration row);
int deleteSysDataMigrationByIds(Long[] migrationIds);
int clearJobId(Long migrationId);
int insertSysDataMigrationLog(SysDataMigrationLog row);
List<SysDataMigrationLog> selectSysDataMigrationLogList(SysDataMigrationLog query);
}
@@ -0,0 +1,29 @@
package com.bms.system.migration.task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.bms.system.service.ISysDataMigrationService;
/**
* Quartz 调用目标:dataMigrationTask.executeById(1L)
*/
@Component("dataMigrationTask")
public class DataMigrationTask
{
private static final Logger log = LoggerFactory.getLogger(DataMigrationTask.class);
@Autowired
private ISysDataMigrationService migrationService;
public void executeById(Long migrationId)
{
if (migrationId == null || migrationId.longValue() <= 0L)
{
log.warn("dataMigrationTask.executeById ignored: empty id");
return;
}
migrationService.executeMigration(migrationId, "CRON", "quartz");
}
}
@@ -0,0 +1,29 @@
package com.bms.system.service;
import java.util.List;
import java.util.Map;
import com.bms.system.domain.SysDataMigration;
import com.bms.system.domain.SysDataMigrationLog;
public interface ISysDataMigrationService
{
SysDataMigration selectSysDataMigrationById(Long migrationId);
List<SysDataMigration> selectSysDataMigrationList(SysDataMigration query);
int insertSysDataMigration(SysDataMigration row);
int updateSysDataMigration(SysDataMigration row);
int deleteSysDataMigrationByIds(Long[] migrationIds);
int clearJobId(Long migrationId);
List<SysDataMigrationLog> selectSysDataMigrationLogList(SysDataMigrationLog query);
/**
* Run saved SQL. triggerType: MANUAL / CRON.
* @return summary map: status, affectedRows, message
*/
Map<String, Object> executeMigration(Long migrationId, String triggerType, String operator);
}
@@ -0,0 +1,233 @@
package com.bms.system.service.impl;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import com.bms.common.exception.ServiceException;
import com.bms.common.utils.StringUtils;
import com.bms.system.domain.SysDataMigration;
import com.bms.system.domain.SysDataMigrationLog;
import com.bms.system.mapper.SysDataMigrationMapper;
import com.bms.system.service.ISysDataMigrationService;
@Service
public class SysDataMigrationServiceImpl implements ISysDataMigrationService
{
private static final Pattern DANGEROUS = Pattern.compile(
"(?i)\\b(drop\\s+database|drop\\s+schema|drop\\s+user|create\\s+user|grant\\b|revoke\\b|shutdown\\b|"
+ "load_file\\s*\\(|into\\s+outfile|into\\s+dumpfile|benchmark\\s*\\(|sleep\\s*\\()",
Pattern.CASE_INSENSITIVE);
@Autowired
private SysDataMigrationMapper migrationMapper;
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public SysDataMigration selectSysDataMigrationById(Long migrationId)
{
return migrationMapper.selectSysDataMigrationById(migrationId);
}
@Override
public List<SysDataMigration> selectSysDataMigrationList(SysDataMigration query)
{
return migrationMapper.selectSysDataMigrationList(query);
}
@Override
public int insertSysDataMigration(SysDataMigration row)
{
validateSql(row == null ? null : row.getSqlText());
if (row.getStatus() == null || row.getStatus().isEmpty())
{
row.setStatus("1");
}
return migrationMapper.insertSysDataMigration(row);
}
@Override
public int updateSysDataMigration(SysDataMigration row)
{
if (row != null && row.getSqlText() != null)
{
validateSql(row.getSqlText());
}
return migrationMapper.updateSysDataMigration(row);
}
@Override
public int deleteSysDataMigrationByIds(Long[] migrationIds)
{
return migrationMapper.deleteSysDataMigrationByIds(migrationIds);
}
@Override
public int clearJobId(Long migrationId)
{
return migrationMapper.clearJobId(migrationId);
}
@Override
public List<SysDataMigrationLog> selectSysDataMigrationLogList(SysDataMigrationLog query)
{
return migrationMapper.selectSysDataMigrationLogList(query);
}
@Override
public Map<String, Object> executeMigration(Long migrationId, String triggerType, String operator)
{
SysDataMigration row = migrationMapper.selectSysDataMigrationById(migrationId);
if (row == null)
{
throw new ServiceException("迁移脚本不存在");
}
validateSql(row.getSqlText());
Date start = new Date();
String status = "0";
String message = "ok";
int affected = 0;
try
{
List<String> statements = splitSql(row.getSqlText());
if (statements.isEmpty())
{
throw new ServiceException("SQL 为空");
}
for (String sql : statements)
{
affected += executeOne(sql);
}
}
catch (Exception ex)
{
status = "1";
message = ex.getMessage() == null ? ex.getClass().getSimpleName() : ex.getMessage();
if (message.length() > 1900)
{
message = message.substring(0, 1900);
}
}
Date end = new Date();
SysDataMigrationLog log = new SysDataMigrationLog();
log.setMigrationId(migrationId);
log.setTriggerType(StringUtils.isEmpty(triggerType) ? "MANUAL" : triggerType);
log.setStartTime(start);
log.setEndTime(end);
log.setStatus(status);
log.setAffectedRows(Integer.valueOf(affected));
log.setMessage(message);
log.setCreateBy(operator);
migrationMapper.insertSysDataMigrationLog(log);
SysDataMigration patch = new SysDataMigration();
patch.setMigrationId(migrationId);
patch.setLastRunTime(end);
patch.setLastRunStatus(status);
patch.setLastRunMessage(message);
patch.setUpdateBy(operator);
migrationMapper.updateSysDataMigration(patch);
Map<String, Object> result = new HashMap<String, Object>();
result.put("status", status);
result.put("affectedRows", Integer.valueOf(affected));
result.put("message", message);
if ("1".equals(status))
{
throw new ServiceException(message);
}
return result;
}
private void validateSql(String sqlText)
{
if (StringUtils.isEmpty(sqlText) || StringUtils.isEmpty(sqlText.trim()))
{
throw new ServiceException("SQL 不能为空");
}
if (DANGEROUS.matcher(sqlText).find())
{
throw new ServiceException("SQL 包含禁用关键词");
}
}
private List<String> splitSql(String sqlText)
{
List<String> list = new ArrayList<String>();
StringBuilder cur = new StringBuilder();
boolean inSingle = false;
boolean inDouble = false;
for (int i = 0; i < sqlText.length(); i++)
{
char c = sqlText.charAt(i);
if (c == '\'' && !inDouble)
{
inSingle = !inSingle;
cur.append(c);
continue;
}
if (c == '"' && !inSingle)
{
inDouble = !inDouble;
cur.append(c);
continue;
}
if (c == ';' && !inSingle && !inDouble)
{
String one = cur.toString().trim();
if (!one.isEmpty())
{
list.add(one);
}
cur.setLength(0);
continue;
}
cur.append(c);
}
String last = cur.toString().trim();
if (!last.isEmpty())
{
list.add(last);
}
return list;
}
private int executeOne(String sql)
{
return jdbcTemplate.execute((Connection conn) -> {
try (Statement st = conn.createStatement())
{
boolean hasResult = st.execute(sql);
if (hasResult)
{
try (ResultSet rs = st.getResultSet())
{
int count = 0;
if (rs != null)
{
while (rs.next())
{
count++;
}
}
return Integer.valueOf(count);
}
}
int updated = st.getUpdateCount();
return Integer.valueOf(updated < 0 ? 0 : updated);
}
}).intValue();
}
}
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bms.system.mapper.SysDataMigrationMapper">
<resultMap type="com.bms.system.domain.SysDataMigration" id="SysDataMigrationResult">
<id property="migrationId" column="migration_id" />
<result property="migrationName" column="migration_name" />
<result property="sqlText" column="sql_text" />
<result property="cronExpression" column="cron_expression" />
<result property="status" column="status" />
<result property="jobId" column="job_id" />
<result property="lastRunTime" column="last_run_time" />
<result property="lastRunStatus" column="last_run_status" />
<result property="lastRunMessage" column="last_run_message" />
<result property="remark" column="remark" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
</resultMap>
<resultMap type="com.bms.system.domain.SysDataMigrationLog" id="SysDataMigrationLogResult">
<id property="logId" column="log_id" />
<result property="migrationId" column="migration_id" />
<result property="triggerType" column="trigger_type" />
<result property="startTime" column="start_time" />
<result property="endTime" column="end_time" />
<result property="status" column="status" />
<result property="affectedRows" column="affected_rows" />
<result property="message" column="message" />
<result property="createBy" column="create_by" />
</resultMap>
<sql id="selectVo">
select migration_id, migration_name, sql_text, cron_expression, status, job_id,
last_run_time, last_run_status, last_run_message, remark,
create_by, create_time, update_by, update_time
from sys_data_migration
</sql>
<select id="selectSysDataMigrationById" resultMap="SysDataMigrationResult">
<include refid="selectVo"/>
where migration_id = #{migrationId}
</select>
<select id="selectSysDataMigrationList" parameterType="com.bms.system.domain.SysDataMigration" resultMap="SysDataMigrationResult">
<include refid="selectVo"/>
<where>
<if test="migrationName != null and migrationName != ''">
and migration_name like concat('%', #{migrationName}, '%')
</if>
<if test="status != null and status != ''">and status = #{status}</if>
</where>
order by migration_id desc
</select>
<insert id="insertSysDataMigration" parameterType="com.bms.system.domain.SysDataMigration" useGeneratedKeys="true" keyProperty="migrationId">
insert into sys_data_migration (
migration_name, sql_text, cron_expression, status, job_id,
last_run_time, last_run_status, last_run_message, remark, create_by, create_time
) values (
#{migrationName}, #{sqlText}, #{cronExpression}, ifnull(#{status}, '1'), #{jobId},
#{lastRunTime}, #{lastRunStatus}, #{lastRunMessage}, #{remark}, #{createBy}, sysdate()
)
</insert>
<update id="updateSysDataMigration" parameterType="com.bms.system.domain.SysDataMigration">
update sys_data_migration
<set>
<if test="migrationName != null and migrationName != ''">migration_name = #{migrationName},</if>
<if test="sqlText != null">sql_text = #{sqlText},</if>
<if test="cronExpression != null">cron_expression = #{cronExpression},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="jobId != null">job_id = #{jobId},</if>
<if test="lastRunTime != null">last_run_time = #{lastRunTime},</if>
<if test="lastRunStatus != null">last_run_status = #{lastRunStatus},</if>
<if test="lastRunMessage != null">last_run_message = #{lastRunMessage},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
update_time = sysdate()
</set>
where migration_id = #{migrationId}
</update>
<delete id="deleteSysDataMigrationByIds">
delete from sys_data_migration where migration_id in
<foreach collection="array" item="id" open="(" separator="," close=")">#{id}</foreach>
</delete>
<update id="clearJobId">
update sys_data_migration set job_id = null, update_time = sysdate() where migration_id = #{migrationId}
</update>
<insert id="insertSysDataMigrationLog" parameterType="com.bms.system.domain.SysDataMigrationLog" useGeneratedKeys="true" keyProperty="logId">
insert into sys_data_migration_log (
migration_id, trigger_type, start_time, end_time, status, affected_rows, message, create_by
) values (
#{migrationId}, #{triggerType}, #{startTime}, #{endTime}, #{status},
ifnull(#{affectedRows}, 0), #{message}, #{createBy}
)
</insert>
<select id="selectSysDataMigrationLogList" parameterType="com.bms.system.domain.SysDataMigrationLog" resultMap="SysDataMigrationLogResult">
select log_id, migration_id, trigger_type, start_time, end_time, status, affected_rows, message, create_by
from sys_data_migration_log
<where>
<if test="migrationId != null">and migration_id = #{migrationId}</if>
<if test="status != null and status != ''">and status = #{status}</if>
<if test="triggerType != null and triggerType != ''">and trigger_type = #{triggerType}</if>
</where>
order by log_id desc
</select>
</mapper>