petstore-backend/db/migrations/20260801_create_store_customer.sql
2026-08-01 21:56:07 +08:00

139 lines
4.5 KiB
SQL

-- Petstore Phase 0: create stable store-customer relationship master.
-- Target: MySQL. Run after 20260801_split_service_identity.sql.
-- One-time migration; back up the database before execution.
CREATE TABLE t_store_customer (
id BIGINT NOT NULL AUTO_INCREMENT,
store_id BIGINT NOT NULL COMMENT '门店 ID',
customer_user_id BIGINT NULL COMMENT '全局 customer 账号,留资客户可空',
phone VARCHAR(20) NULL COMMENT '本店最后确认的联系手机号',
display_name VARCHAR(64) NULL COMMENT '门店视角客户称呼',
source VARCHAR(32) NOT NULL COMMENT '首次来源',
first_contact_at DATETIME NULL COMMENT '首次接触时间',
last_contact_at DATETIME NULL COMMENT '最近接触时间',
create_time DATETIME NOT NULL,
update_time DATETIME NOT NULL,
deleted TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id),
UNIQUE KEY uk_store_customer_user (store_id, customer_user_id),
UNIQUE KEY uk_store_customer_phone (store_id, phone),
KEY idx_store_customer_active_update (store_id, deleted, update_time),
KEY idx_store_customer_last_contact (store_id, last_contact_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='门店客户关系主档';
-- 历史预约客户建档。旧数据无法精确确定首次是自助或代客,统一记为 appointment。
INSERT INTO t_store_customer (
store_id,
customer_user_id,
phone,
display_name,
source,
first_contact_at,
last_contact_at,
create_time,
update_time,
deleted
)
SELECT
a.store_id,
a.customer_user_id,
MAX(u.phone),
MAX(COALESCE(NULLIF(u.name, ''), NULLIF(u.username, ''), '客户')),
'appointment',
MIN(COALESCE(a.create_time, a.appointment_time, NOW())),
MAX(COALESCE(a.update_time, a.create_time, a.appointment_time, NOW())),
NOW(),
NOW(),
0
FROM t_appointment a
JOIN t_user u
ON u.id = a.customer_user_id
AND u.deleted = 0
AND u.role = 'customer'
WHERE a.deleted = 0
AND a.store_id IS NOT NULL
AND a.customer_user_id IS NOT NULL
GROUP BY a.store_id, a.customer_user_id;
-- 报告留资建档。若同店同手机已由预约建档,只更新最近接触时间并保留首次来源。
INSERT INTO t_store_customer (
store_id,
customer_user_id,
phone,
display_name,
source,
first_contact_at,
last_contact_at,
create_time,
update_time,
deleted
)
SELECT
l.store_id,
MAX(CASE WHEN u.role = 'customer' AND u.deleted = 0 THEN u.id ELSE NULL END),
l.phone,
MAX(CASE
WHEN u.role = 'customer' AND u.deleted = 0
THEN COALESCE(NULLIF(u.name, ''), NULLIF(u.username, ''), '留资客户')
ELSE '留资客户'
END),
'report_lead',
MIN(COALESCE(l.create_time, NOW())),
MAX(COALESCE(l.update_time, l.create_time, NOW())),
NOW(),
NOW(),
0
FROM t_report_lead l
LEFT JOIN t_user u
ON u.phone = l.phone
WHERE l.store_id IS NOT NULL
AND l.phone IS NOT NULL
AND l.phone <> ''
GROUP BY l.store_id, l.phone
ON DUPLICATE KEY UPDATE
customer_user_id = COALESCE(t_store_customer.customer_user_id, VALUES(customer_user_id)),
phone = COALESCE(t_store_customer.phone, VALUES(phone)),
display_name = CASE
WHEN t_store_customer.display_name IS NULL OR t_store_customer.display_name = ''
THEN VALUES(display_name)
ELSE t_store_customer.display_name
END,
first_contact_at = LEAST(
COALESCE(t_store_customer.first_contact_at, VALUES(first_contact_at)),
VALUES(first_contact_at)
),
last_contact_at = GREATEST(
COALESCE(t_store_customer.last_contact_at, VALUES(last_contact_at)),
VALUES(last_contact_at)
),
update_time = NOW(),
deleted = 0;
-- Verification queries. All three counts should be 0 before application release.
SELECT COUNT(*) AS appointments_without_store_customer
FROM t_appointment a
LEFT JOIN t_store_customer sc
ON sc.store_id = a.store_id
AND sc.customer_user_id = a.customer_user_id
AND sc.deleted = 0
WHERE a.deleted = 0
AND a.customer_user_id IS NOT NULL
AND sc.id IS NULL;
SELECT COUNT(*) AS leads_without_store_customer
FROM t_report_lead l
LEFT JOIN t_store_customer sc
ON sc.store_id = l.store_id
AND sc.phone = l.phone
AND sc.deleted = 0
WHERE l.store_id IS NOT NULL
AND l.phone IS NOT NULL
AND l.phone <> ''
AND sc.id IS NULL;
SELECT COUNT(*) AS store_customers_linked_to_non_customer
FROM t_store_customer sc
JOIN t_user u ON u.id = sc.customer_user_id
WHERE sc.deleted = 0
AND (u.deleted <> 0 OR u.role <> 'customer');