diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBill.java b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBill.java index 2ecc0c1f..fe31a58a 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBill.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBill.java @@ -77,6 +77,8 @@ public class ArBill extends BaseEntity { private String confirmedBy; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date confirmedTime; + /** 创建账单的用户主键。 */ + private Long createUserId; /** * 账单明细;详情接口分页查询时通常不直接填充该集合。 diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillExport.java b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillExport.java index fbb7650c..12a77dfd 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillExport.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillExport.java @@ -46,6 +46,8 @@ public class ArBillExport implements Serializable { * 导出状态:PROCESSING生成中、SUCCESS成功可下载、FAILED失败。 */ private String exportStatus; + /** 发起导出的用户主键。 */ + private Long exportUserId; private String exportBy; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date exportTime; diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillGenerateTask.java b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillGenerateTask.java index ca5107f2..00833fcf 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillGenerateTask.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillGenerateTask.java @@ -75,6 +75,8 @@ public class ArBillGenerateTask implements Serializable { * 发起任务的用户。 */ private String createBy; + /** 创建任务的用户主键。 */ + private Long createUserId; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillLog.java b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillLog.java index e939e82f..c58c1570 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillLog.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillLog.java @@ -33,7 +33,7 @@ public class ArBillLog implements Serializable { private String toStatus; private String beforeData; private String afterData; - private String operatorId; + private Long operatorId; private String operatorName; /** * 操作来源:WEB网页端、API接口、JOB定时任务、IMPORT数据导入。 diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/service/IArBillLogService.java b/bms-back/bms-system/src/main/java/com/bms/ar/service/IArBillLogService.java index ee73c35e..32b07ccf 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/service/IArBillLogService.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/service/IArBillLogService.java @@ -12,5 +12,5 @@ public interface IArBillLogService { void record(ArBillLog log); 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); } diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBalanceWriteoffServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBalanceWriteoffServiceImpl.java index 576026fa..c33c7d23 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBalanceWriteoffServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBalanceWriteoffServiceImpl.java @@ -26,11 +26,11 @@ import com.bms.ar.mapper.ArBillMapper; import com.bms.ar.service.IArBalanceWriteoffService; import com.bms.ar.service.IArBillLogService; 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.ReceiptWriteoffPlan; import com.bms.customer.customermoney.mapper.CustomerMoneyMapper; import com.bms.customer.customermoney.mapper.ReceiptWriteoffPlanMapper; -import com.bms.common.utils.SecurityUtils; import com.bms.common.utils.uuid.IdUtils; /** @@ -286,7 +286,7 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService { log.setOperationDesc(desc); log.setFromStatus(fromStatus); log.setToStatus(toStatus); - log.setOperatorId(operator); + log.setOperatorId(SecurityUtils.getUserId()); log.setOperatorName(operator); log.setOperationSource(ArLogOperationSource.WEB.getCode()); log.setRequestId(requestId); diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillLogServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillLogServiceImpl.java index 90f887d3..b98e5b38 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillLogServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillLogServiceImpl.java @@ -36,7 +36,7 @@ public class ArBillLogServiceImpl implements IArBillLogService { @Override @Transactional(propagation = Propagation.REQUIRES_NEW) 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(); log.setArBillId(arBillId); log.setObjectType(objectType.getCode()); @@ -44,8 +44,8 @@ public class ArBillLogServiceImpl implements IArBillLogService { log.setOperationType(operationType.getCode()); log.setOperationDesc(message); log.setFromStatus(fromStatus); - log.setOperatorId(operator); - log.setOperatorName(operator); + log.setOperatorId(operatorId); + log.setOperatorName(operatorName); log.setOperationSource(ArLogOperationSource.WEB.getCode()); log.setRequestId(requestId == null ? IdUtils.fastSimpleUUID() : requestId); log.setResult(ArLogResult.FAILED.getCode()); diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillServiceImpl.java index 6f605227..01bd583a 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillServiceImpl.java @@ -81,6 +81,7 @@ import com.bms.ar.domain.vo.ArBillQuery; import com.bms.common.config.BmsConfig; import com.bms.common.exception.ServiceException; import com.bms.common.utils.DateUtils; +import com.bms.common.utils.SecurityUtils; import com.bms.common.utils.StringUtils; import com.bms.common.utils.file.FileUtils; import com.bms.common.utils.file.OssStorage; @@ -326,6 +327,7 @@ public class ArBillServiceImpl implements IArBillService { @Transactional(rollbackFor = Exception.class) public ArBillGenerateTask generateBill(ArBillGenerateRequest request, String operator, String requestId) { long startedAt = System.nanoTime(); + Long createUserId = SecurityUtils.getUserId(); validatePeriod(request); ArBillGenerateTask existed = generateTaskMapper.selectByRequestToken(requestId); if (existed != null) { @@ -400,6 +402,7 @@ public class ArBillServiceImpl implements IArBillService { bill.setBankName(bankAccount.getBankName()); bill.setRemark(request.getRemark()); bill.setCreateBy(operator); + bill.setCreateUserId(createUserId); bill.setUpdateBy(operator); billMapper.insertArBill(bill); @@ -417,6 +420,7 @@ public class ArBillServiceImpl implements IArBillService { task.setStatus(ArBillGenerateTaskStatus.WAITING); task.setRequestToken(requestId); task.setCreateBy(operator); + task.setCreateUserId(createUserId); generateTaskMapper.insert(task); eventPublisher.publishEvent(new ArBillGenerateTaskEvent(task.getTaskId())); log.info("应收账单生成任务已创建,operator={}, requestId={}, customerId={}, billPeriodEnd={}, selectionMode={}, selectedCount={}, excludedCount={}, taskId={}, status={}, costMs={}", @@ -798,7 +802,7 @@ public class ArBillServiceImpl implements IArBillService { try { logService.recordFailure(task.getArBillId(), ArLogObjectType.BILL, String.valueOf(task.getTaskId()), ArLogOperationType.CREATE, ArBillStatus.DRAFT.getCode(), - message, operator, requestId); + message, task.getCreateUserId(), operator, requestId); } catch (Exception logException) { log.warn("账单生成失败日志写入失败,但任务清理已完成,taskId={}", task.getTaskId(), logException); } @@ -851,7 +855,7 @@ public class ArBillServiceImpl implements IArBillService { } recordSuccess(finalBill, ArLogObjectType.BILL, ArLogOperationType.CREATE, "异步生成正式账单", ArBillStatus.DRAFT.getCode(), ArBillStatus.DRAFT.getCode(), - task.getRequestToken(), task.getCreateBy()); + task.getRequestToken(), task.getCreateUserId(), task.getCreateBy()); } private String generateFailureMessage(Exception e) { @@ -903,7 +907,8 @@ public class ArBillServiceImpl implements IArBillService { } // 应收账单只负责形成应收,不参与客户余额或授信额度变更。 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); log.info("应收账单确认处理完成,operator={}, requestId={}, arBillId={}, status={}, costMs={}", safeLogValue(operator), safeLogValue(requestId), arBillId, result.getStatus(), elapsedMillis(startedAt)); @@ -946,7 +951,8 @@ public class ArBillServiceImpl implements IArBillService { sourceFeeMapper.restoreUnbilledIfNoActiveArBillByArBillId(arBillId); sourceBillMapper.refreshArBillStatusByArBillId(arBillId); 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); log.info("应收账单作废处理完成,operator={}, requestId={}, arBillId={}, status={}, costMs={}", safeLogValue(operator), safeLogValue(requestId), arBillId, result.getStatus(), elapsedMillis(startedAt)); @@ -960,6 +966,7 @@ public class ArBillServiceImpl implements IArBillService { @Transactional(rollbackFor = Exception.class) public ArBillExport exportBill(Long arBillId, String operator, String requestId) { long startedAt = System.nanoTime(); + Long exportUserId = SecurityUtils.getUserId(); ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, ArLogObjectType.EXPORT.getCode(), ArLogResult.SUCCESS.getCode()); if (previous != null && isNumber(previous.getObjectId())) { @@ -1010,13 +1017,15 @@ public class ArBillServiceImpl implements IArBillService { export.setTemplateStorageType(template.getStorageType()); export.setTemplateStorageKey(template.getStorageKey()); export.setTemplateHash(template.getFileHash()); + export.setExportUserId(exportUserId); export.setExportBy(operator); export.setRowCount(bill.getOrderCount()); export.setStorageKey(""); export.setExportStatus(ArExportStatus.PROCESSING.getCode()); exportMapper.insert(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())); log.info("应收账单导出任务已创建,operator={}, requestId={}, arBillId={}, exportId={}, status={}, costMs={}", safeLogValue(operator), safeLogValue(requestId), arBillId, export.getExportId(), @@ -1087,7 +1096,8 @@ public class ArBillServiceImpl implements IArBillService { exportId, export.getArBillId(), safeLogValue(message), elapsedMillis(startedAt), e); if (exportMapper.markFailed(exportId, message) > 0) { 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 { 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, - String toStatus, String requestId, String operator) { - recordSuccess(bill, objectType, operationType, desc, fromStatus, toStatus, requestId, operator, null); + String toStatus, String requestId, Long operatorId, String operatorName) { + recordSuccess(bill, objectType, operationType, desc, fromStatus, toStatus, requestId, + operatorId, operatorName, null); } 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(); log.setArBillId(bill.getArBillId()); log.setObjectType(objectType.getCode()); @@ -1249,8 +1260,8 @@ public class ArBillServiceImpl implements IArBillService { log.setToStatus(toStatus); log.setBeforeData(safeStatusJson(fromStatus)); log.setAfterData(safeStatusJson(toStatus)); - log.setOperatorId(operator); - log.setOperatorName(operator); + log.setOperatorId(operatorId); + log.setOperatorName(operatorName); log.setOperationSource(ArLogOperationSource.WEB.getCode()); log.setRequestId(requestId); 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, 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); } diff --git a/bms-back/bms-system/src/main/resources/mapper/ar/ArBillExportMapper.xml b/bms-back/bms-system/src/main/resources/mapper/ar/ArBillExportMapper.xml index 12433438..578d7fc8 100644 --- a/bms-back/bms-system/src/main/resources/mapper/ar/ArBillExportMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/ar/ArBillExportMapper.xml @@ -20,6 +20,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -41,6 +42,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" file_hash, row_count, export_status, + export_user_id, export_by, export_time, remark @@ -71,11 +73,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" insert into ar_bill_detail_export ( ar_bill_id, file_name, file_type, template_id, template_code, template_version, 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 ( #{arBillId}, #{fileName}, #{fileType}, #{templateId}, #{templateCode}, #{templateVersion}, #{templateStorageType}, #{templateStorageKey}, #{templateHash}, #{storageKey}, - #{fileSize}, #{fileHash}, #{rowCount}, #{exportStatus}, #{exportBy}, sysdate(), #{remark} + #{fileSize}, #{fileHash}, #{rowCount}, #{exportStatus}, #{exportUserId}, #{exportBy}, sysdate(), #{remark} ) diff --git a/bms-back/bms-system/src/main/resources/mapper/ar/ArBillGenerateTaskMapper.xml b/bms-back/bms-system/src/main/resources/mapper/ar/ArBillGenerateTaskMapper.xml index a7e7d9ab..27d3b0be 100644 --- a/bms-back/bms-system/src/main/resources/mapper/ar/ArBillGenerateTaskMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/ar/ArBillGenerateTaskMapper.xml @@ -20,6 +20,7 @@ + @@ -42,6 +43,7 @@ error_message, request_token, create_by, + create_user_id, create_time, started_at, heartbeat_time, @@ -79,11 +81,11 @@ 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, 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) values (#{taskNo}, #{arBillId}, #{selectionMode}, #{requestPayload}, #{snapshotCreateTime}, #{lastSourceBillId}, #{totalCount}, #{processedCount}, #{successCount}, #{totalAmount}, - #{status}, #{errorMessage}, #{requestToken}, #{createBy}, sysdate(), #{startedAt}, + #{status}, #{errorMessage}, #{requestToken}, #{createBy}, #{createUserId}, sysdate(), #{startedAt}, #{heartbeatTime}, #{finishedAt}) diff --git a/bms-back/bms-system/src/main/resources/mapper/ar/ArBillMapper.xml b/bms-back/bms-system/src/main/resources/mapper/ar/ArBillMapper.xml index bb4daae6..aa835a73 100644 --- a/bms-back/bms-system/src/main/resources/mapper/ar/ArBillMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/ar/ArBillMapper.xml @@ -29,6 +29,7 @@ + @@ -59,6 +60,7 @@ confirmed_by, confirmed_time, create_by, + create_user_id, create_time, update_by, update_time @@ -138,13 +140,13 @@ 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, 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}, #{billPeriodEnd}, #{billDate}, #{currency}, #{orderCount}, #{totalAmount}, #{writtenOffAmount}, #{unwrittenAmount}, #{status}, #{generationStatus}, #{billVersion}, #{bankAccountName}, #{bankAccountNo}, #{bankName}, #{remark}, - #{confirmedBy}, #{confirmedTime}, #{createBy}, sysdate(), #{updateBy}, sysdate()) + #{confirmedBy}, #{confirmedTime}, #{createBy}, #{createUserId}, sysdate(), #{updateBy}, sysdate()) diff --git a/bms-back/sql/20261019_add_ar_create_user_id.sql b/bms-back/sql/20261019_add_ar_create_user_id.sql new file mode 100644 index 00000000..b8be8c84 --- /dev/null +++ b/bms-back/sql/20261019_add_ar_create_user_id.sql @@ -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; diff --git a/bms-back/sql/20261019_prod_ar_bill_unified_migration.sql b/bms-back/sql/20261019_prod_ar_bill_unified_migration.sql new file mode 100644 index 00000000..400c1643 --- /dev/null +++ b/bms-back/sql/20261019_prod_ar_bill_unified_migration.sql @@ -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;