Commit 676b953e by renyizhao

spu 导入修改,表单去掉销量,销量自增

parent f2e21d79
......@@ -40,7 +40,7 @@ public class ResourceSpuImportController {
/** 固定列头 */
private static final List<String> FIXED_HEADERS = List.of(
"商品名称", "商品分类", "算力来源", "商品简介", "封面图", "商品销量", "状态");
"商品名称", "商品分类", "算力来源", "商品简介", "服务器所在地");
@Resource
private ResourceSpuImportService resourceSpuImportService;
......
......@@ -37,8 +37,7 @@ public class ResourceSpuSaveReqVO {
@Schema(description = "商品轮播图地址,以逗号分隔,最多15张")
private String sliderPicUrls;
@Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "商品销量不能为空")
@Schema(description = "商品销量(默认 0,由订单支付回调自动累加)")
private Integer sales;
@Schema(description = "服务器所在地")
......
......@@ -128,4 +128,14 @@ public interface ResourceSpuMapper extends BaseMapperX<ResourceSpuDO> {
* 返回 [{areaId, compute}]
*/
List<Map<String, Object>> selectSumComputeByAreaId();
/**
* 累加 SPU 销量(订单支付成功回调使用)
*
* @param id SPU 主键
* @param increment 增量(正数加、负数减;订单取消时传负数即可)
* @param updater 操作人
* @return 受影响行数
*/
int incrSalesById(@Param("id") Long id, @Param("increment") int increment, @Param("updater") String updater);
}
......@@ -23,6 +23,7 @@ import com.luhu.computility.module.compute.dal.dataobject.resourcesku.ResourceSk
import com.luhu.computility.module.compute.dal.dataobject.resourceordersnapshot.ResourceOrderSnapshotDO;
import com.luhu.computility.module.compute.dal.dataobject.resourcespuspecvalue.ResourceSpuSpecValueDO;
import com.luhu.computility.module.compute.dal.mysql.resourceorder.ResourceOrderMapper;
import com.luhu.computility.module.compute.dal.mysql.resourcespu.ResourceSpuMapper;
import com.luhu.computility.module.compute.dal.redis.no.ResourceOrderNoRedisDAO;
import com.luhu.computility.module.compute.enums.ResourceOrderInvoiceStatus;
import com.luhu.computility.module.compute.enums.ResourceOrderRefundStatus;
......@@ -91,6 +92,9 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
private ResourceOrderMapper resourceOrderMapper;
@Resource
private ResourceSpuMapper resourceSpuMapper;
@Resource
private ResourceSkuService resourceSkuService;
@Resource
......@@ -292,6 +296,37 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
updateOrder.setPayTime(LocalDateTime.now());
resourceOrderMapper.updateById(updateOrder);
// 4. 累加 SPU 销量(订单首次变为已支付时 +1,重复回调不重复累加)
incrementSpuSalesOnPaid(order);
}
/**
* 订单支付成功时累加其 SPU 销量。
* 仅在订单从 UNPAID 跨到 PENDING_DELIVERY 时调用,重复回调因早期 return 不会进到这里。
* SKU 缺失 / SPU 缺失 / 已软删的 SPU 不会抛错,只记 warn 日志。
*/
private void incrementSpuSalesOnPaid(ResourceOrderDO order) {
if (order == null || order.getSkuId() == null) {
return;
}
try {
ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId());
if (sku == null || sku.getSpuId() == null) {
log.warn("[incrementSpuSalesOnPaid] SKU 或 SPU ID 缺失,订单ID: {}", order.getId());
return;
}
int rows = resourceSpuMapper.incrSalesById(sku.getSpuId(), 1, "system");
if (rows == 0) {
log.warn("[incrementSpuSalesOnPaid] SPU 不存在或已删除,订单ID: {}, SPU_ID: {}",
order.getId(), sku.getSpuId());
} else {
log.info("[incrementSpuSalesOnPaid] 订单 {} 支付成功,SPU {} 销量 +1", order.getId(), sku.getSpuId());
}
} catch (Exception e) {
// 不阻塞主流程,销量同步失败只记日志
log.error("[incrementSpuSalesOnPaid] 累加 SPU 销量异常,订单ID: {}", order.getId(), e);
}
}
@Override
......@@ -713,6 +748,9 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
resourceOrderMapper.updateById(updateResourceOrder);
payOrderWpgjMapper.updateById(updatePayOrder);
// 4.1 累加 SPU 销量(首次支付成功 +1,重复回调不重复累加)
incrementSpuSalesOnPaid(order);
// 触发管理员新订单通知(钉钉群),失败仅记日志,不抛
smsNotifyService.notifyAdminsOnPurchase(order);
......
......@@ -38,7 +38,7 @@ import static com.luhu.computility.module.compute.enums.ErrorCodeConstants.*;
*
* <p>Excel 模板列结构:
* <ul>
* <li>固定列:商品名称 / 商品分类 / 封面图 / 商品销量 / 状态</li>
* <li>固定列:商品名称 / 商品分类 / 算力来源 / 商品简介 / 服务器所在地 / 商品销量 / 状态</li>
* <li>动态列:根据字典 compute_resource_config_category 的 label 动态生成(如 CPU / GPU / RAM / Storage)</li>
* </ul>
*
......@@ -76,8 +76,8 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
private static final String COL_SOURCE = "算力来源";
/** 固定列:商品简介 */
private static final String COL_INTRO = "商品简介";
/** 固定列:封面图 */
private static final String COL_PIC_URL = "封面图";
/** 固定列:服务器所在地 */
private static final String COL_LOCATION = "服务器所在地";
/** 固定列:商品销量 */
private static final String COL_SALES = "商品销量";
/** 固定列:状态 */
......@@ -85,6 +85,8 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
/** 字典类型:算力资源配置类别 */
private static final String DICT_TYPE_CONFIG_CATEGORY = "compute_resource_config_category";
/** 字典类型:算力来源 */
private static final String DICT_TYPE_SOURCE = "compute_resource_source";
@Override
public ResourceSpuImportRespVO importSpuList(MultipartFile file) throws IOException {
......@@ -150,6 +152,9 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
DictDataRespDTO::getValue,
(a, b) -> a));
// 3.1 拿算力来源字典(label->value,例如 "合作" -> "cooperative")
List<DictDataRespDTO> sourceDictList = dictDataApi.getDictDataList(DICT_TYPE_SOURCE);
// 4. 解析列索引
Map<String, Integer> fixedColIdx = new HashMap<>();
Map<Integer, String> specColIdxToCategory = new HashMap<>();
......@@ -164,9 +169,7 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
case COL_CATEGORY:
case COL_SOURCE:
case COL_INTRO:
case COL_PIC_URL:
case COL_SALES:
case COL_STATUS:
case COL_LOCATION:
fixedColIdx.put(col, i);
break;
default:
......@@ -178,8 +181,7 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
}
// 5. 校验固定列必须存在
if (!fixedColIdx.containsKey(COL_NAME) || !fixedColIdx.containsKey(COL_CATEGORY)
|| !fixedColIdx.containsKey(COL_PIC_URL)) {
if (!fixedColIdx.containsKey(COL_NAME) || !fixedColIdx.containsKey(COL_CATEGORY)) {
throw exception(RESOURCE_SPU_IMPORT_HEADER_INVALID);
}
......@@ -197,13 +199,22 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
Map<String, Long> categoryNameCache = new HashMap<>();
// 缓存已查询/已创建的 specConfig(key=configCategory:value, value=configId)
Map<String, Long> specCache = new HashMap<>();
// 算力来源字典:label -> value(如 "合作" -> "cooperative")
Map<String, String> sourceLabelToValue = new HashMap<>();
Map<String, String> sourceValueToLabel = new HashMap<>();
if (CollUtil.isNotEmpty(sourceDictList)) {
for (DictDataRespDTO d : sourceDictList) {
sourceLabelToValue.put(d.getLabel(), d.getValue());
sourceValueToLabel.put(d.getValue(), d.getLabel());
}
}
for (int rowIdx = 0; rowIdx < dataRows.size(); rowIdx++) {
List<String> row = dataRows.get(rowIdx);
int excelRow = rowIdx + 2; // Excel 行号:表头占 1 行,数据行从第 2 行开始
String rowName = getCellValue(row, fixedColIdx.get(COL_NAME));
try {
processRow(row, fixedColIdx, specColIdxToCategory, nameInBatch, categoryValidCache, categoryNameCache, specCache, respVO);
processRow(row, fixedColIdx, specColIdxToCategory, nameInBatch, categoryValidCache, categoryNameCache, sourceLabelToValue, sourceValueToLabel, specCache, respVO);
} catch (Exception e) {
log.warn("[importSpuList][第 {} 行导入失败:{}]", excelRow, e.getMessage());
respVO.getFailureRows().add(ResourceSpuImportRowVO.builder()
......@@ -226,16 +237,16 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
Set<String> nameInBatch,
Map<Long, Boolean> categoryValidCache,
Map<String, Long> categoryNameCache,
Map<String, String> sourceLabelToValue,
Map<String, String> sourceValueToLabel,
Map<String, Long> specCache,
ResourceSpuImportRespVO respVO) {
// 1. 解析固定字段
String name = getCellValue(row, fixedColIdx.get(COL_NAME));
String categoryIdStr = getCellValue(row, fixedColIdx.get(COL_CATEGORY));
String source = getCellValue(row, fixedColIdx.get(COL_SOURCE));
String sourceRaw = getCellValue(row, fixedColIdx.get(COL_SOURCE));
String intro = getCellValue(row, fixedColIdx.get(COL_INTRO));
String picUrl = getCellValue(row, fixedColIdx.get(COL_PIC_URL));
String salesStr = getCellValue(row, fixedColIdx.get(COL_SALES));
String statusStr = getCellValue(row, fixedColIdx.get(COL_STATUS));
String location = getCellValue(row, fixedColIdx.get(COL_LOCATION));
if (StrUtil.isBlank(name)) {
throw new IllegalArgumentException("商品名称不能为空");
......@@ -265,9 +276,8 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
throw exception(RESOURCE_SPU_IMPORT_NAME_DUPLICATE);
}
// 5. 解析可选字段
Integer sales = parseIntOrDefault(salesStr, 0);
Integer status = parseStatusOrDefault(statusStr, ResourceSpuStatus.ONLINE.getValue());
// 5. 算力来源:按字典 label 转 value(如 "合作" -> "cooperative"),兼容直接填 value
String source = resolveSource(sourceRaw, sourceLabelToValue, sourceValueToLabel);
// 6. 处理规格列:每列对应 (configCategory, configOption),查/建 spec
List<Long> specConfigIds = new ArrayList<>();
......@@ -301,9 +311,10 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
saveReqVO.setCategoryId(categoryId);
saveReqVO.setSource(source);
saveReqVO.setIntro(intro);
saveReqVO.setPicUrl(picUrl);
saveReqVO.setSales(sales);
saveReqVO.setStatus(status);
saveReqVO.setLocation(location);
// sales/status 不在模板里,给默认值
saveReqVO.setSales(0);
saveReqVO.setStatus(ResourceSpuStatus.ONLINE.getValue());
saveReqVO.setSpecConfigIds(specConfigIds);
resourceSpuService.createResourceSpu(saveReqVO);
......@@ -348,6 +359,29 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
}
/**
* 解析算力来源:按字典 label 转 value,兼容用户直接填 value 的情况
*/
private String resolveSource(String input,
Map<String, String> sourceLabelToValue,
Map<String, String> sourceValueToLabel) {
if (StrUtil.isBlank(input)) {
return null;
}
String trimmed = input.trim();
// 1. 已经是字典 value(如 "cooperative"),原样返回
if (sourceValueToLabel.containsKey(trimmed)) {
return trimmed;
}
// 2. 是字典 label(如 "合作"),转为 value
String mapped = sourceLabelToValue.get(trimmed);
if (mapped != null) {
return mapped;
}
// 3. 字典里查不到,按原值落库(不阻断导入,保留用户输入)
return trimmed;
}
/**
* 查/建规格项:先按 (configCategory, configOption) 查 spec 表,不存在则新建
*/
private Long getOrCreateSpecId(String configCategory, String configOption,
......@@ -384,30 +418,4 @@ public class ResourceSpuImportServiceImpl implements ResourceSpuImportService {
return row.get(idx);
}
private Integer parseIntOrDefault(String s, Integer defaultVal) {
if (StrUtil.isBlank(s)) {
return defaultVal;
}
try {
return Integer.parseInt(s.trim());
} catch (NumberFormatException e) {
return defaultVal;
}
}
private Integer parseStatusOrDefault(String s, Integer defaultVal) {
if (StrUtil.isBlank(s)) {
return defaultVal;
}
String trimmed = s.trim();
// 优先按 label 匹配
for (ResourceSpuStatus st : ResourceSpuStatus.values()) {
if (st.getLabel().equals(trimmed)) {
return st.getValue();
}
}
// 退化为数字
return parseIntOrDefault(trimmed, defaultVal);
}
}
......@@ -55,6 +55,10 @@ public class ResourceSpuServiceImpl implements ResourceSpuService {
if (CollUtil.isEmpty(createReqVO.getSpecConfigIds())) {
throw exception(RESOURCE_SPU_SPEC_REQUIRED);
}
// 1.1 销量表单不维护,默认 0(由订单支付回调自动累加)
if (createReqVO.getSales() == null) {
createReqVO.setSales(0);
}
// 2. 校验:所有 specConfigIds 在规格字典里存在
validateSpecConfigIdsExist(createReqVO.getSpecConfigIds());
......
......@@ -55,4 +55,18 @@
GROUP BY spu.area_id
</select>
<!--
订单支付成功回调里调用,累加 SPU 销量。
increment: 正数加、负数减(按需扩展,如订单取消时扣减)。
只对未删除的 SPU 生效(避免误加到已软删的 SPU 上)。
-->
<update id="incrSalesById">
UPDATE compute_resource_spu
SET sales = sales + #{increment},
updater = #{updater},
update_time = NOW()
WHERE id = #{id}
AND deleted = 0
</update>
</mapper>
-- ============================================================
-- 算力资源配置 · 算力字段扩展
-- 目标:为 compute_resource_config 表加 compute 字段
-- 算力资源配置 · 算力字段扩展 + update_time 默认值修复
-- 目标:
-- 1) 修复 compute_resource_config 表 update_time 字段的非法默认值
-- (原: DEFAULT '0000-00-00 00:00:00' 违反 MySQL 8.0 NO_ZERO_DATE)
-- (新: DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP)
-- 2) 为 compute_resource_config 表加 compute 字段
-- 适用库:new_computility(线上库名若不同请修改下方 USE 那一行)
-- 前置:无
-- 配套:
......@@ -14,17 +18,60 @@
-- - 默认 0,老数据不破坏
--
-- 生产环境安全说明:
-- - ALTER TABLE ... ADD COLUMN 是 MySQL 5.6+ 的 Online DDL,
-- - ALTER TABLE ... ADD/MODIFY COLUMN 是 MySQL 5.6+ 的 Online DDL,
-- 仅修改表元数据,不全表重建;ALGORITHM=INPLACE, LOCK=NONE
-- 期间允许 DML(不阻塞业务),但首尾仍需元数据锁几毫秒
-- - 适用于表数据量 1w 行以内秒级完成;若该表已超 50w 行,
-- 建议低峰期执行(22:00 之后)
-- - 本脚本幂等:若已存在 compute 字段会跳过并提示
-- - 本脚本幂等:update_time 已修复会跳过;compute 字段已存在会跳过
-- ============================================================
USE `new_computility`;
-- ---------- 幂等性检查 ----------
-- ============================================================
-- 步骤 1:探测 update_time 字段当前定义
-- ============================================================
-- 期望看到 COLUMN_DEFAULT = '0000-00-00 00:00:00' 表示待修复
-- 期望看到 COLUMN_DEFAULT = 'CURRENT_TIMESTAMP' 表示已修复
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, EXTRA
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'compute_resource_config'
AND COLUMN_NAME = 'update_time';
-- ============================================================
-- 步骤 2:修复 update_time 字段默认值(仅当尚未修复时执行)
-- ============================================================
-- 幂等性检查
SET @update_time_needs_fix := (
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'compute_resource_config'
AND COLUMN_NAME = 'update_time'
AND COLUMN_DEFAULT = '0000-00-00 00:00:00'
);
SET @msg := IF(@update_time_needs_fix > 0,
'update_time 默认值为 0000-00-00 00:00:00,开始修复',
'update_time 默认值已正确或不存在问题,跳过修复');
SELECT @msg AS info;
-- 仅在 update_time 默认值是 0000-00-00 时执行 MODIFY
-- 修复后: DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
SET @sql_update_time := IF(@update_time_needs_fix > 0,
"ALTER TABLE `compute_resource_config`
MODIFY COLUMN `update_time` DATETIME NOT NULL
DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
COMMENT '更新时间'",
"SELECT 'update_time 已修复,跳过' AS done");
PREPARE stmt FROM @sql_update_time;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- ============================================================
-- 步骤 3:探测 compute 字段是否已存在
-- ============================================================
SET @col_exists := (
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
......@@ -33,26 +80,44 @@ SET @col_exists := (
AND COLUMN_NAME = 'compute'
);
SET @msg := IF(@col_exists > 0,
SET @msg2 := IF(@col_exists > 0,
'compute 字段已存在,跳过本次新增',
'compute 字段不存在,开始执行新增');
SELECT @msg AS info;
SELECT @msg2 AS info;
-- ---------- 新增字段 ----------
-- ============================================================
-- 步骤 4:新增 compute 字段(仅当字段不存在时执行)
-- ============================================================
-- 使用 Online DDL:INPLACE 算法不重建表,NONE 锁允许并发 DML
ALTER TABLE `compute_resource_config`
SET @sql_add_compute := IF(@col_exists = 0,
"ALTER TABLE `compute_resource_config`
ADD COLUMN `compute` DECIMAL(18,2) NOT NULL DEFAULT 0
COMMENT '算力值(TOPS),默认 0,由运营手动配置' AFTER `detail_remark`,
ALGORITHM=INPLACE, LOCK=NONE;
ALGORITHM=INPLACE, LOCK=NONE",
"SELECT 'compute 字段已存在,跳过' AS done");
PREPARE stmt FROM @sql_add_compute;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- ---------- 验证 ----------
-- SHOW COLUMNS FROM `compute_resource_config` LIKE 'compute';
-- 期望结果:
-- compute | decimal(18,2) | NO | | 0.00 | 算力值(TOPS),默认 0,由运营手动配置
-- ============================================================
-- 步骤 5:验证
-- ============================================================
-- 期望结果 1:update_time 默认值 = CURRENT_TIMESTAMP
-- 期望结果 2:compute 字段存在,默认 0
SHOW COLUMNS FROM `compute_resource_config` LIKE 'update_time';
SHOW COLUMNS FROM `compute_resource_config` LIKE 'compute';
-- ============================================================
-- 回滚脚本(仅在确认要回滚时手动执行,默认不执行)
-- ============================================================
-- -- 1. 删除 compute 字段
-- ALTER TABLE `compute_resource_config`
-- DROP COLUMN `compute`,
-- ALGORITHM=INPLACE, LOCK=NONE;
--
-- -- 2. 还原 update_time 默认值(注意:这会把 update_time 默认值改回 0000-00-00,
-- -- 下次 ALTER TABLE 可能还会报错,但这是回滚到原始状态)
-- ALTER TABLE `compute_resource_config`
-- MODIFY COLUMN `update_time` DATETIME NOT NULL
-- DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP
-- COMMENT '更新时间';
-- ============================================================
-- 验证大屏"API 请求趋势"图表数据准确性(v2 - 修正表名)
-- 库:new_computility
-- 文件:2026_08_18_verify_api_calls_trend_v2.sql
-- 修正:原 v1 误用 infra_api_access_log,实际是 apihub_api_call_log
--
-- 【业务语义】
-- "API 请求趋势" = API 服务市场应用被用户调用的次数
-- 数据源表:apihub_api_call_log(项目自有的 apihub 模块)
-- 过滤字段:create_time(HomeIndexServiceImpl:213 setCreateTime)
-- 注意:表里还有 call_time 字段,但 service 实际按 create_time 过滤
--
-- 【与后端 service 对齐】
-- HomeIndexServiceImpl.getApiCallsData(dateType)
-- ├─ d (日): 近 7 天,截止到昨天
-- ├─ m (月): 近 30 天,截止到昨天
-- └─ y (年): 近 12 个月,截止到上月末
-- endTime = yesterday.atTime(23, 59, 59, 999_999_999)
-- ============================================================
USE `new_computility`;
-- ============================================================
-- 0) 今日日期确认 + 表数据健康检查
-- ============================================================
SELECT CURDATE() AS today, DATE_SUB(CURDATE(), INTERVAL 1 DAY) AS yesterday;
SELECT
COUNT(*) AS total_cnt,
MIN(create_time) AS min_ct,
MAX(create_time) AS max_ct
FROM apihub_api_call_log;
-- 如果 total_cnt = 0,说明 API 服务市场还没人调用过,图表必然为 0
-- 如果 max_ct 早于昨天,说明可能没新调用
-- ============================================================
-- 1) d(日)维度验证:近 7 天,截止到昨天
-- 假设今天是 2026-08-18 → 窗口 2026-08-11 ~ 2026-08-17
-- ============================================================
SELECT DATE(create_time) AS day, COUNT(*) AS cnt
FROM apihub_api_call_log
WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
AND create_time < CURDATE()
AND create_time >= '1970-01-01'
GROUP BY DATE(create_time)
ORDER BY day;
-- 期望 7 行,每行 day + cnt 对应图表日维度的 x + y
-- ============================================================
-- 2) m(月)维度验证:近 30 天,截止到昨天
-- 假设今天是 2026-08-18 → 窗口 2026-07-19 ~ 2026-08-17
-- ============================================================
SELECT DATE(create_time) AS day, COUNT(*) AS cnt
FROM apihub_api_call_log
WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
AND create_time < CURDATE()
AND create_time >= '1970-01-01'
GROUP BY DATE(create_time)
ORDER BY day;
-- 期望 30 行,对应图表月维度的 x + y
-- ============================================================
-- 3) y(年)维度验证:近 12 个月,截止到上月末
-- 假设今天是 2026-08-18 → 上月末 2026-07-31
-- 时间窗口 2025-08-01 00:00:00 ~ 2026-07-31 23:59:59.999999
-- ============================================================
SELECT DATE_FORMAT(create_time, '%Y-%m') AS month, COUNT(*) AS cnt
FROM apihub_api_call_log
WHERE create_time >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 12 MONTH), '%Y-%m-01')
AND create_time < DATE_FORMAT(CURDATE(), '%Y-%m-01')
AND create_time >= '1970-01-01'
GROUP BY DATE_FORMAT(create_time, '%Y-%m')
ORDER BY month;
-- 期望 12 行(每个月末一天),对应图表年维度的 x + y
-- 注意:图表 x 显示 "yyyy-MM" 格式(如 "2025-08"),不是月末日期
-- ============================================================
-- 4) 总数校验
-- ============================================================
SELECT
CURDATE() AS today,
(SELECT COUNT(*) FROM apihub_api_call_log WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)) AS cnt_7d,
(SELECT COUNT(*) FROM apihub_api_call_log WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)) AS cnt_30d,
(SELECT COUNT(*) FROM apihub_api_call_log) AS cnt_total;
-- cnt_7d 应等于 d 维度 7 行 cnt 之和
-- cnt_30d 应等于 m 维度 30 行 cnt 之和
-- cnt_total 是整张表总数
-- ============================================================
-- 5) 抽样验证:看最近 10 条记录
-- ============================================================
SELECT id, user_id, api_endpoint_name, call_time, response_status, create_time
FROM apihub_api_call_log
ORDER BY create_time DESC
LIMIT 10;
-- 用来:
-- (a) 确认有真实数据流入
-- (b) 看 create_time 与 call_time 是否一致(绝大多数应一致,差几毫秒)
-- (c) 看 response_status 分布(0=成功,其他=异常)
-- ============================================================
-- 6) 按用户/接口维度统计(辅助排查"为什么是这个数字")
-- ============================================================
SELECT
api_endpoint_name,
COUNT(*) AS cnt
FROM apihub_api_call_log
WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
AND create_time < CURDATE()
GROUP BY api_endpoint_name
ORDER BY cnt DESC
LIMIT 20;
-- 用来确认是哪些 API 贡献了大部分调用量
-- 也帮助理解图表上某个高点为什么突增
-- 算力资源订单 - 补齐线上缺失字段(紧急修复)
-- 库:new_computility
-- 背景:本地已加 2026_08_11 阶段 A/D 字段,线上漏执行,触发 getAllDashboardData 报 500
-- 本脚本:幂等版本(INFORMATION_SCHEMA.COLUMNS 探测,缺则加,不缺则跳过)
--
-- 涉及字段(基于错误堆栈 + 已知迁移文件):
-- - 阶段 A(商家发货):delivery_ip, delivery_username, delivery_password,
-- delivered_time, deliver_operator_id, deliver_operator_name
-- - 阶段 D(人工退款):refund_time
USE `new_computility`;
-- =====================================================
-- 工具:检查并 ADD COLUMN(缺则加,不缺则跳过)
-- =====================================================
DROP PROCEDURE IF EXISTS add_column_if_missing;
DELIMITER //
CREATE PROCEDURE add_column_if_missing(
IN p_table VARCHAR(64),
IN p_column VARCHAR(64),
IN p_definition TEXT
)
BEGIN
DECLARE v_exists INT DEFAULT 0;
SELECT COUNT(*) INTO v_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = p_table
AND COLUMN_NAME = p_column;
IF v_exists = 0 THEN
SET @sql = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_definition);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT CONCAT('[ADD] ', p_table, '.', p_column) AS action;
ELSE
SELECT CONCAT('[SKIP] ', p_table, '.', p_column, ' 已存在') AS action;
END IF;
END //
DELIMITER ;
-- =====================================================
-- 1. compute_resource_order 补齐字段
-- =====================================================
-- 1.1 阶段 A 发货字段
CALL add_column_if_missing('compute_resource_order', 'delivery_ip',
"VARCHAR(64) DEFAULT NULL COMMENT '发货 IP(商家发货时填写)' AFTER `pay_channel_code`");
CALL add_column_if_missing('compute_resource_order', 'delivery_username',
"VARCHAR(64) DEFAULT NULL COMMENT '发货初始用户名' AFTER `delivery_ip`");
CALL add_column_if_missing('compute_resource_order', 'delivery_password',
"VARCHAR(128) DEFAULT NULL COMMENT '发货初始密码' AFTER `delivery_username`");
CALL add_column_if_missing('compute_resource_order', 'delivered_time',
"DATETIME DEFAULT NULL COMMENT '发货时间(也是租赁开始时间)' AFTER `delivery_password`");
CALL add_column_if_missing('compute_resource_order', 'deliver_operator_id',
"BIGINT DEFAULT NULL COMMENT '发货操作人 ID' AFTER `delivered_time`");
CALL add_column_if_missing('compute_resource_order', 'deliver_operator_name',
"VARCHAR(64) DEFAULT NULL COMMENT '发货操作人姓名(冗余)' AFTER `deliver_operator_id`");
-- 1.2 阶段 D 退款时间
CALL add_column_if_missing('compute_resource_order', 'refund_time',
"DATETIME DEFAULT NULL COMMENT '退款时间(客服标已退款时填写)' AFTER `refund_price`");
-- =====================================================
-- 清理
-- =====================================================
DROP PROCEDURE IF EXISTS add_column_if_missing;
-- =====================================================
-- 验证
-- =====================================================
SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_DEFAULT, IS_NULLABLE, COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'compute_resource_order'
AND COLUMN_NAME IN (
'delivery_ip', 'delivery_username', 'delivery_password',
'delivered_time', 'deliver_operator_id', 'deliver_operator_name',
'refund_time'
)
ORDER BY ORDINAL_POSITION;
-- =====================================================
-- 回滚脚本(如需)
-- =====================================================
-- ALTER TABLE `compute_resource_order`
-- DROP COLUMN `delivery_ip`,
-- DROP COLUMN `delivery_username`,
-- DROP COLUMN `delivery_password`,
-- DROP COLUMN `delivered_time`,
-- DROP COLUMN `deliver_operator_id`,
-- DROP COLUMN `deliver_operator_name`,
-- DROP COLUMN `refund_time`;
-- 算力资源订单 - update_time 修复 + 补齐缺失字段(紧急修复)
-- 库:new_computility
-- 背景:
-- 1. 线上 compute_resource_order.update_time 默认值是 '0000-00-00 00:00:00',
-- 违反 MySQL 8.0 严格模式 NO_ZERO_DATE,导致任何 ALTER TABLE 失败
-- 2. 修复 update_time DEFAULT 为 CURRENT_TIMESTAMP 后,再 ADD COLUMN 补齐缺失字段
-- 3. 字段来源:2026_08_11_resource_order_delivery_fields.sql(阶段 A)
-- + 2026_08_11_resource_order_refund_time.sql(阶段 D)
--
-- 脚本:纯存储过程版本,幂等,可重复执行
USE `new_computility`;
-- =====================================================
-- 存储过程 1:修复 update_time 字段默认值
-- 仅当 DEFAULT 为 '0000-00-00 00:00:00' 时才 MODIFY
-- =====================================================
DROP PROCEDURE IF EXISTS fix_update_time_default;
DELIMITER //
CREATE PROCEDURE fix_update_time_default(
IN p_table VARCHAR(64)
)
BEGIN
DECLARE v_default VARCHAR(64) DEFAULT NULL;
SELECT COLUMN_DEFAULT INTO v_default
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = p_table
AND COLUMN_NAME = 'update_time';
IF v_default = '0000-00-00 00:00:00' THEN
SET @sql = CONCAT(
'ALTER TABLE `', p_table, '` ',
'MODIFY COLUMN `update_time` DATETIME NOT NULL ',
'DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ',
'COMMENT ''更新时间'''
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT CONCAT('[FIX] ', p_table, '.update_time DEFAULT updated to CURRENT_TIMESTAMP') AS action;
ELSE
SELECT CONCAT('[SKIP] ', p_table, '.update_time DEFAULT is OK (', IFNULL(v_default, 'NULL'), ')') AS action;
END IF;
END //
DELIMITER ;
-- =====================================================
-- 存储过程 2:检查并 ADD COLUMN(缺则加,不缺则跳过)
-- =====================================================
DROP PROCEDURE IF EXISTS add_column_if_missing;
DELIMITER //
CREATE PROCEDURE add_column_if_missing(
IN p_table VARCHAR(64),
IN p_column VARCHAR(64),
IN p_definition TEXT
)
BEGIN
DECLARE v_exists INT DEFAULT 0;
SELECT COUNT(*) INTO v_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = p_table
AND COLUMN_NAME = p_column;
IF v_exists = 0 THEN
SET @sql = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_definition);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT CONCAT('[ADD] ', p_table, '.', p_column) AS action;
ELSE
SELECT CONCAT('[SKIP] ', p_table, '.', p_column, ' 已存在') AS action;
END IF;
END //
DELIMITER ;
-- =====================================================
-- 步骤 1:先修 update_time 字段
-- =====================================================
CALL fix_update_time_default('compute_resource_order');
-- =====================================================
-- 步骤 2:补齐缺失字段
-- =====================================================
-- 2.1 阶段 A 发货字段
CALL add_column_if_missing('compute_resource_order', 'delivery_ip',
"VARCHAR(64) DEFAULT NULL COMMENT '发货 IP(商家发货时填写)' AFTER `pay_channel_code`");
CALL add_column_if_missing('compute_resource_order', 'delivery_username',
"VARCHAR(64) DEFAULT NULL COMMENT '发货初始用户名' AFTER `delivery_ip`");
CALL add_column_if_missing('compute_resource_order', 'delivery_password',
"VARCHAR(128) DEFAULT NULL COMMENT '发货初始密码' AFTER `delivery_username`");
CALL add_column_if_missing('compute_resource_order', 'delivered_time',
"DATETIME DEFAULT NULL COMMENT '发货时间(也是租赁开始时间)' AFTER `delivery_password`");
CALL add_column_if_missing('compute_resource_order', 'deliver_operator_id',
"BIGINT DEFAULT NULL COMMENT '发货操作人 ID' AFTER `delivered_time`");
CALL add_column_if_missing('compute_resource_order', 'deliver_operator_name',
"VARCHAR(64) DEFAULT NULL COMMENT '发货操作人姓名(冗余)' AFTER `deliver_operator_id`");
-- 2.2 阶段 D 退款时间
CALL add_column_if_missing('compute_resource_order', 'refund_time',
"DATETIME DEFAULT NULL COMMENT '退款时间(客服标已退款时填写)' AFTER `refund_price`");
-- =====================================================
-- 清理存储过程
-- =====================================================
DROP PROCEDURE IF EXISTS fix_update_time_default;
DROP PROCEDURE IF EXISTS add_column_if_missing;
-- =====================================================
-- 验证
-- =====================================================
SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_DEFAULT, IS_NULLABLE, COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'compute_resource_order'
AND COLUMN_NAME IN (
'update_time',
'delivery_ip', 'delivery_username', 'delivery_password',
'delivered_time', 'deliver_operator_id', 'deliver_operator_name',
'refund_time'
)
ORDER BY ORDINAL_POSITION;
-- =====================================================
-- 回滚脚本(如需)
-- =====================================================
-- ALTER TABLE `compute_resource_order`
-- DROP COLUMN `delivery_ip`,
-- DROP COLUMN `delivery_username`,
-- DROP COLUMN `delivery_password`,
-- DROP COLUMN `delivered_time`,
-- DROP COLUMN `deliver_operator_id`,
-- DROP COLUMN `deliver_operator_name`,
-- DROP COLUMN `refund_time`;
-- ALTER TABLE `compute_resource_order`
-- MODIFY COLUMN `update_time` DATETIME NOT NULL
-- DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP
-- COMMENT '更新时间';
-- 算力资源订单快照表 - 字段重命名 + 补齐 delivered_time
-- 库:new_computility
-- 背景:本地 DO 字段名已重命名(ip→delivery_ip, init_username→delivery_username,
-- init_password→delivery_password, +新增 delivered_time),但线上没执行迁移
-- 脚本:幂等版本(INFORMATION_SCHEMA 探测)
-- 安全:使用 RENAME COLUMN(MySQL 8.0+),不丢数据;如已为新字段名则跳过
USE `new_computility`;
-- =====================================================
-- 存储过程:智能重命名列(老列存在则 RENAME,新列已存在则 SKIP)
-- =====================================================
DROP PROCEDURE IF EXISTS rename_column_if_exists;
DELIMITER //
CREATE PROCEDURE rename_column_if_exists(
IN p_table VARCHAR(64),
IN p_old_column VARCHAR(64),
IN p_new_column VARCHAR(64)
)
BEGIN
DECLARE v_old_exists INT DEFAULT 0;
DECLARE v_new_exists INT DEFAULT 0;
SELECT COUNT(*) INTO v_old_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = p_table
AND COLUMN_NAME = p_old_column;
SELECT COUNT(*) INTO v_new_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = p_table
AND COLUMN_NAME = p_new_column;
IF v_old_exists = 1 AND v_new_exists = 0 THEN
SET @sql = CONCAT(
'ALTER TABLE `', p_table, '` ',
'RENAME COLUMN `', p_old_column, '` TO `', p_new_column, '`'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT CONCAT('[RENAME] ', p_table, '.', p_old_column, ' → ', p_new_column) AS action;
ELSEIF v_new_exists = 1 THEN
SELECT CONCAT('[SKIP] ', p_table, '.', p_new_column, ' 已存在(可能已重命名或新增)') AS action;
ELSE
SELECT CONCAT('[WARN] ', p_table, ' 缺少老列 ', p_old_column, ',也无法找到新列 ', p_new_column) AS action;
END IF;
END //
DELIMITER ;
-- =====================================================
-- 存储过程:检查并 ADD COLUMN(缺则加,不缺则跳过)
-- =====================================================
DROP PROCEDURE IF EXISTS add_column_if_missing;
DELIMITER //
CREATE PROCEDURE add_column_if_missing(
IN p_table VARCHAR(64),
IN p_column VARCHAR(64),
IN p_definition TEXT
)
BEGIN
DECLARE v_exists INT DEFAULT 0;
SELECT COUNT(*) INTO v_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = p_table
AND COLUMN_NAME = p_column;
IF v_exists = 0 THEN
SET @sql = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_definition);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT CONCAT('[ADD] ', p_table, '.', p_column) AS action;
ELSE
SELECT CONCAT('[SKIP] ', p_table, '.', p_column, ' 已存在') AS action;
END IF;
END //
DELIMITER ;
-- =====================================================
-- 执行修复
-- =====================================================
-- 1. 重命名老字段 → 新字段名
CALL rename_column_if_exists('compute_resource_order_snapshot', 'ip', 'delivery_ip');
CALL rename_column_if_exists('compute_resource_order_snapshot', 'init_username', 'delivery_username');
CALL rename_column_if_exists('compute_resource_order_snapshot', 'init_password', 'delivery_password');
-- 2. 新增 delivered_time
CALL add_column_if_missing('compute_resource_order_snapshot', 'delivered_time',
"DATETIME DEFAULT NULL COMMENT '发货时间(快照)' AFTER `delivery_password`");
-- =====================================================
-- 清理
-- =====================================================
DROP PROCEDURE IF EXISTS rename_column_if_exists;
DROP PROCEDURE IF EXISTS add_column_if_missing;
-- =====================================================
-- 验证
-- =====================================================
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'compute_resource_order_snapshot'
AND COLUMN_NAME IN (
'delivery_ip', 'delivery_username', 'delivery_password', 'delivered_time',
'ip', 'init_username', 'init_password' -- 检查老列是否还有残留
)
ORDER BY ORDINAL_POSITION;
-- =====================================================
-- 回滚脚本(如需)
-- =====================================================
-- ALTER TABLE `compute_resource_order_snapshot`
-- RENAME COLUMN `delivery_ip` TO `ip`,
-- RENAME COLUMN `delivery_username` TO `init_username`,
-- RENAME COLUMN `delivery_password` TO `init_password`,
-- DROP COLUMN `delivered_time`;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment