This commit is contained in:
luojiaquan
2026-10-19 16:03:36 +08:00
13 changed files with 636 additions and 25 deletions
@@ -77,6 +77,8 @@ public class ArBill extends BaseEntity {
private String confirmedBy; private String confirmedBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date confirmedTime; private Date confirmedTime;
/** 创建账单的用户主键。 */
private Long createUserId;
/** /**
* 账单明细;详情接口分页查询时通常不直接填充该集合。 * 账单明细;详情接口分页查询时通常不直接填充该集合。
@@ -46,6 +46,8 @@ public class ArBillExport implements Serializable {
* 导出状态:PROCESSING生成中、SUCCESS成功可下载、FAILED失败。 * 导出状态:PROCESSING生成中、SUCCESS成功可下载、FAILED失败。
*/ */
private String exportStatus; private String exportStatus;
/** 发起导出的用户主键。 */
private Long exportUserId;
private String exportBy; private String exportBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date exportTime; private Date exportTime;
@@ -75,6 +75,8 @@ public class ArBillGenerateTask implements Serializable {
* 发起任务的用户。 * 发起任务的用户。
*/ */
private String createBy; private String createBy;
/** 创建任务的用户主键。 */
private Long createUserId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime; private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -33,7 +33,7 @@ public class ArBillLog implements Serializable {
private String toStatus; private String toStatus;
private String beforeData; private String beforeData;
private String afterData; private String afterData;
private String operatorId; private Long operatorId;
private String operatorName; private String operatorName;
/** /**
* 操作来源:WEB网页端、API接口、JOB定时任务、IMPORT数据导入。 * 操作来源:WEB网页端、API接口、JOB定时任务、IMPORT数据导入。
@@ -12,5 +12,5 @@ public interface IArBillLogService {
void record(ArBillLog log); void record(ArBillLog log);
void recordFailure(Long arBillId, ArLogObjectType objectType, String objectId, ArLogOperationType operationType, void recordFailure(Long arBillId, ArLogObjectType objectType, String objectId, ArLogOperationType operationType,
String fromStatus, String message, String operator, String requestId); String fromStatus, String message, Long operatorId, String operatorName, String requestId);
} }
@@ -26,11 +26,11 @@ import com.bms.ar.mapper.ArBillMapper;
import com.bms.ar.service.IArBalanceWriteoffService; import com.bms.ar.service.IArBalanceWriteoffService;
import com.bms.ar.service.IArBillLogService; import com.bms.ar.service.IArBillLogService;
import com.bms.common.exception.ServiceException; import com.bms.common.exception.ServiceException;
import com.bms.common.utils.SecurityUtils;
import com.bms.customer.customermoney.domain.CustomerReceipt; import com.bms.customer.customermoney.domain.CustomerReceipt;
import com.bms.customer.customermoney.domain.ReceiptWriteoffPlan; import com.bms.customer.customermoney.domain.ReceiptWriteoffPlan;
import com.bms.customer.customermoney.mapper.CustomerMoneyMapper; import com.bms.customer.customermoney.mapper.CustomerMoneyMapper;
import com.bms.customer.customermoney.mapper.ReceiptWriteoffPlanMapper; import com.bms.customer.customermoney.mapper.ReceiptWriteoffPlanMapper;
import com.bms.common.utils.SecurityUtils;
import com.bms.common.utils.uuid.IdUtils; import com.bms.common.utils.uuid.IdUtils;
/** /**
@@ -286,7 +286,7 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService {
log.setOperationDesc(desc); log.setOperationDesc(desc);
log.setFromStatus(fromStatus); log.setFromStatus(fromStatus);
log.setToStatus(toStatus); log.setToStatus(toStatus);
log.setOperatorId(operator); log.setOperatorId(SecurityUtils.getUserId());
log.setOperatorName(operator); log.setOperatorName(operator);
log.setOperationSource(ArLogOperationSource.WEB.getCode()); log.setOperationSource(ArLogOperationSource.WEB.getCode());
log.setRequestId(requestId); log.setRequestId(requestId);
@@ -36,7 +36,7 @@ public class ArBillLogServiceImpl implements IArBillLogService {
@Override @Override
@Transactional(propagation = Propagation.REQUIRES_NEW) @Transactional(propagation = Propagation.REQUIRES_NEW)
public void recordFailure(Long arBillId, ArLogObjectType objectType, String objectId, ArLogOperationType operationType, public void recordFailure(Long arBillId, ArLogObjectType objectType, String objectId, ArLogOperationType operationType,
String fromStatus, String message, String operator, String requestId) { String fromStatus, String message, Long operatorId, String operatorName, String requestId) {
ArBillLog log = new ArBillLog(); ArBillLog log = new ArBillLog();
log.setArBillId(arBillId); log.setArBillId(arBillId);
log.setObjectType(objectType.getCode()); log.setObjectType(objectType.getCode());
@@ -44,8 +44,8 @@ public class ArBillLogServiceImpl implements IArBillLogService {
log.setOperationType(operationType.getCode()); log.setOperationType(operationType.getCode());
log.setOperationDesc(message); log.setOperationDesc(message);
log.setFromStatus(fromStatus); log.setFromStatus(fromStatus);
log.setOperatorId(operator); log.setOperatorId(operatorId);
log.setOperatorName(operator); log.setOperatorName(operatorName);
log.setOperationSource(ArLogOperationSource.WEB.getCode()); log.setOperationSource(ArLogOperationSource.WEB.getCode());
log.setRequestId(requestId == null ? IdUtils.fastSimpleUUID() : requestId); log.setRequestId(requestId == null ? IdUtils.fastSimpleUUID() : requestId);
log.setResult(ArLogResult.FAILED.getCode()); log.setResult(ArLogResult.FAILED.getCode());
@@ -81,6 +81,7 @@ import com.bms.ar.domain.vo.ArBillQuery;
import com.bms.common.config.BmsConfig; import com.bms.common.config.BmsConfig;
import com.bms.common.exception.ServiceException; import com.bms.common.exception.ServiceException;
import com.bms.common.utils.DateUtils; import com.bms.common.utils.DateUtils;
import com.bms.common.utils.SecurityUtils;
import com.bms.common.utils.StringUtils; import com.bms.common.utils.StringUtils;
import com.bms.common.utils.file.FileUtils; import com.bms.common.utils.file.FileUtils;
import com.bms.common.utils.file.OssStorage; import com.bms.common.utils.file.OssStorage;
@@ -326,6 +327,7 @@ public class ArBillServiceImpl implements IArBillService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ArBillGenerateTask generateBill(ArBillGenerateRequest request, String operator, String requestId) { public ArBillGenerateTask generateBill(ArBillGenerateRequest request, String operator, String requestId) {
long startedAt = System.nanoTime(); long startedAt = System.nanoTime();
Long createUserId = SecurityUtils.getUserId();
validatePeriod(request); validatePeriod(request);
ArBillGenerateTask existed = generateTaskMapper.selectByRequestToken(requestId); ArBillGenerateTask existed = generateTaskMapper.selectByRequestToken(requestId);
if (existed != null) { if (existed != null) {
@@ -400,6 +402,7 @@ public class ArBillServiceImpl implements IArBillService {
bill.setBankName(bankAccount.getBankName()); bill.setBankName(bankAccount.getBankName());
bill.setRemark(request.getRemark()); bill.setRemark(request.getRemark());
bill.setCreateBy(operator); bill.setCreateBy(operator);
bill.setCreateUserId(createUserId);
bill.setUpdateBy(operator); bill.setUpdateBy(operator);
billMapper.insertArBill(bill); billMapper.insertArBill(bill);
@@ -417,6 +420,7 @@ public class ArBillServiceImpl implements IArBillService {
task.setStatus(ArBillGenerateTaskStatus.WAITING); task.setStatus(ArBillGenerateTaskStatus.WAITING);
task.setRequestToken(requestId); task.setRequestToken(requestId);
task.setCreateBy(operator); task.setCreateBy(operator);
task.setCreateUserId(createUserId);
generateTaskMapper.insert(task); generateTaskMapper.insert(task);
eventPublisher.publishEvent(new ArBillGenerateTaskEvent(task.getTaskId())); eventPublisher.publishEvent(new ArBillGenerateTaskEvent(task.getTaskId()));
log.info("应收账单生成任务已创建,operator={}, requestId={}, customerId={}, billPeriodEnd={}, selectionMode={}, selectedCount={}, excludedCount={}, taskId={}, status={}, costMs={}", log.info("应收账单生成任务已创建,operator={}, requestId={}, customerId={}, billPeriodEnd={}, selectionMode={}, selectedCount={}, excludedCount={}, taskId={}, status={}, costMs={}",
@@ -798,7 +802,7 @@ public class ArBillServiceImpl implements IArBillService {
try { try {
logService.recordFailure(task.getArBillId(), ArLogObjectType.BILL, logService.recordFailure(task.getArBillId(), ArLogObjectType.BILL,
String.valueOf(task.getTaskId()), ArLogOperationType.CREATE, ArBillStatus.DRAFT.getCode(), String.valueOf(task.getTaskId()), ArLogOperationType.CREATE, ArBillStatus.DRAFT.getCode(),
message, operator, requestId); message, task.getCreateUserId(), operator, requestId);
} catch (Exception logException) { } catch (Exception logException) {
log.warn("账单生成失败日志写入失败,但任务清理已完成,taskId={}", task.getTaskId(), logException); log.warn("账单生成失败日志写入失败,但任务清理已完成,taskId={}", task.getTaskId(), logException);
} }
@@ -851,7 +855,7 @@ public class ArBillServiceImpl implements IArBillService {
} }
recordSuccess(finalBill, ArLogObjectType.BILL, ArLogOperationType.CREATE, recordSuccess(finalBill, ArLogObjectType.BILL, ArLogOperationType.CREATE,
"异步生成正式账单", ArBillStatus.DRAFT.getCode(), ArBillStatus.DRAFT.getCode(), "异步生成正式账单", ArBillStatus.DRAFT.getCode(), ArBillStatus.DRAFT.getCode(),
task.getRequestToken(), task.getCreateBy()); task.getRequestToken(), task.getCreateUserId(), task.getCreateBy());
} }
private String generateFailureMessage(Exception e) { private String generateFailureMessage(Exception e) {
@@ -903,7 +907,8 @@ public class ArBillServiceImpl implements IArBillService {
} }
// 应收账单只负责形成应收,不参与客户余额或授信额度变更。 // 应收账单只负责形成应收,不参与客户余额或授信额度变更。
recordSuccess(bill, ArLogObjectType.BILL, ArLogOperationType.CONFIRM, recordSuccess(bill, ArLogObjectType.BILL, ArLogOperationType.CONFIRM,
"确认应收账单", ArBillStatus.DRAFT.getCode(), ArBillStatus.CONFIRMED.getCode(), requestId, operator); "确认应收账单", ArBillStatus.DRAFT.getCode(), ArBillStatus.CONFIRMED.getCode(), requestId,
SecurityUtils.getUserId(), operator);
ArBill result = selectArBillById(arBillId); ArBill result = selectArBillById(arBillId);
log.info("应收账单确认处理完成,operator={}, requestId={}, arBillId={}, status={}, costMs={}", log.info("应收账单确认处理完成,operator={}, requestId={}, arBillId={}, status={}, costMs={}",
safeLogValue(operator), safeLogValue(requestId), arBillId, result.getStatus(), elapsedMillis(startedAt)); safeLogValue(operator), safeLogValue(requestId), arBillId, result.getStatus(), elapsedMillis(startedAt));
@@ -946,7 +951,8 @@ public class ArBillServiceImpl implements IArBillService {
sourceFeeMapper.restoreUnbilledIfNoActiveArBillByArBillId(arBillId); sourceFeeMapper.restoreUnbilledIfNoActiveArBillByArBillId(arBillId);
sourceBillMapper.refreshArBillStatusByArBillId(arBillId); sourceBillMapper.refreshArBillStatusByArBillId(arBillId);
recordSuccess(bill, ArLogObjectType.BILL, ArLogOperationType.VOID, recordSuccess(bill, ArLogObjectType.BILL, ArLogOperationType.VOID,
"作废应收账单:" + remark, bill.getStatus(), ArBillStatus.VOID.getCode(), requestId, operator); "作废应收账单:" + remark, bill.getStatus(), ArBillStatus.VOID.getCode(), requestId,
SecurityUtils.getUserId(), operator);
ArBill result = selectArBillById(arBillId); ArBill result = selectArBillById(arBillId);
log.info("应收账单作废处理完成,operator={}, requestId={}, arBillId={}, status={}, costMs={}", log.info("应收账单作废处理完成,operator={}, requestId={}, arBillId={}, status={}, costMs={}",
safeLogValue(operator), safeLogValue(requestId), arBillId, result.getStatus(), elapsedMillis(startedAt)); safeLogValue(operator), safeLogValue(requestId), arBillId, result.getStatus(), elapsedMillis(startedAt));
@@ -960,6 +966,7 @@ public class ArBillServiceImpl implements IArBillService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ArBillExport exportBill(Long arBillId, String operator, String requestId) { public ArBillExport exportBill(Long arBillId, String operator, String requestId) {
long startedAt = System.nanoTime(); long startedAt = System.nanoTime();
Long exportUserId = SecurityUtils.getUserId();
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, ArLogObjectType.EXPORT.getCode(), ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, ArLogObjectType.EXPORT.getCode(),
ArLogResult.SUCCESS.getCode()); ArLogResult.SUCCESS.getCode());
if (previous != null && isNumber(previous.getObjectId())) { if (previous != null && isNumber(previous.getObjectId())) {
@@ -1010,13 +1017,15 @@ public class ArBillServiceImpl implements IArBillService {
export.setTemplateStorageType(template.getStorageType()); export.setTemplateStorageType(template.getStorageType());
export.setTemplateStorageKey(template.getStorageKey()); export.setTemplateStorageKey(template.getStorageKey());
export.setTemplateHash(template.getFileHash()); export.setTemplateHash(template.getFileHash());
export.setExportUserId(exportUserId);
export.setExportBy(operator); export.setExportBy(operator);
export.setRowCount(bill.getOrderCount()); export.setRowCount(bill.getOrderCount());
export.setStorageKey(""); export.setStorageKey("");
export.setExportStatus(ArExportStatus.PROCESSING.getCode()); export.setExportStatus(ArExportStatus.PROCESSING.getCode());
exportMapper.insert(export); exportMapper.insert(export);
recordSuccess(bill, ArLogObjectType.EXPORT, ArLogOperationType.EXPORT, recordSuccess(bill, ArLogObjectType.EXPORT, ArLogOperationType.EXPORT,
"已提交账单导出任务", bill.getStatus(), bill.getStatus(), requestId, operator, export.getExportId()); "已提交账单导出任务", bill.getStatus(), bill.getStatus(), requestId,
exportUserId, operator, export.getExportId());
eventPublisher.publishEvent(new ArBillExportTaskEvent(export.getExportId())); eventPublisher.publishEvent(new ArBillExportTaskEvent(export.getExportId()));
log.info("应收账单导出任务已创建,operator={}, requestId={}, arBillId={}, exportId={}, status={}, costMs={}", log.info("应收账单导出任务已创建,operator={}, requestId={}, arBillId={}, exportId={}, status={}, costMs={}",
safeLogValue(operator), safeLogValue(requestId), arBillId, export.getExportId(), safeLogValue(operator), safeLogValue(requestId), arBillId, export.getExportId(),
@@ -1087,7 +1096,8 @@ public class ArBillServiceImpl implements IArBillService {
exportId, export.getArBillId(), safeLogValue(message), elapsedMillis(startedAt), e); exportId, export.getArBillId(), safeLogValue(message), elapsedMillis(startedAt), e);
if (exportMapper.markFailed(exportId, message) > 0) { if (exportMapper.markFailed(exportId, message) > 0) {
logService.recordFailure(export.getArBillId(), ArLogObjectType.EXPORT, String.valueOf(exportId), logService.recordFailure(export.getArBillId(), ArLogObjectType.EXPORT, String.valueOf(exportId),
ArLogOperationType.EXPORT, bill == null ? null : bill.getStatus(), message, export.getExportBy(), null); ArLogOperationType.EXPORT, bill == null ? null : bill.getStatus(), message,
export.getExportUserId(), export.getExportBy(), null);
} }
} finally { } finally {
if (temp != null) { if (temp != null) {
@@ -1233,12 +1243,13 @@ public class ArBillServiceImpl implements IArBillService {
} }
private void recordSuccess(ArBill bill, ArLogObjectType objectType, ArLogOperationType operationType, String desc, String fromStatus, private void recordSuccess(ArBill bill, ArLogObjectType objectType, ArLogOperationType operationType, String desc, String fromStatus,
String toStatus, String requestId, String operator) { String toStatus, String requestId, Long operatorId, String operatorName) {
recordSuccess(bill, objectType, operationType, desc, fromStatus, toStatus, requestId, operator, null); recordSuccess(bill, objectType, operationType, desc, fromStatus, toStatus, requestId,
operatorId, operatorName, null);
} }
private void recordSuccess(ArBill bill, ArLogObjectType objectType, ArLogOperationType operationType, String desc, String fromStatus, private void recordSuccess(ArBill bill, ArLogObjectType objectType, ArLogOperationType operationType, String desc, String fromStatus,
String toStatus, String requestId, String operator, Long objectId) { String toStatus, String requestId, Long operatorId, String operatorName, Long objectId) {
ArBillLog log = new ArBillLog(); ArBillLog log = new ArBillLog();
log.setArBillId(bill.getArBillId()); log.setArBillId(bill.getArBillId());
log.setObjectType(objectType.getCode()); log.setObjectType(objectType.getCode());
@@ -1249,8 +1260,8 @@ public class ArBillServiceImpl implements IArBillService {
log.setToStatus(toStatus); log.setToStatus(toStatus);
log.setBeforeData(safeStatusJson(fromStatus)); log.setBeforeData(safeStatusJson(fromStatus));
log.setAfterData(safeStatusJson(toStatus)); log.setAfterData(safeStatusJson(toStatus));
log.setOperatorId(operator); log.setOperatorId(operatorId);
log.setOperatorName(operator); log.setOperatorName(operatorName);
log.setOperationSource(ArLogOperationSource.WEB.getCode()); log.setOperationSource(ArLogOperationSource.WEB.getCode());
log.setRequestId(requestId); log.setRequestId(requestId);
log.setResult(ArLogResult.SUCCESS.getCode()); log.setResult(ArLogResult.SUCCESS.getCode());
@@ -1259,7 +1270,8 @@ public class ArBillServiceImpl implements IArBillService {
private ServiceException failure(Long arBillId, ArLogObjectType objectType, String objectId, ArLogOperationType operationType, private ServiceException failure(Long arBillId, ArLogObjectType objectType, String objectId, ArLogOperationType operationType,
String fromStatus, String message, String operator, String requestId) { String fromStatus, String message, String operator, String requestId) {
logService.recordFailure(arBillId, objectType, objectId, operationType, fromStatus, message, operator, requestId); logService.recordFailure(arBillId, objectType, objectId, operationType, fromStatus, message,
SecurityUtils.getUserId(), operator, requestId);
return new ServiceException(message); return new ServiceException(message);
} }
@@ -20,6 +20,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="fileHash" column="file_hash"/> <result property="fileHash" column="file_hash"/>
<result property="rowCount" column="row_count"/> <result property="rowCount" column="row_count"/>
<result property="exportStatus" column="export_status"/> <result property="exportStatus" column="export_status"/>
<result property="exportUserId" column="export_user_id"/>
<result property="exportBy" column="export_by"/> <result property="exportBy" column="export_by"/>
<result property="exportTime" column="export_time"/> <result property="exportTime" column="export_time"/>
<result property="remark" column="remark"/> <result property="remark" column="remark"/>
@@ -41,6 +42,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
file_hash, file_hash,
row_count, row_count,
export_status, export_status,
export_user_id,
export_by, export_by,
export_time, export_time,
remark remark
@@ -71,11 +73,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
insert into ar_bill_detail_export ( insert into ar_bill_detail_export (
ar_bill_id, file_name, file_type, template_id, template_code, template_version, ar_bill_id, file_name, file_type, template_id, template_code, template_version,
template_storage_type, template_storage_key, template_hash, storage_key, template_storage_type, template_storage_key, template_hash, storage_key,
file_size, file_hash, row_count, export_status, export_by, export_time, remark file_size, file_hash, row_count, export_status, export_user_id, export_by, export_time, remark
) values ( ) values (
#{arBillId}, #{fileName}, #{fileType}, #{templateId}, #{templateCode}, #{templateVersion}, #{arBillId}, #{fileName}, #{fileType}, #{templateId}, #{templateCode}, #{templateVersion},
#{templateStorageType}, #{templateStorageKey}, #{templateHash}, #{storageKey}, #{templateStorageType}, #{templateStorageKey}, #{templateHash}, #{storageKey},
#{fileSize}, #{fileHash}, #{rowCount}, #{exportStatus}, #{exportBy}, sysdate(), #{remark} #{fileSize}, #{fileHash}, #{rowCount}, #{exportStatus}, #{exportUserId}, #{exportBy}, sysdate(), #{remark}
) )
</insert> </insert>
@@ -20,6 +20,7 @@
<result property="errorMessage" column="error_message"/> <result property="errorMessage" column="error_message"/>
<result property="requestToken" column="request_token"/> <result property="requestToken" column="request_token"/>
<result property="createBy" column="create_by"/> <result property="createBy" column="create_by"/>
<result property="createUserId" column="create_user_id"/>
<result property="createTime" column="create_time"/> <result property="createTime" column="create_time"/>
<result property="startedAt" column="started_at"/> <result property="startedAt" column="started_at"/>
<result property="heartbeatTime" column="heartbeat_time"/> <result property="heartbeatTime" column="heartbeat_time"/>
@@ -42,6 +43,7 @@
error_message, error_message,
request_token, request_token,
create_by, create_by,
create_user_id,
create_time, create_time,
started_at, started_at,
heartbeat_time, heartbeat_time,
@@ -79,11 +81,11 @@
insert into ar_bill_generate_task (task_no, ar_bill_id, selection_mode, request_payload, snapshot_create_time, insert into ar_bill_generate_task (task_no, ar_bill_id, selection_mode, request_payload, snapshot_create_time,
last_source_bill_id, total_count, processed_count, success_count, last_source_bill_id, total_count, processed_count, success_count,
total_amount, total_amount,
status, error_message, request_token, create_by, create_time, started_at, status, error_message, request_token, create_by, create_user_id, create_time, started_at,
heartbeat_time, finished_at) heartbeat_time, finished_at)
values (#{taskNo}, #{arBillId}, #{selectionMode}, #{requestPayload}, #{snapshotCreateTime}, values (#{taskNo}, #{arBillId}, #{selectionMode}, #{requestPayload}, #{snapshotCreateTime},
#{lastSourceBillId}, #{totalCount}, #{processedCount}, #{successCount}, #{totalAmount}, #{lastSourceBillId}, #{totalCount}, #{processedCount}, #{successCount}, #{totalAmount},
#{status}, #{errorMessage}, #{requestToken}, #{createBy}, sysdate(), #{startedAt}, #{status}, #{errorMessage}, #{requestToken}, #{createBy}, #{createUserId}, sysdate(), #{startedAt},
#{heartbeatTime}, #{finishedAt}) #{heartbeatTime}, #{finishedAt})
</insert> </insert>
@@ -29,6 +29,7 @@
<result property="confirmedBy" column="confirmed_by"/> <result property="confirmedBy" column="confirmed_by"/>
<result property="confirmedTime" column="confirmed_time"/> <result property="confirmedTime" column="confirmed_time"/>
<result property="createBy" column="create_by"/> <result property="createBy" column="create_by"/>
<result property="createUserId" column="create_user_id"/>
<result property="createTime" column="create_time"/> <result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/> <result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/> <result property="updateTime" column="update_time"/>
@@ -59,6 +60,7 @@
confirmed_by, confirmed_by,
confirmed_time, confirmed_time,
create_by, create_by,
create_user_id,
create_time, create_time,
update_by, update_by,
update_time update_time
@@ -138,13 +140,13 @@
bill_date, currency, order_count, total_amount, written_off_amount, unwritten_amount, bill_date, currency, order_count, total_amount, written_off_amount, unwritten_amount,
status, generation_status, bill_version, bank_account_name, bank_account_no, bank_name, status, generation_status, bill_version, bank_account_name, bank_account_no, bank_name,
remark, remark,
confirmed_by, confirmed_time, create_by, create_time, update_by, update_time) confirmed_by, confirmed_time, create_by, create_user_id, create_time, update_by, update_time)
values (#{billNo}, #{billName}, #{customerId}, #{customerCode}, #{customerName}, #{billPeriodStart}, values (#{billNo}, #{billName}, #{customerId}, #{customerCode}, #{customerName}, #{billPeriodStart},
#{billPeriodEnd}, #{billPeriodEnd},
#{billDate}, #{currency}, #{orderCount}, #{totalAmount}, #{writtenOffAmount}, #{unwrittenAmount}, #{billDate}, #{currency}, #{orderCount}, #{totalAmount}, #{writtenOffAmount}, #{unwrittenAmount},
#{status}, #{generationStatus}, #{billVersion}, #{bankAccountName}, #{bankAccountNo}, #{bankName}, #{status}, #{generationStatus}, #{billVersion}, #{bankAccountName}, #{bankAccountNo}, #{bankName},
#{remark}, #{remark},
#{confirmedBy}, #{confirmedTime}, #{createBy}, sysdate(), #{updateBy}, sysdate()) #{confirmedBy}, #{confirmedTime}, #{createBy}, #{createUserId}, sysdate(), #{updateBy}, sysdate())
</insert> </insert>
<update id="updateArBillStatus"> <update id="updateArBillStatus">
@@ -0,0 +1,294 @@
-- =============================================================
-- 为应收账单、导出任务和生成任务增加稳定的创建用户主键。
-- 日志表使用 operator_id 保存用户主键,operator_name 保留操作人名称快照。
-- 本脚本可重复执行。
-- =============================================================
-- -------------------------------------------------------------
-- 1. ar_bill 增加 create_user_id,并放到 confirmed_time 之后
-- -------------------------------------------------------------
SET @column_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill'
AND COLUMN_NAME = 'create_user_id'
);
SET @sql := IF(@column_exists = 0,
'ALTER TABLE `ar_bill` ADD COLUMN `create_user_id` bigint(20) DEFAULT NULL COMMENT ''创建用户ID'' AFTER `confirmed_time`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 字段已存在时统一放到 confirmed_time 后(幂等,位置一致时无副作用)
ALTER TABLE `ar_bill`
MODIFY COLUMN `create_user_id` bigint(20) DEFAULT NULL COMMENT '创建用户ID' AFTER `confirmed_time`;
-- -------------------------------------------------------------
-- 2. ar_bill_detail_export 增加 export_user_id,兼容旧字段 create_user_id
-- -------------------------------------------------------------
SET @export_create_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_detail_export'
AND COLUMN_NAME = 'create_user_id'
);
SET @export_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_detail_export'
AND COLUMN_NAME = 'export_user_id'
);
-- 2.1 旧列存在、新列不存在:直接改名
SET @sql := IF(@export_create_user_id_exists > 0 AND @export_user_id_exists = 0,
'ALTER TABLE `ar_bill_detail_export` CHANGE COLUMN `create_user_id` `export_user_id` bigint(20) DEFAULT NULL COMMENT ''导出用户ID'' AFTER `export_status`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2.2 新列不存在(旧列也不存在):新增
SET @export_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_detail_export'
AND COLUMN_NAME = 'export_user_id'
);
SET @sql := IF(@export_user_id_exists = 0,
'ALTER TABLE `ar_bill_detail_export` ADD COLUMN `export_user_id` bigint(20) DEFAULT NULL COMMENT ''导出用户ID'' AFTER `export_status`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2.3 新旧列同时存在:保留新列已有值,用旧列补空
SET @export_create_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_detail_export'
AND COLUMN_NAME = 'create_user_id'
);
SET @sql := IF(@export_create_user_id_exists > 0,
'UPDATE `ar_bill_detail_export` SET `export_user_id` = `create_user_id` WHERE `export_user_id` IS NULL AND `create_user_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2.4 删除旧列
SET @sql := IF(@export_create_user_id_exists > 0,
'ALTER TABLE `ar_bill_detail_export` DROP COLUMN `create_user_id`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2.5 统一 export_user_id 位置到 export_by 前(幂等)
ALTER TABLE `ar_bill_detail_export`
MODIFY COLUMN `export_user_id` bigint(20) DEFAULT NULL COMMENT '导出用户ID' AFTER `export_status`;
-- -------------------------------------------------------------
-- 3. ar_bill_generate_task 增加 create_user_id,并放到 request_token 之后
-- -------------------------------------------------------------
SET @column_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_generate_task'
AND COLUMN_NAME = 'create_user_id'
);
SET @sql := IF(@column_exists = 0,
'ALTER TABLE `ar_bill_generate_task` ADD COLUMN `create_user_id` bigint(20) DEFAULT NULL COMMENT ''创建用户ID'' AFTER `request_token`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
ALTER TABLE `ar_bill_generate_task`
MODIFY COLUMN `create_user_id` bigint(20) DEFAULT NULL COMMENT '创建用户ID' AFTER `request_token`;
-- -------------------------------------------------------------
-- 4. 按登录名补齐历史账单和异步任务的创建用户ID
-- 两边都加 COLLATE,确保 general_ci / unicode_ci 混用时不报 1267。
-- -------------------------------------------------------------
UPDATE `ar_bill`
INNER JOIN `sys_user`
ON `sys_user`.`user_name` COLLATE utf8mb4_unicode_ci = `ar_bill`.`create_by` COLLATE utf8mb4_unicode_ci
SET `ar_bill`.`create_user_id` = `sys_user`.`user_id`
WHERE `ar_bill`.`create_user_id` IS NULL;
UPDATE `ar_bill_detail_export`
INNER JOIN `sys_user`
ON `sys_user`.`user_name` COLLATE utf8mb4_unicode_ci = `ar_bill_detail_export`.`export_by` COLLATE utf8mb4_unicode_ci
SET `ar_bill_detail_export`.`export_user_id` = `sys_user`.`user_id`
WHERE `ar_bill_detail_export`.`export_user_id` IS NULL;
UPDATE `ar_bill_generate_task`
INNER JOIN `sys_user`
ON `sys_user`.`user_name` COLLATE utf8mb4_unicode_ci = `ar_bill_generate_task`.`create_by` COLLATE utf8mb4_unicode_ci
SET `ar_bill_generate_task`.`create_user_id` = `sys_user`.`user_id`
WHERE `ar_bill_generate_task`.`create_user_id` IS NULL;
-- -------------------------------------------------------------
-- 5. 兼容曾加过 ar_bill_log.create_user_id 的库:
-- 先把 create_user_id 迁入 operator_id,再删除重复列。
-- -------------------------------------------------------------
SET @log_create_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'create_user_id'
);
-- 5.1 迁移前先把 operator_name 空值用 operator_id 字符串补齐
SET @sql := IF(@log_create_user_id_exists > 0,
'UPDATE `ar_bill_log` SET `operator_name` = CAST(`operator_id` AS CHAR) WHERE (`operator_name` IS NULL OR `operator_name` = '''') AND `operator_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 5.2 把 create_user_id 迁入 operator_id
SET @sql := IF(@log_create_user_id_exists > 0,
'UPDATE `ar_bill_log` SET `operator_id` = CAST(`create_user_id` AS CHAR) WHERE `create_user_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 5.3 删除重复列
SET @sql := IF(@log_create_user_id_exists > 0,
'ALTER TABLE `ar_bill_log` DROP COLUMN `create_user_id`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- -------------------------------------------------------------
-- 6. ar_bill_log.operator_id 处理
-- 目标:operator_id 最终为 bigint(20) NULL,存 sys_user.user_id。
-- 流程:先确保可空 -> 若为字符串则把登录名/数字映射成用户ID -> 最后统一改成 bigint。
-- -------------------------------------------------------------
-- 6.1 先确保 operator_id 可空(否则后面 UPDATE 置 NULL 会失败)
SET @operator_id_nullable := (
SELECT IS_NULLABLE FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'operator_id'
);
SET @sql := IF(@operator_id_nullable <> 'YES',
'ALTER TABLE `ar_bill_log` MODIFY COLUMN `operator_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ''操作人ID''',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 6.2 重新读取类型
SET @operator_id_type := (
SELECT DATA_TYPE FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'operator_id'
);
-- 6.3 若 operator_id 为字符串:先用 operator_id 补齐 operator_name 空值
SET @sql := IF(@operator_id_type <> 'bigint',
'UPDATE `ar_bill_log` SET `operator_name` = CAST(`operator_id` AS CHAR) WHERE (`operator_name` IS NULL OR `operator_name` = '''') AND `operator_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 6.4 若 operator_id 为字符串:登录名 -> sys_user.user_id;纯数字保留;其余置 NULL
SET @sql := IF(@operator_id_type <> 'bigint',
'UPDATE `ar_bill_log` l
LEFT JOIN `sys_user` u
ON u.`user_name` COLLATE utf8mb4_unicode_ci = l.`operator_id` COLLATE utf8mb4_unicode_ci
SET l.`operator_id` = CASE
WHEN u.`user_id` IS NOT NULL THEN CAST(u.`user_id` AS CHAR)
WHEN CAST(l.`operator_id` AS CHAR) REGEXP ''^[0-9]+$'' THEN CAST(l.`operator_id` AS CHAR)
ELSE NULL END
WHERE l.`operator_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 6.5 最终统一改成 bigint(20) NULL
SET @operator_id_type := (
SELECT DATA_TYPE FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'operator_id'
);
SET @operator_id_nullable := (
SELECT IS_NULLABLE FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'operator_id'
);
SET @sql := IF(@operator_id_type <> 'bigint' OR @operator_id_nullable <> 'YES',
'ALTER TABLE `ar_bill_log` MODIFY COLUMN `operator_id` bigint(20) DEFAULT NULL COMMENT ''操作人用户ID''',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- -------------------------------------------------------------
-- 7. ar_bill_generate_task 索引:确保 idx_ar_bill_generate_creator
-- 以 create_user_id 为首列。
-- -------------------------------------------------------------
SET @creator_index_exists := (
SELECT COUNT(1) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_generate_task'
AND INDEX_NAME = 'idx_ar_bill_generate_creator'
);
SET @creator_index_user_id_exists := (
SELECT COUNT(1) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_generate_task'
AND INDEX_NAME = 'idx_ar_bill_generate_creator'
AND COLUMN_NAME = 'create_user_id'
AND SEQ_IN_INDEX = 1
);
-- 7.1 旧索引存在但不以 create_user_id 为首列:先删
SET @sql := IF(@creator_index_exists > 0 AND @creator_index_user_id_exists = 0,
'ALTER TABLE `ar_bill_generate_task` DROP INDEX `idx_ar_bill_generate_creator`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 7.2 重新读取,确认需要创建
SET @creator_index_user_id_exists := (
SELECT COUNT(1) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_generate_task'
AND INDEX_NAME = 'idx_ar_bill_generate_creator'
AND COLUMN_NAME = 'create_user_id'
AND SEQ_IN_INDEX = 1
);
SET @sql := IF(@creator_index_user_id_exists = 0,
'ALTER TABLE `ar_bill_generate_task` ADD KEY `idx_ar_bill_generate_creator` (`create_user_id`,`status`,`create_time`)',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
@@ -0,0 +1,293 @@
-- =============================================================
-- 生产环境统一迁移脚本:应收账单用户归属字段及日志操作人ID迁移
-- 本脚本不包含 ar_bill_export 表改名。
-- 执行前确认目标库已存在 ar_bill_detail_export 表,并确认当前连接为生产库。
-- 本脚本包含字段新增/改名、历史数据回填、日志字段转换及索引整理。
-- =============================================================
-- 1. ar_bill 增加 create_user_id,并放到 confirmed_time 之后
-- -------------------------------------------------------------
SET @column_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill'
AND COLUMN_NAME = 'create_user_id'
);
SET @sql := IF(@column_exists = 0,
'ALTER TABLE `ar_bill` ADD COLUMN `create_user_id` bigint(20) DEFAULT NULL COMMENT ''创建用户ID'' AFTER `confirmed_time`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 字段已存在时统一放到 confirmed_time 后(幂等,位置一致时无副作用)
ALTER TABLE `ar_bill`
MODIFY COLUMN `create_user_id` bigint(20) DEFAULT NULL COMMENT '创建用户ID' AFTER `confirmed_time`;
-- -------------------------------------------------------------
-- 2. ar_bill_detail_export 增加 export_user_id,兼容旧字段 create_user_id
-- -------------------------------------------------------------
SET @export_create_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_detail_export'
AND COLUMN_NAME = 'create_user_id'
);
SET @export_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_detail_export'
AND COLUMN_NAME = 'export_user_id'
);
-- 2.1 旧列存在、新列不存在:直接改名
SET @sql := IF(@export_create_user_id_exists > 0 AND @export_user_id_exists = 0,
'ALTER TABLE `ar_bill_detail_export` CHANGE COLUMN `create_user_id` `export_user_id` bigint(20) DEFAULT NULL COMMENT ''导出用户ID'' AFTER `export_status`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2.2 新列不存在(旧列也不存在):新增
SET @export_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_detail_export'
AND COLUMN_NAME = 'export_user_id'
);
SET @sql := IF(@export_user_id_exists = 0,
'ALTER TABLE `ar_bill_detail_export` ADD COLUMN `export_user_id` bigint(20) DEFAULT NULL COMMENT ''导出用户ID'' AFTER `export_status`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2.3 新旧列同时存在:保留新列已有值,用旧列补空
SET @export_create_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_detail_export'
AND COLUMN_NAME = 'create_user_id'
);
SET @sql := IF(@export_create_user_id_exists > 0,
'UPDATE `ar_bill_detail_export` SET `export_user_id` = `create_user_id` WHERE `export_user_id` IS NULL AND `create_user_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2.4 删除旧列
SET @sql := IF(@export_create_user_id_exists > 0,
'ALTER TABLE `ar_bill_detail_export` DROP COLUMN `create_user_id`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2.5 统一 export_user_id 位置到 export_by 前(幂等)
ALTER TABLE `ar_bill_detail_export`
MODIFY COLUMN `export_user_id` bigint(20) DEFAULT NULL COMMENT '导出用户ID' AFTER `export_status`;
-- -------------------------------------------------------------
-- 3. ar_bill_generate_task 增加 create_user_id,并放到 request_token 之后
-- -------------------------------------------------------------
SET @column_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_generate_task'
AND COLUMN_NAME = 'create_user_id'
);
SET @sql := IF(@column_exists = 0,
'ALTER TABLE `ar_bill_generate_task` ADD COLUMN `create_user_id` bigint(20) DEFAULT NULL COMMENT ''创建用户ID'' AFTER `request_token`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
ALTER TABLE `ar_bill_generate_task`
MODIFY COLUMN `create_user_id` bigint(20) DEFAULT NULL COMMENT '创建用户ID' AFTER `request_token`;
-- -------------------------------------------------------------
-- 4. 按登录名补齐历史账单和异步任务的创建用户ID
-- 两边都加 COLLATE,确保 general_ci / unicode_ci 混用时不报 1267。
-- -------------------------------------------------------------
UPDATE `ar_bill`
INNER JOIN `sys_user`
ON `sys_user`.`user_name` COLLATE utf8mb4_unicode_ci = `ar_bill`.`create_by` COLLATE utf8mb4_unicode_ci
SET `ar_bill`.`create_user_id` = `sys_user`.`user_id`
WHERE `ar_bill`.`create_user_id` IS NULL;
UPDATE `ar_bill_detail_export`
INNER JOIN `sys_user`
ON `sys_user`.`user_name` COLLATE utf8mb4_unicode_ci = `ar_bill_detail_export`.`export_by` COLLATE utf8mb4_unicode_ci
SET `ar_bill_detail_export`.`export_user_id` = `sys_user`.`user_id`
WHERE `ar_bill_detail_export`.`export_user_id` IS NULL;
UPDATE `ar_bill_generate_task`
INNER JOIN `sys_user`
ON `sys_user`.`user_name` COLLATE utf8mb4_unicode_ci = `ar_bill_generate_task`.`create_by` COLLATE utf8mb4_unicode_ci
SET `ar_bill_generate_task`.`create_user_id` = `sys_user`.`user_id`
WHERE `ar_bill_generate_task`.`create_user_id` IS NULL;
-- -------------------------------------------------------------
-- 5. 兼容曾加过 ar_bill_log.create_user_id 的库:
-- 先把 create_user_id 迁入 operator_id,再删除重复列。
-- -------------------------------------------------------------
SET @log_create_user_id_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'create_user_id'
);
-- 5.1 迁移前先把 operator_name 空值用 operator_id 字符串补齐
SET @sql := IF(@log_create_user_id_exists > 0,
'UPDATE `ar_bill_log` SET `operator_name` = CAST(`operator_id` AS CHAR) WHERE (`operator_name` IS NULL OR `operator_name` = '''') AND `operator_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 5.2 把 create_user_id 迁入 operator_id
SET @sql := IF(@log_create_user_id_exists > 0,
'UPDATE `ar_bill_log` SET `operator_id` = CAST(`create_user_id` AS CHAR) WHERE `create_user_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 5.3 删除重复列
SET @sql := IF(@log_create_user_id_exists > 0,
'ALTER TABLE `ar_bill_log` DROP COLUMN `create_user_id`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- -------------------------------------------------------------
-- 6. ar_bill_log.operator_id 处理
-- 目标:operator_id 最终为 bigint(20) NULL,存 sys_user.user_id。
-- 流程:先确保可空 -> 若为字符串则把登录名/数字映射成用户ID -> 最后统一改成 bigint。
-- -------------------------------------------------------------
-- 6.1 先确保 operator_id 可空(否则后面 UPDATE 置 NULL 会失败)
SET @operator_id_nullable := (
SELECT IS_NULLABLE FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'operator_id'
);
SET @sql := IF(@operator_id_nullable <> 'YES',
'ALTER TABLE `ar_bill_log` MODIFY COLUMN `operator_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ''操作人ID''',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 6.2 重新读取类型
SET @operator_id_type := (
SELECT DATA_TYPE FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'operator_id'
);
-- 6.3 若 operator_id 为字符串:先用 operator_id 补齐 operator_name 空值
SET @sql := IF(@operator_id_type <> 'bigint',
'UPDATE `ar_bill_log` SET `operator_name` = CAST(`operator_id` AS CHAR) WHERE (`operator_name` IS NULL OR `operator_name` = '''') AND `operator_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 6.4 若 operator_id 为字符串:登录名 -> sys_user.user_id;纯数字保留;其余置 NULL
SET @sql := IF(@operator_id_type <> 'bigint',
'UPDATE `ar_bill_log` l
LEFT JOIN `sys_user` u
ON u.`user_name` COLLATE utf8mb4_unicode_ci = l.`operator_id` COLLATE utf8mb4_unicode_ci
SET l.`operator_id` = CASE
WHEN u.`user_id` IS NOT NULL THEN CAST(u.`user_id` AS CHAR)
WHEN CAST(l.`operator_id` AS CHAR) REGEXP ''^[0-9]+$'' THEN CAST(l.`operator_id` AS CHAR)
ELSE NULL END
WHERE l.`operator_id` IS NOT NULL',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 6.5 最终统一改成 bigint(20) NULL
SET @operator_id_type := (
SELECT DATA_TYPE FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'operator_id'
);
SET @operator_id_nullable := (
SELECT IS_NULLABLE FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_log'
AND COLUMN_NAME = 'operator_id'
);
SET @sql := IF(@operator_id_type <> 'bigint' OR @operator_id_nullable <> 'YES',
'ALTER TABLE `ar_bill_log` MODIFY COLUMN `operator_id` bigint(20) DEFAULT NULL COMMENT ''操作人用户ID''',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- -------------------------------------------------------------
-- 7. ar_bill_generate_task 索引:确保 idx_ar_bill_generate_creator
-- 以 create_user_id 为首列。
-- -------------------------------------------------------------
SET @creator_index_exists := (
SELECT COUNT(1) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_generate_task'
AND INDEX_NAME = 'idx_ar_bill_generate_creator'
);
SET @creator_index_user_id_exists := (
SELECT COUNT(1) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_generate_task'
AND INDEX_NAME = 'idx_ar_bill_generate_creator'
AND COLUMN_NAME = 'create_user_id'
AND SEQ_IN_INDEX = 1
);
-- 7.1 旧索引存在但不以 create_user_id 为首列:先删
SET @sql := IF(@creator_index_exists > 0 AND @creator_index_user_id_exists = 0,
'ALTER TABLE `ar_bill_generate_task` DROP INDEX `idx_ar_bill_generate_creator`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 7.2 重新读取,确认需要创建
SET @creator_index_user_id_exists := (
SELECT COUNT(1) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ar_bill_generate_task'
AND INDEX_NAME = 'idx_ar_bill_generate_creator'
AND COLUMN_NAME = 'create_user_id'
AND SEQ_IN_INDEX = 1
);
SET @sql := IF(@creator_index_user_id_exists = 0,
'ALTER TABLE `ar_bill_generate_task` ADD KEY `idx_ar_bill_generate_creator` (`create_user_id`,`status`,`create_time`)',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;