feat: 优化商品洞察相关代码逻辑、新增性能优化 SQL 并更新前端样式
This commit is contained in:
@@ -12,6 +12,7 @@ let tagChart;
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initCharts();
|
||||
setupNavigation();
|
||||
setupRepurchasePeriod();
|
||||
|
||||
// Fetch cutoff dates + initial page data
|
||||
fetchDataCutoff();
|
||||
@@ -181,9 +182,14 @@ async function fetchTrend() {
|
||||
}
|
||||
|
||||
// ============== PAGE 2: PRODUCT INSIGHTS ==============
|
||||
async function fetchRepurchase() {
|
||||
|
||||
// 当前选中的复购率统计周期
|
||||
let repurchaseDays = 30;
|
||||
|
||||
async function fetchRepurchase(days) {
|
||||
if (days !== undefined) repurchaseDays = days;
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/product/repurchase`);
|
||||
const response = await fetch(`${BASE_URL}/product/repurchase?days=${repurchaseDays}`);
|
||||
if (!response.ok) return;
|
||||
const data = await response.json();
|
||||
|
||||
@@ -215,6 +221,20 @@ async function fetchRepurchase() {
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 初始化复购率周期切换控件
|
||||
function setupRepurchasePeriod() {
|
||||
const container = document.getElementById('repurchase-period');
|
||||
if (!container) return;
|
||||
container.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.segment-btn');
|
||||
if (!btn) return;
|
||||
container.querySelectorAll('.segment-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
const days = parseInt(btn.dataset.days);
|
||||
fetchRepurchase(days);
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchBasket() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/product/basket`);
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>餐饮零售数据中台 | 核心引擎</title>
|
||||
<!-- Google Fonts for Apple-like Typography -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
<!-- System font stack for Apple-like Typography (no external CDN dependency) -->
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<!-- ECharts -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
|
||||
@@ -92,17 +89,22 @@
|
||||
<div class="dashboard-grid">
|
||||
<div class="glass-card chart-container">
|
||||
<div class="chart-header">
|
||||
<h2>商品30天复购率与波士顿矩阵 <span class="info-icon" data-tooltip="每日洞察计算任务生成, 统计近30天各商品的购买/复购人数">ⓘ</span></h2>
|
||||
<h2>商品复购率与波士顿矩阵 <span class="info-icon" data-tooltip="每日洞察计算任务生成, 统计各商品的购买/复购人数">ⓘ</span></h2>
|
||||
<div class="segment-control" id="repurchase-period">
|
||||
<button class="segment-btn" data-days="7">7天</button>
|
||||
<button class="segment-btn" data-days="15">15天</button>
|
||||
<button class="segment-btn active" data-days="30">30天</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="data-table" id="repurchase-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>商品名称</th>
|
||||
<th>30天购买人数 <span class="info-icon info-icon-sm" data-tooltip="COUNT(DISTINCT yz_open_id), 近30天购买过该商品的不重复客户数">ⓘ</span></th>
|
||||
<th>30天复购人数 <span class="info-icon info-icon-sm" data-tooltip="近30天内购买该商品≥2次的客户数">ⓘ</span></th>
|
||||
<th id="th-buyer-count">购买人数 <span class="info-icon info-icon-sm" data-tooltip="COUNT(DISTINCT yz_open_id), 该周期内购买过该商品的不重复客户数">ⓘ</span></th>
|
||||
<th id="th-repurchase-count">复购人数 <span class="info-icon info-icon-sm" data-tooltip="该周期内购买该商品≥2次的客户数">ⓘ</span></th>
|
||||
<th>复购率 <span class="info-icon info-icon-sm" data-tooltip="= 复购人数 ÷ 购买人数 × 100%">ⓘ</span></th>
|
||||
<th>波士顿矩阵打标 <span class="info-icon info-icon-sm" data-tooltip="购买人数高+复购率高=核心引流爆款; 购买人数高+复购率低=体验差需淘汰; 购买人数低+复购率高=小众潜力款; 其他=长尾观测品">ⓘ</span></th>
|
||||
<th>波士顿矩阵打标 <span class="info-icon info-icon-sm" data-tooltip="购买人数高+复购率高=核心引流爆款; 购买人数高+复购率低=体验差需淘汰; 购买人数低+复购率高=小众潜力款; 其他=平庸款">ⓘ</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Inter", "Helvetica Neue", sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
@@ -302,6 +302,52 @@ body {
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* Chart header with optional controls */
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Apple-style Segmented Control */
|
||||
.segment-control {
|
||||
display: inline-flex;
|
||||
background: rgba(134, 134, 139, 0.08);
|
||||
border-radius: 10px;
|
||||
padding: 3px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.segment-btn {
|
||||
padding: 5px 16px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s ease;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.segment-btn:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.segment-btn.active {
|
||||
background: var(--text-primary);
|
||||
color: var(--bg-base);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
/* Dashboard Grid */
|
||||
.dashboard-grid {
|
||||
display: flex;
|
||||
|
||||
@@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
@@ -55,22 +56,74 @@ public class ProductController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品30天复购率与波士顿矩阵打标
|
||||
* 商品复购率与波士顿矩阵打标
|
||||
* @param days 统计周期: 7 / 15 / 30 (默认30)
|
||||
* - 30天: 直接读取预计算表 adm_item_repurchase
|
||||
* - 7/15天: 从订单明细实时计算
|
||||
*/
|
||||
@GetMapping("/repurchase")
|
||||
public List<Map<String, Object>> getRepurchaseRanking() {
|
||||
String sql = """
|
||||
public List<Map<String, Object>> getRepurchaseRanking(
|
||||
@RequestParam(value = "days", defaultValue = "30") int days) {
|
||||
|
||||
// 30天走预计算表 (洞察计算已写入)
|
||||
if (days == 30) {
|
||||
String sql = """
|
||||
SELECT
|
||||
item_name, outer_item_id,
|
||||
stat_date,
|
||||
purchaser_count_30d,
|
||||
repurchaser_count_30d,
|
||||
repurchase_rate_30d,
|
||||
matrix_tag
|
||||
FROM adm_item_repurchase
|
||||
WHERE stat_date = (SELECT MAX(stat_date) FROM adm_item_repurchase)
|
||||
ORDER BY repurchase_rate_30d DESC, purchaser_count_30d DESC
|
||||
""";
|
||||
return jdbcTemplate.queryForList(sql);
|
||||
}
|
||||
|
||||
// 白名单校验,防止 SQL 注入
|
||||
if (days != 7 && days != 15) days = 30;
|
||||
|
||||
// 7/15天: 实时计算 (MySQL 不支持 INTERVAL ? DAY 参数化)
|
||||
String sql = String.format("""
|
||||
WITH
|
||||
UserItemOrders AS (
|
||||
SELECT
|
||||
o.yz_open_id, o.outer_item_id,
|
||||
COUNT(DISTINCT o.tid) AS buy_freq
|
||||
FROM dwd_trade_order_detail o
|
||||
WHERE o.pay_time >= DATE_SUB(CURDATE(), INTERVAL %d DAY)
|
||||
AND o.yz_open_id IS NOT NULL AND o.yz_open_id != ''
|
||||
AND o.outer_item_id IS NOT NULL AND o.outer_item_id != ''
|
||||
AND o.title NOT LIKE '%%餐具%%'
|
||||
GROUP BY o.yz_open_id, o.outer_item_id
|
||||
),
|
||||
Aggregated AS (
|
||||
SELECT
|
||||
outer_item_id,
|
||||
COUNT(yz_open_id) AS purchaser_count_30d,
|
||||
SUM(CASE WHEN buy_freq >= 2 THEN 1 ELSE 0 END) AS repurchaser_count_30d
|
||||
FROM UserItemOrders
|
||||
GROUP BY outer_item_id
|
||||
)
|
||||
SELECT
|
||||
item_name, outer_item_id,
|
||||
stat_date,
|
||||
purchaser_count_30d,
|
||||
repurchaser_count_30d,
|
||||
repurchase_rate_30d,
|
||||
matrix_tag
|
||||
FROM adm_item_repurchase
|
||||
WHERE stat_date = (SELECT MAX(stat_date) FROM adm_item_repurchase)
|
||||
(SELECT MAX(d.title) FROM dwd_trade_order_detail d
|
||||
WHERE d.outer_item_id = a.outer_item_id AND d.title IS NOT NULL) AS item_name,
|
||||
a.outer_item_id,
|
||||
CURDATE() AS stat_date,
|
||||
a.purchaser_count_30d,
|
||||
a.repurchaser_count_30d,
|
||||
CAST(a.repurchaser_count_30d * 1.0 / a.purchaser_count_30d AS DECIMAL(5,4)) AS repurchase_rate_30d,
|
||||
CASE
|
||||
WHEN a.purchaser_count_30d >= 20 AND (a.repurchaser_count_30d * 1.0 / a.purchaser_count_30d) >= 0.2 THEN '核心引流爆款'
|
||||
WHEN a.purchaser_count_30d >= 20 AND (a.repurchaser_count_30d * 1.0 / a.purchaser_count_30d) < 0.05 THEN '体验差需淘汰'
|
||||
WHEN a.purchaser_count_30d < 10 AND (a.repurchaser_count_30d * 1.0 / a.purchaser_count_30d) >= 0.3 THEN '小众潜力款'
|
||||
ELSE '平庸款'
|
||||
END AS matrix_tag
|
||||
FROM Aggregated a
|
||||
ORDER BY repurchase_rate_30d DESC, purchaser_count_30d DESC
|
||||
""";
|
||||
""", days);
|
||||
return jdbcTemplate.queryForList(sql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ let tagChart;
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initCharts();
|
||||
setupNavigation();
|
||||
setupRepurchasePeriod();
|
||||
|
||||
// Fetch cutoff dates + initial page data
|
||||
fetchDataCutoff();
|
||||
@@ -181,9 +182,14 @@ async function fetchTrend() {
|
||||
}
|
||||
|
||||
// ============== PAGE 2: PRODUCT INSIGHTS ==============
|
||||
async function fetchRepurchase() {
|
||||
|
||||
// 当前选中的复购率统计周期
|
||||
let repurchaseDays = 30;
|
||||
|
||||
async function fetchRepurchase(days) {
|
||||
if (days !== undefined) repurchaseDays = days;
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/product/repurchase`);
|
||||
const response = await fetch(`${BASE_URL}/product/repurchase?days=${repurchaseDays}`);
|
||||
if (!response.ok) return;
|
||||
const data = await response.json();
|
||||
|
||||
@@ -215,6 +221,20 @@ async function fetchRepurchase() {
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 初始化复购率周期切换控件
|
||||
function setupRepurchasePeriod() {
|
||||
const container = document.getElementById('repurchase-period');
|
||||
if (!container) return;
|
||||
container.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.segment-btn');
|
||||
if (!btn) return;
|
||||
container.querySelectorAll('.segment-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
const days = parseInt(btn.dataset.days);
|
||||
fetchRepurchase(days);
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchBasket() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/product/basket`);
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>餐饮零售数据中台 | 核心引擎</title>
|
||||
<!-- Google Fonts for Apple-like Typography -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
<!-- System font stack for Apple-like Typography (no external CDN dependency) -->
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<!-- ECharts -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
|
||||
@@ -92,17 +89,22 @@
|
||||
<div class="dashboard-grid">
|
||||
<div class="glass-card chart-container">
|
||||
<div class="chart-header">
|
||||
<h2>商品30天复购率与波士顿矩阵 <span class="info-icon" data-tooltip="每日洞察计算任务生成, 统计近30天各商品的购买/复购人数">ⓘ</span></h2>
|
||||
<h2>商品复购率与波士顿矩阵 <span class="info-icon" data-tooltip="每日洞察计算任务生成, 统计各商品的购买/复购人数">ⓘ</span></h2>
|
||||
<div class="segment-control" id="repurchase-period">
|
||||
<button class="segment-btn" data-days="7">7天</button>
|
||||
<button class="segment-btn" data-days="15">15天</button>
|
||||
<button class="segment-btn active" data-days="30">30天</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="data-table" id="repurchase-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>商品名称</th>
|
||||
<th>30天购买人数 <span class="info-icon info-icon-sm" data-tooltip="COUNT(DISTINCT yz_open_id), 近30天购买过该商品的不重复客户数">ⓘ</span></th>
|
||||
<th>30天复购人数 <span class="info-icon info-icon-sm" data-tooltip="近30天内购买该商品≥2次的客户数">ⓘ</span></th>
|
||||
<th id="th-buyer-count">购买人数 <span class="info-icon info-icon-sm" data-tooltip="COUNT(DISTINCT yz_open_id), 该周期内购买过该商品的不重复客户数">ⓘ</span></th>
|
||||
<th id="th-repurchase-count">复购人数 <span class="info-icon info-icon-sm" data-tooltip="该周期内购买该商品≥2次的客户数">ⓘ</span></th>
|
||||
<th>复购率 <span class="info-icon info-icon-sm" data-tooltip="= 复购人数 ÷ 购买人数 × 100%">ⓘ</span></th>
|
||||
<th>波士顿矩阵打标 <span class="info-icon info-icon-sm" data-tooltip="购买人数高+复购率高=核心引流爆款; 购买人数高+复购率低=体验差需淘汰; 购买人数低+复购率高=小众潜力款; 其他=长尾观测品">ⓘ</span></th>
|
||||
<th>波士顿矩阵打标 <span class="info-icon info-icon-sm" data-tooltip="购买人数高+复购率高=核心引流爆款; 购买人数高+复购率低=体验差需淘汰; 购买人数低+复购率高=小众潜力款; 其他=平庸款">ⓘ</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Inter", "Helvetica Neue", sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
@@ -302,6 +302,52 @@ body {
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* Chart header with optional controls */
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Apple-style Segmented Control */
|
||||
.segment-control {
|
||||
display: inline-flex;
|
||||
background: rgba(134, 134, 139, 0.08);
|
||||
border-radius: 10px;
|
||||
padding: 3px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.segment-btn {
|
||||
padding: 5px 16px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s ease;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.segment-btn:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.segment-btn.active {
|
||||
background: var(--text-primary);
|
||||
color: var(--bg-base);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
/* Dashboard Grid */
|
||||
.dashboard-grid {
|
||||
display: flex;
|
||||
|
||||
33
sql/performance_indexes.sql
Normal file
33
sql/performance_indexes.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
-- =============================================
|
||||
-- 性能优化: 补全关键查询索引
|
||||
-- 执行方式: 在 MySQL 中直接执行,IF NOT EXISTS 保证幂等
|
||||
-- =============================================
|
||||
|
||||
-- 1. 订单表: pay_time (大盘总览/趋势图/订货建议 的 WHERE 条件)
|
||||
-- DDL 中已定义,此处兜底确保存在
|
||||
CREATE INDEX idx_pay_time ON dwd_trade_order_detail(pay_time);
|
||||
-- 如果已存在会报错,可忽略。或使用以下方式:
|
||||
-- ALTER TABLE dwd_trade_order_detail ADD INDEX idx_pay_time(pay_time);
|
||||
|
||||
-- 2. 订单表: status (数据截止时间查询 WHERE status = 'TRADE_SUCCESS')
|
||||
ALTER TABLE dwd_trade_order_detail ADD INDEX idx_status(status);
|
||||
|
||||
-- 3. 订单表: etl_update_time (客户聚合增量检测)
|
||||
ALTER TABLE dwd_trade_order_detail ADD INDEX idx_etl_update_time(etl_update_time);
|
||||
|
||||
-- 4. 订单表: outer_item_id (商品洞察/订货建议 GROUP BY)
|
||||
ALTER TABLE dwd_trade_order_detail ADD INDEX idx_outer_item_id(outer_item_id);
|
||||
|
||||
-- 5. 退款表: created_time (大盘总览退款额统计)
|
||||
ALTER TABLE dwd_trade_refund_detail ADD INDEX idx_created_time(created_time);
|
||||
|
||||
-- 6. 退款表: etl_update_time (增量检测)
|
||||
ALTER TABLE dwd_trade_refund_detail ADD INDEX idx_etl_update_time(etl_update_time);
|
||||
|
||||
-- 7. 客户表: etl_update_time (客户聚合增量检测)
|
||||
ALTER TABLE dim_customer_info ADD INDEX idx_etl_update_time(etl_update_time);
|
||||
|
||||
-- 验证索引:
|
||||
-- SHOW INDEX FROM dwd_trade_order_detail;
|
||||
-- SHOW INDEX FROM dwd_trade_refund_detail;
|
||||
-- SHOW INDEX FROM dim_customer_info;
|
||||
@@ -42,6 +42,9 @@ CREATE TABLE `dwd_trade_order_detail` (
|
||||
KEY `idx_tid` (`tid`),
|
||||
KEY `idx_pay_time` (`pay_time`),
|
||||
KEY `idx_update_time` (`update_time`),
|
||||
KEY `idx_etl_update_time` (`etl_update_time`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_outer_item_id` (`outer_item_id`),
|
||||
KEY `idx_buyer` (`buyer_phone`, `yz_open_id`),
|
||||
KEY `idx_item_sku` (`item_id`, `sku_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='交易明细事实表 (T+1有赞同步)';
|
||||
@@ -79,7 +82,8 @@ CREATE TABLE `dim_customer_info` (
|
||||
UNIQUE KEY `uk_yz_open_id` (`yz_open_id`),
|
||||
UNIQUE KEY `uk_mobile` (`mobile`), -- 可空唯一: MySQL 允许多个 NULL, 有手机号则保证不重复
|
||||
KEY `idx_wx_union_id` (`wx_union_id`),
|
||||
KEY `idx_last_pay_time` (`last_pay_time`)
|
||||
KEY `idx_last_pay_time` (`last_pay_time`),
|
||||
KEY `idx_etl_update_time` (`etl_update_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一客户维度宽表 (OneID及基础RFM视角)';
|
||||
|
||||
-- ==========================================
|
||||
@@ -170,7 +174,9 @@ CREATE TABLE `dwd_trade_refund_detail` (
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_refund_id` (`refund_id`),
|
||||
KEY `idx_tid` (`tid`),
|
||||
KEY `idx_created_time` (`created_time`),
|
||||
KEY `idx_success_time` (`success_time`),
|
||||
KEY `idx_etl_update_time` (`etl_update_time`),
|
||||
KEY `idx_yz_open_id` (`yz_open_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='售后与退款明细表';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user