This commit is contained in:
2026-06-30 10:33:56 +08:00
commit 6e047dc0a5
607 changed files with 65966 additions and 0 deletions
+589
View File
@@ -0,0 +1,589 @@
============================================================
-- 杜康好客 V3.1 数据库初始化脚本
-- MySQL 8.0+ utf8mb4_unicode_ci InnoDB
-- ============================================================
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
CREATE DATABASE IF NOT EXISTS dukang_haoke
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_unicode_ci;
USE dukang_haoke;
-- ===================== COMMON =============================
DROP TABLE IF EXISTS common_wx_app_config;
CREATE TABLE common_wx_app_config (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
client_app VARCHAR(32) NOT NULL COMMENT 'USER_MINI|PARTNER_MINI|HQ_MINI|SHOP_H5',
app_id VARCHAR(64) NOT NULL,
app_secret VARCHAR(128) NOT NULL,
mch_id VARCHAR(32) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_common_wx_app_config_client (client_app)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='四端微信配置';
DROP TABLE IF EXISTS common_resource;
CREATE TABLE common_resource (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
owner_type VARCHAR(32) NOT NULL COMMENT 'PRODUCT|STORE|PARTNER|USER|ORDER|PROMO|HQ',
owner_id BIGINT UNSIGNED NOT NULL COMMENT '归属业务ID',
biz_type VARCHAR(32) NOT NULL COMMENT 'COVER|ENV|CONTRACT|CAROUSEL|DETAIL|AVATAR|QRCODE|SIGN_PHOTO|VIDEO',
media_type VARCHAR(16) NOT NULL DEFAULT 'IMAGE' COMMENT 'IMAGE|VIDEO|FILE',
oss_bucket VARCHAR(64) NOT NULL COMMENT 'OSS Bucket',
oss_key VARCHAR(256) NOT NULL COMMENT 'OSS Object Key',
url VARCHAR(512) NOT NULL COMMENT 'CDN访问URL',
file_name VARCHAR(128) DEFAULT NULL,
file_size BIGINT UNSIGNED DEFAULT NULL COMMENT '字节',
mime_type VARCHAR(64) DEFAULT NULL,
sort_order INT NOT NULL DEFAULT 0,
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE|DELETED',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_common_resource_owner (owner_type, owner_id, biz_type),
KEY idx_common_resource_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='统一资源表(OSS)';
DROP TABLE IF EXISTS common_event;
CREATE TABLE common_event (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
event_type VARCHAR(32) NOT NULL COMMENT 'STORE_AUDIT|ORDER_STATUS|BENEFIT_LEDGER|HQ_OPERATION|PROMO_TOUCH',
ref_type VARCHAR(32) NOT NULL COMMENT 'ORDER|STORE|BENEFIT_COUPON|USER|PRODUCT|...',
ref_id BIGINT UNSIGNED NOT NULL,
actor_type VARCHAR(16) DEFAULT NULL COMMENT 'USER|STORE|PARTNER|HQ|SYSTEM',
actor_id BIGINT UNSIGNED DEFAULT NULL,
status VARCHAR(32) DEFAULT NULL COMMENT '事件子状态(如审核PENDING/APPROVED)',
param1 VARCHAR(128) DEFAULT NULL,
param1_desc VARCHAR(64) DEFAULT NULL,
param2 VARCHAR(128) DEFAULT NULL,
param2_desc VARCHAR(64) DEFAULT NULL,
param3 VARCHAR(128) DEFAULT NULL,
param3_desc VARCHAR(64) DEFAULT NULL,
amount1 DECIMAL(10,2) DEFAULT NULL COMMENT '仅BENEFIT_LEDGER:变动额',
amount2 DECIMAL(10,2) DEFAULT NULL COMMENT '仅BENEFIT_LEDGER:变动后余额',
remark VARCHAR(512) DEFAULT NULL,
extra_json JSON DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_common_event_ref (ref_type, ref_id, event_type),
KEY idx_common_event_type_created (event_type, created_at),
KEY idx_common_event_actor (actor_type, actor_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='统一事件表';
DROP TABLE IF EXISTS common_ticket;
CREATE TABLE common_ticket (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
ticket_no VARCHAR(32) NOT NULL,
ticket_type VARCHAR(32) NOT NULL COMMENT 'REFUND|RESHIPMENT|ALERT',
status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
ref_type VARCHAR(32) NOT NULL COMMENT 'ORDER|STORE|PARTNER|...',
ref_id BIGINT UNSIGNED NOT NULL,
operator_type VARCHAR(16) DEFAULT NULL COMMENT 'HQ|PARTNER|SYSTEM',
operator_id BIGINT UNSIGNED DEFAULT NULL,
param1 VARCHAR(128) DEFAULT NULL,
param1_desc VARCHAR(64) DEFAULT NULL,
param2 VARCHAR(128) DEFAULT NULL,
param2_desc VARCHAR(64) DEFAULT NULL,
param3 VARCHAR(128) DEFAULT NULL,
param3_desc VARCHAR(64) DEFAULT NULL,
remark VARCHAR(512) DEFAULT NULL,
extra_json JSON DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
completed_at DATETIME(3) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_common_ticket_no (ticket_no),
KEY idx_common_ticket_ref (ref_type, ref_id),
KEY idx_common_ticket_type_status (ticket_type, status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='通用工单表';
DROP TABLE IF EXISTS common_product_item;
CREATE TABLE common_product_item (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
sku_code VARCHAR(32) NOT NULL COMMENT 'SKU编码',
barcode_69 VARCHAR(32) NOT NULL COMMENT '69码(商品条码)',
name VARCHAR(128) NOT NULL,
subtitle VARCHAR(256) DEFAULT NULL,
aroma_type VARCHAR(16) NOT NULL COMMENT 'QINGXIANG|JIANGXIANG|NONGXIANG',
spec VARCHAR(128) NOT NULL,
price DECIMAL(10,2) NOT NULL,
benefit_amount DECIMAL(10,2) DEFAULT NULL COMMENT 'NULL=等同售价',
status VARCHAR(16) NOT NULL DEFAULT 'DRAFT' COMMENT 'DRAFT|ON_SALE|OFF_SALE',
sort_order INT NOT NULL DEFAULT 0,
cover_resource_id BIGINT UNSIGNED DEFAULT NULL COMMENT '主图 common_resource.id',
detail_content JSON DEFAULT NULL COMMENT '图文详情(纯文本结构)',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_common_product_item_sku (sku_code),
UNIQUE KEY uk_common_product_item_barcode (barcode_69),
KEY idx_common_product_item_status (status, aroma_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商品SKU';
DROP TABLE IF EXISTS common_store_category;
CREATE TABLE common_store_category (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
code VARCHAR(32) NOT NULL,
name VARCHAR(64) NOT NULL,
sort INT NOT NULL DEFAULT 0,
PRIMARY KEY (id),
UNIQUE KEY uk_common_store_category_code (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='门店餐饮分类';
DROP TABLE IF EXISTS common_promo_code;
CREATE TABLE common_promo_code (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
code VARCHAR(32) NOT NULL,
name VARCHAR(128) NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
qrcode_resource_id BIGINT UNSIGNED DEFAULT NULL COMMENT '小程序码 common_resource.id',
scan_count INT NOT NULL DEFAULT 0,
order_count INT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_common_promo_code_code (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推广码';
-- ===================== PARTNER(先于 city/store =============================
DROP TABLE IF EXISTS partner_partner;
CREATE TABLE partner_partner (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
company_name VARCHAR(128) NOT NULL,
address VARCHAR(256) NOT NULL,
contact_phone VARCHAR(20) NOT NULL,
contract_no VARCHAR(64) DEFAULT NULL COMMENT '合同编号',
contract_signed_at DATETIME(3) DEFAULT NULL,
contract_expire_at DATETIME(3) DEFAULT NULL,
bank_account_name VARCHAR(64) DEFAULT NULL,
bank_account_no VARCHAR(32) DEFAULT NULL,
bank_branch VARCHAR(128) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_partner_partner_phone (contact_phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='城市合伙人主体';
DROP TABLE IF EXISTS common_city;
CREATE TABLE common_city (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
code VARCHAR(16) NOT NULL,
name VARCHAR(64) NOT NULL,
province VARCHAR(32) NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'PENDING',
partner_id BIGINT UNSIGNED DEFAULT NULL,
local_min_qty INT NOT NULL DEFAULT 2,
cross_min_qty INT NOT NULL DEFAULT 6,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_common_city_code (code),
KEY idx_common_city_partner (partner_id),
CONSTRAINT fk_common_city_partner FOREIGN KEY (partner_id) REFERENCES partner_partner(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='开城配置';
DROP TABLE IF EXISTS common_city_commission_rule;
CREATE TABLE common_city_commission_rule (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
city_id BIGINT UNSIGNED NOT NULL,
order_commission_rate DECIMAL(5,4) NOT NULL DEFAULT 0.0000,
redeem_commission_rate DECIMAL(5,4) NOT NULL DEFAULT 0.0000,
partner_profit_rate DECIMAL(5,4) NOT NULL DEFAULT 0.3500,
store_settlement_rate DECIMAL(5,4) NOT NULL DEFAULT 0.6000,
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_common_city_commission_city (city_id),
CONSTRAINT fk_common_city_commission_city FOREIGN KEY (city_id) REFERENCES common_city(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='城市佣金规则';
DROP TABLE IF EXISTS partner_account;
CREATE TABLE partner_account (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
partner_id BIGINT UNSIGNED NOT NULL,
phone VARCHAR(20) NOT NULL,
name VARCHAR(64) NOT NULL,
wx_open_id VARCHAR(64) DEFAULT NULL,
wx_union_id VARCHAR(64) DEFAULT NULL,
is_primary TINYINT NOT NULL DEFAULT 0,
parent_account_id BIGINT UNSIGNED DEFAULT NULL,
staff_role VARCHAR(16) DEFAULT NULL COMMENT 'PARTNER|INTERNAL|PROMOTER',
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
last_login_at DATETIME(3) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_partner_account_phone (phone),
KEY idx_partner_account_partner (partner_id),
CONSTRAINT fk_partner_account_partner FOREIGN KEY (partner_id) REFERENCES partner_partner(id) ON DELETE RESTRICT,
CONSTRAINT fk_partner_account_parent FOREIGN KEY (parent_account_id) REFERENCES partner_account(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合伙人账号';
DROP TABLE IF EXISTS partner_bill;
CREATE TABLE partner_bill (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
bill_no VARCHAR(32) NOT NULL,
partner_id BIGINT UNSIGNED NOT NULL,
period_start DATETIME(3) NOT NULL,
period_end DATETIME(3) NOT NULL,
order_commission DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '下单佣金汇总',
redeem_commission DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '核销佣金汇总',
total_amount DECIMAL(10,2) NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'DRAFT',
confirmed_at DATETIME(3) DEFAULT NULL,
paid_at DATETIME(3) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_partner_bill_no (bill_no),
KEY idx_partner_bill_partner_status (partner_id, status),
CONSTRAINT fk_partner_bill_partner FOREIGN KEY (partner_id) REFERENCES partner_partner(id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合伙人T+30账单';
-- ===================== HQ =============================
DROP TABLE IF EXISTS hq_account;
CREATE TABLE hq_account (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
phone VARCHAR(20) NOT NULL,
name VARCHAR(64) NOT NULL,
admin_role VARCHAR(32) NOT NULL DEFAULT 'OPS',
wx_open_id VARCHAR(64) DEFAULT NULL,
wx_union_id VARCHAR(64) DEFAULT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
last_login_at DATETIME(3) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_hq_account_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='总部账号';
-- ===================== USER =============================
DROP TABLE IF EXISTS user_user;
CREATE TABLE user_user (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_no VARCHAR(20) NOT NULL,
phone VARCHAR(20) NOT NULL,
wx_open_id VARCHAR(64) DEFAULT NULL,
wx_union_id VARCHAR(64) DEFAULT NULL,
nickname VARCHAR(64) DEFAULT NULL,
avatar_resource_id BIGINT UNSIGNED DEFAULT NULL COMMENT '头像 common_resource.id',
status TINYINT NOT NULL DEFAULT 1,
source_type VARCHAR(32) NOT NULL DEFAULT 'ORGANIC' COMMENT 'ORGANIC|PROMO_CODE|SHARE_LINK|FRIEND_REFERRAL|OFFLINE_EVENT|OTHER',
source_ref_id BIGINT UNSIGNED DEFAULT NULL COMMENT 'promo_code_id 或 referrer_user_id',
source_label VARCHAR(128) DEFAULT NULL COMMENT '渠道名称快照',
referrer_user_id BIGINT UNSIGNED DEFAULT NULL COMMENT '好友推荐人 user_user.id',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_user_phone (phone),
UNIQUE KEY uk_user_user_no (user_no),
KEY idx_user_user_source (source_type, source_ref_id),
KEY idx_user_user_referrer (referrer_user_id),
KEY idx_user_user_wx_open (wx_open_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='C端用户';
DROP TABLE IF EXISTS user_address;
CREATE TABLE user_address (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
receiver_name VARCHAR(32) NOT NULL,
phone VARCHAR(20) NOT NULL,
province VARCHAR(32) NOT NULL,
city VARCHAR(32) NOT NULL,
district VARCHAR(32) NOT NULL,
detail VARCHAR(256) NOT NULL,
latitude DECIMAL(10,7) DEFAULT NULL,
longitude DECIMAL(10,7) DEFAULT NULL,
is_default TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_user_address_user (user_id),
CONSTRAINT fk_user_address_user FOREIGN KEY (user_id) REFERENCES user_user(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户收货地址';
DROP TABLE IF EXISTS user_city_preference;
CREATE TABLE user_city_preference (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
selected_city_code VARCHAR(16) DEFAULT NULL,
selected_district VARCHAR(32) DEFAULT NULL,
locate_city_code VARCHAR(16) DEFAULT NULL,
locate_district VARCHAR(32) DEFAULT NULL,
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_city_preference_user (user_id),
CONSTRAINT fk_user_city_preference_user FOREIGN KEY (user_id) REFERENCES user_user(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户城市偏好';
DROP TABLE IF EXISTS user_promo_attribution;
CREATE TABLE user_promo_attribution (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
promo_code_id BIGINT UNSIGNED NOT NULL,
channel_name VARCHAR(128) NOT NULL,
first_touch_at DATETIME(3) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_user_promo_attribution_user (user_id),
KEY idx_user_promo_attribution_promo (promo_code_id),
CONSTRAINT fk_user_promo_attribution_user FOREIGN KEY (user_id) REFERENCES user_user(id) ON DELETE CASCADE,
CONSTRAINT fk_user_promo_attribution_promo FOREIGN KEY (promo_code_id) REFERENCES common_promo_code(id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推广首次触达(统计)';
-- ===================== STORE =============================
DROP TABLE IF EXISTS store_store;
CREATE TABLE store_store (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
city_id BIGINT UNSIGNED NOT NULL,
partner_id BIGINT UNSIGNED NOT NULL,
category_id BIGINT UNSIGNED DEFAULT NULL,
name VARCHAR(128) NOT NULL,
phone VARCHAR(20) NOT NULL,
province VARCHAR(32) NOT NULL,
city_name VARCHAR(32) NOT NULL COMMENT '市(冗余)',
district VARCHAR(32) NOT NULL,
address VARCHAR(256) NOT NULL,
latitude DECIMAL(10,7) DEFAULT NULL,
longitude DECIMAL(10,7) DEFAULT NULL,
intro TEXT DEFAULT NULL,
cover_resource_id BIGINT UNSIGNED DEFAULT NULL COMMENT '门头图',
avg_price DECIMAL(10,2) DEFAULT NULL,
rating DECIMAL(3,2) DEFAULT NULL,
tags JSON DEFAULT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'PAUSED',
open_time VARCHAR(8) DEFAULT NULL,
close_time VARCHAR(8) DEFAULT NULL,
bank_account_name VARCHAR(64) DEFAULT NULL,
bank_account_no VARCHAR(32) DEFAULT NULL,
bank_branch VARCHAR(128) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_store_store_city_status (city_id, status),
KEY idx_store_store_partner (partner_id),
CONSTRAINT fk_store_store_city FOREIGN KEY (city_id) REFERENCES common_city(id) ON DELETE RESTRICT,
CONSTRAINT fk_store_store_partner FOREIGN KEY (partner_id) REFERENCES partner_partner(id) ON DELETE RESTRICT,
CONSTRAINT fk_store_store_category FOREIGN KEY (category_id) REFERENCES common_store_category(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='餐饮门店';
DROP TABLE IF EXISTS store_account;
CREATE TABLE store_account (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
store_id BIGINT UNSIGNED NOT NULL,
phone VARCHAR(20) NOT NULL,
name VARCHAR(64) NOT NULL,
wx_open_id VARCHAR(64) DEFAULT NULL,
wx_union_id VARCHAR(64) DEFAULT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
last_login_at DATETIME(3) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_store_account_store (store_id),
UNIQUE KEY uk_store_account_phone (phone),
CONSTRAINT fk_store_account_store FOREIGN KEY (store_id) REFERENCES store_store(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='门店H5账号';
-- ===================== USER ORDER =============================
DROP TABLE IF EXISTS user_order_item;
DROP TABLE IF EXISTS user_order_delivery;
DROP TABLE IF EXISTS user_order;
CREATE TABLE user_order (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
order_no VARCHAR(32) NOT NULL,
order_type VARCHAR(16) NOT NULL DEFAULT 'NORMAL' COMMENT 'NORMAL|RESHIPMENT',
user_id BIGINT UNSIGNED NOT NULL,
city_id BIGINT UNSIGNED NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'PENDING_PAY',
pay_status VARCHAR(16) NOT NULL DEFAULT 'UNPAID' COMMENT 'UNPAID|PAYING|PAID|REFUNDING|REFUNDED',
delivery_type VARCHAR(16) NOT NULL COMMENT 'LOCAL|CROSS_CITY',
origin_order_id BIGINT UNSIGNED DEFAULT NULL,
promo_code_id BIGINT UNSIGNED DEFAULT NULL,
channel_source VARCHAR(128) DEFAULT NULL,
product_id BIGINT UNSIGNED NOT NULL COMMENT '商品 common_product_item.id',
barcode_69 VARCHAR(32) NOT NULL COMMENT '69码快照',
product_name VARCHAR(128) NOT NULL COMMENT '商品名称快照',
product_spec VARCHAR(128) NOT NULL COMMENT '规格快照',
image_resource_id BIGINT UNSIGNED DEFAULT NULL COMMENT '商品图快照 common_resource.id',
quantity INT NOT NULL COMMENT '购买数量',
list_unit_price DECIMAL(10,2) NOT NULL COMMENT '标价单价',
list_amount DECIMAL(10,2) NOT NULL COMMENT '标价总额',
discount_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '优惠金额',
product_amount DECIMAL(10,2) NOT NULL COMMENT '商品应付',
freight_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '运费',
freight_pay_type VARCHAR(8) DEFAULT NULL COMMENT 'FREE|COD',
pay_amount DECIMAL(10,2) NOT NULL COMMENT '实付总额',
benefit_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '本单发放权益',
receiver_name VARCHAR(32) NOT NULL,
receiver_phone VARCHAR(20) NOT NULL,
receiver_address TEXT NOT NULL,
receiver_province VARCHAR(32) NOT NULL,
receiver_city VARCHAR(32) NOT NULL,
receiver_district VARCHAR(32) NOT NULL,
pay_external_no VARCHAR(64) DEFAULT NULL COMMENT '微信交易号(冗余)',
paid_at DATETIME(3) DEFAULT NULL COMMENT '支付时间',
shipped_at DATETIME(3) DEFAULT NULL COMMENT '发货时间(冗余=user_order_delivery.shipping_at)',
completed_at DATETIME(3) DEFAULT NULL COMMENT '完成时间',
cancelled_at DATETIME(3) DEFAULT NULL COMMENT '取消时间',
pay_expire_at DATETIME(3) DEFAULT NULL COMMENT '待付款过期时间',
remark VARCHAR(512) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_order_no (order_no),
KEY idx_user_order_user_status (user_id, status),
KEY idx_user_order_city_created (city_id, created_at),
KEY idx_user_order_product (product_id),
KEY idx_user_order_barcode (barcode_69),
KEY idx_user_order_pay_external (pay_external_no),
CONSTRAINT fk_user_order_user FOREIGN KEY (user_id) REFERENCES user_user(id) ON DELETE RESTRICT,
CONSTRAINT fk_user_order_city FOREIGN KEY (city_id) REFERENCES common_city(id) ON DELETE RESTRICT,
CONSTRAINT fk_user_order_origin FOREIGN KEY (origin_order_id) REFERENCES user_order(id) ON DELETE SET NULL,
CONSTRAINT fk_user_order_promo FOREIGN KEY (promo_code_id) REFERENCES common_promo_code(id) ON DELETE SET NULL,
CONSTRAINT fk_user_order_product FOREIGN KEY (product_id) REFERENCES common_product_item(id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单(含商品快照,V1单SKU)';
DROP TABLE IF EXISTS user_order_delivery;
CREATE TABLE user_order_delivery (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
order_id BIGINT UNSIGNED NOT NULL,
provider VARCHAR(16) NOT NULL COMMENT 'XFX|LOGISTICS|MANUAL',
provider_order_no VARCHAR(64) DEFAULT NULL,
tracking_no VARCHAR(64) DEFAULT NULL,
out_warehouse_at DATETIME(3) DEFAULT NULL,
shipping_at DATETIME(3) DEFAULT NULL,
delivered_at DATETIME(3) DEFAULT NULL,
sign_photo_resource_id BIGINT UNSIGNED DEFAULT NULL COMMENT '签收照片 common_resource.id',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_order_delivery_order (order_id),
CONSTRAINT fk_user_order_delivery_order FOREIGN KEY (order_id) REFERENCES user_order(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单配送(与user_order 1:1)';
-- ===================== BENEFIT & REDEEM =============================
DROP TABLE IF EXISTS user_benefit_coupon;
CREATE TABLE user_benefit_coupon (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
coupon_no VARCHAR(32) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
order_id BIGINT UNSIGNED NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
used_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
balance DECIMAL(10,2) NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
source_product VARCHAR(128) NOT NULL,
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_benefit_coupon_no (coupon_no),
KEY idx_user_benefit_coupon_user (user_id, status),
CONSTRAINT fk_user_benefit_coupon_user FOREIGN KEY (user_id) REFERENCES user_user(id) ON DELETE RESTRICT,
CONSTRAINT fk_user_benefit_coupon_order FOREIGN KEY (order_id) REFERENCES user_order(id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='好客权益券';
DROP TABLE IF EXISTS user_redeem_record;
CREATE TABLE user_redeem_record (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
redeem_no VARCHAR(32) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
coupon_id BIGINT UNSIGNED NOT NULL,
store_id BIGINT UNSIGNED NOT NULL,
amount DECIMAL(10,2) NOT NULL,
settle_amount DECIMAL(10,2) NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_redeem_record_no (redeem_no),
KEY idx_user_redeem_record_store (store_id, created_at),
CONSTRAINT fk_user_redeem_record_user FOREIGN KEY (user_id) REFERENCES user_user(id) ON DELETE RESTRICT,
CONSTRAINT fk_user_redeem_record_coupon FOREIGN KEY (coupon_id) REFERENCES user_benefit_coupon(id) ON DELETE RESTRICT,
CONSTRAINT fk_user_redeem_record_store FOREIGN KEY (store_id) REFERENCES store_store(id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='核销记录';
DROP TABLE IF EXISTS user_store_rating;
CREATE TABLE user_store_rating (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
redeem_record_id BIGINT UNSIGNED NOT NULL,
store_id BIGINT UNSIGNED NOT NULL,
service_score TINYINT NOT NULL,
env_score TINYINT NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_store_rating_redeem (redeem_record_id),
CONSTRAINT fk_user_store_rating_redeem FOREIGN KEY (redeem_record_id) REFERENCES user_redeem_record(id) ON DELETE CASCADE,
CONSTRAINT fk_user_store_rating_store FOREIGN KEY (store_id) REFERENCES store_store(id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='核销评价';
DROP TABLE IF EXISTS store_payout;
CREATE TABLE store_payout (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
redeem_record_id BIGINT UNSIGNED NOT NULL,
store_id BIGINT UNSIGNED NOT NULL,
redeem_amount DECIMAL(10,2) NOT NULL,
payout_amount DECIMAL(10,2) NOT NULL,
settlement_rate DECIMAL(5,4) NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'PENDING',
expected_pay_at DATETIME(3) NOT NULL,
paid_at DATETIME(3) DEFAULT NULL,
batch_no VARCHAR(32) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_store_payout_redeem (redeem_record_id),
KEY idx_store_payout_store_status (store_id, status),
CONSTRAINT fk_store_payout_redeem FOREIGN KEY (redeem_record_id) REFERENCES user_redeem_record(id) ON DELETE RESTRICT,
CONSTRAINT fk_store_payout_store FOREIGN KEY (store_id) REFERENCES store_store(id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='门店T+1打款';
-- ===================== LOG =============================
DROP TABLE IF EXISTS log_third_party;
CREATE TABLE log_third_party (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
provider VARCHAR(32) NOT NULL COMMENT 'WECHAT_PAY|WECHAT_REFUND|WECHAT_AUTH|WECHAT_MAP|XFX|SMS|LOGISTICS',
scene VARCHAR(64) NOT NULL COMMENT '业务场景',
ref_type VARCHAR(32) DEFAULT NULL COMMENT 'ORDER|USER|STORE|...',
ref_id BIGINT UNSIGNED DEFAULT NULL,
request_url VARCHAR(512) DEFAULT NULL,
request_body JSON DEFAULT NULL COMMENT '发送参数',
response_body JSON DEFAULT NULL COMMENT '响应数据',
external_no VARCHAR(128) DEFAULT NULL COMMENT '第三方单号',
amount DECIMAL(10,2) DEFAULT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING|SUCCESS|FAILED',
error_message VARCHAR(512) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_log_third_party_ref (ref_type, ref_id),
KEY idx_log_third_party_provider_scene (provider, scene, created_at),
KEY idx_log_third_party_external (external_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='第三方交互记录';
DROP TABLE IF EXISTS log_user_analytics;
CREATE TABLE log_user_analytics (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED DEFAULT NULL COMMENT '未登录可为NULL',
session_id VARCHAR(64) DEFAULT NULL COMMENT '会话ID',
event_name VARCHAR(64) NOT NULL COMMENT '见§4.1 event_name 清单',
client_app VARCHAR(32) DEFAULT NULL COMMENT 'USER_MINI|PARTNER_MINI|HQ_MINI|SHOP_H5',
page_path VARCHAR(128) DEFAULT NULL COMMENT '页面路径',
ref_type VARCHAR(32) DEFAULT NULL COMMENT 'PRODUCT|STORE|ORDER|TAB|ADDRESS|PROMO|...',
ref_id BIGINT UNSIGNED DEFAULT NULL,
keyword VARCHAR(128) DEFAULT NULL COMMENT '搜索关键词',
source_type VARCHAR(32) DEFAULT NULL COMMENT 'register事件:来源类型快照',
source_ref_id BIGINT UNSIGNED DEFAULT NULL COMMENT 'register事件:来源ID',
extra_json JSON DEFAULT NULL COMMENT 'PRD埋点参数',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_log_user_analytics_user_created (user_id, created_at),
KEY idx_log_user_analytics_event_created (event_name, created_at),
KEY idx_log_user_analytics_session (session_id),
KEY idx_log_user_analytics_ref (ref_type, ref_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户行为埋点日志';
SET FOREIGN_KEY_CHECKS = 1;
+990
View File
@@ -0,0 +1,990 @@
// 杜康好客 · V2.1 数据模型
// 约束说明见 doc/数据库设计2-杜康好客.md
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
// ─── 枚举 ─────────────────────────────────────────────
enum ClientApp {
USER_MINI
USER_H5
PARTNER_MINI
PARTNER_H5
HQ_MINI
SHOP_H5
}
enum AccountStatus {
ACTIVE
DISABLED
}
enum HqAdminRole {
SUPER_ADMIN
OPS
FINANCE
CUSTOMER_SERVICE
}
enum PartnerStaffRole {
PARTNER
INTERNAL
PROMOTER
}
enum CityStatus {
PENDING
ACTIVE
PAUSED
}
enum AromaType {
QINGXIANG
JIANGXIANG
NONGXIANG
}
enum ProductStatus {
DRAFT
ON_SALE
OFF_SALE
}
enum StoreStatus {
OPEN
PAUSED
CLOSED
}
enum StoreAuditType {
NEW
UPDATE
}
enum StoreAuditStatus {
PENDING
APPROVED
REJECTED
}
enum DeliveryType {
LOCAL
CROSS_CITY
}
enum OrderType {
NORMAL
RESHIPMENT
}
enum OrderStatus {
PENDING_PAY
PENDING_SHIP
OUT_WAREHOUSE
SHIPPING
PENDING_RECEIVE
COMPLETED
CANCELLED
REFUNDING
REFUNDED
}
enum PaymentStatus {
PENDING
SUCCESS
FAILED
CLOSED
}
enum RefundStatus {
PENDING
APPROVED
REJECTED
PROCESSING
SUCCESS
FAILED
}
enum InterceptStatus {
PENDING
INTERCEPTING
SUCCESS
FAILED
REDELIVERING
COMPLETED
}
enum BenefitCouponStatus {
ACTIVE
USED_UP
VOID
}
enum BenefitLedgerType {
GRANT
REDEEM
REFUND_VOID
ADJUST
}
enum RedeemTokenStatus {
ACTIVE
USED
EXPIRED
CANCELLED
}
enum StorePayoutStatus {
PENDING
PAID
}
enum PartnerBillStatus {
DRAFT
PENDING_CONFIRM
CONFIRMED
PAID
REJECTED
}
enum WithdrawalStatus {
PENDING
APPROVED
REJECTED
PAID
}
enum PromoCodeStatus {
ACTIVE
DISABLED
}
enum AlertType {
ORDER_TIMEOUT
DELIVERY_DELAY
OTHER
}
enum AlertStatus {
OPEN
RESOLVED
}
enum AfterSaleType {
REFUND
RESHIPMENT
}
enum AfterSaleStatus {
PENDING
PROCESSING
COMPLETED
REJECTED
}
// ─── 微信应用配置 ─────────────────────────────────────
model WxAppConfig {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
clientApp ClientApp @unique @map("client_app")
appId String @map("app_id") @db.VarChar(64)
appSecret String @map("app_secret") @db.VarChar(128)
mchId String? @map("mch_id") @db.VarChar(32)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
@@map("wx_app_configs")
}
// ─── C端用户(phone 唯一主键,微信字段辅助)──────────
model User {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
userNo String @unique @map("user_no") @db.VarChar(20)
phone String @unique @db.VarChar(20)
wxOpenId String? @map("wx_open_id") @db.VarChar(64)
wxUnionId String? @map("wx_union_id") @db.VarChar(64)
nickname String? @db.VarChar(64)
avatarUrl String? @map("avatar_url") @db.VarChar(512)
status Int @default(1) @db.TinyInt
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
addresses UserAddress[]
cityPref UserCityPreference?
orders Order[]
benefitCoupons BenefitCoupon[]
benefitLedgers BenefitLedger[]
redeemRecords RedeemRecord[]
promoTouch UserPromoAttribution?
eventLogs EventLog[]
@@index([wxOpenId])
@@index([wxUnionId])
@@map("users")
}
model UserAddress {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
userId BigInt @map("user_id") @db.UnsignedBigInt
receiverName String @map("receiver_name") @db.VarChar(32)
phone String @db.VarChar(20)
province String @db.VarChar(32)
city String @db.VarChar(32)
district String @db.VarChar(32)
detail String @db.VarChar(256)
latitude Decimal? @db.Decimal(10, 7)
longitude Decimal? @db.Decimal(10, 7)
isDefault Int @default(0) @map("is_default") @db.TinyInt
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("user_addresses")
}
model UserCityPreference {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
userId BigInt @unique @map("user_id") @db.UnsignedBigInt
selectedCityCode String? @map("selected_city_code") @db.VarChar(16)
selectedDistrict String? @map("selected_district") @db.VarChar(32)
locateCityCode String? @map("locate_city_code") @db.VarChar(16)
locateDistrict String? @map("locate_district") @db.VarChar(32)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("user_city_preferences")
}
// ─── B端账号(三表分离)──────────────────────────────
model StoreAccount {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
storeId BigInt @unique @map("store_id") @db.UnsignedBigInt
phone String @unique @db.VarChar(20)
name String @db.VarChar(64)
wxOpenId String? @map("wx_open_id") @db.VarChar(64)
wxUnionId String? @map("wx_union_id") @db.VarChar(64)
status AccountStatus @default(ACTIVE)
lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
@@index([wxOpenId])
@@map("store_accounts")
}
model PartnerAccount {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
phone String @unique @db.VarChar(20)
name String @db.VarChar(64)
wxOpenId String? @map("wx_open_id") @db.VarChar(64)
wxUnionId String? @map("wx_union_id") @db.VarChar(64)
isPrimary Int @default(0) @map("is_primary") @db.TinyInt
parentAccountId BigInt? @map("parent_account_id") @db.UnsignedBigInt
staffRole PartnerStaffRole? @map("staff_role")
status AccountStatus @default(ACTIVE)
lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict)
parent PartnerAccount? @relation("PartnerAccountHierarchy", fields: [parentAccountId], references: [id], onDelete: SetNull)
children PartnerAccount[] @relation("PartnerAccountHierarchy")
@@index([partnerId])
@@index([parentAccountId])
@@index([wxOpenId])
@@map("partner_accounts")
}
model HqAccount {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
phone String @unique @db.VarChar(20)
name String @db.VarChar(64)
adminRole HqAdminRole @default(OPS) @map("admin_role")
wxOpenId String? @map("wx_open_id") @db.VarChar(64)
wxUnionId String? @map("wx_union_id") @db.VarChar(64)
status AccountStatus @default(ACTIVE)
lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
storeAudits StoreAudit[]
operationLogs OperationLog[]
afterSales AfterSaleTicket[]
refunds Refund[]
@@index([wxOpenId])
@@index([adminRole])
@@map("hq_accounts")
}
// ─── 开城与合伙人 ─────────────────────────────────────
model Partner {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
companyName String @map("company_name") @db.VarChar(128)
address String @db.VarChar(256)
contactPhone String @map("contact_phone") @db.VarChar(20)
bankAccountName String? @map("bank_account_name") @db.VarChar(64)
bankAccountNo String? @map("bank_account_no") @db.VarChar(32)
bankBranch String? @map("bank_branch") @db.VarChar(128)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
cities City[]
accounts PartnerAccount[]
stores Store[]
bills PartnerBill[]
withdrawals PartnerWithdrawal[]
contracts PartnerContract[]
orderCommissions OrderCommission[]
@@index([contactPhone])
@@map("partners")
}
model City {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
code String @unique @db.VarChar(16)
name String @db.VarChar(64)
province String @db.VarChar(32)
status CityStatus @default(PENDING)
partnerId BigInt? @map("partner_id") @db.UnsignedBigInt
localMinQty Int @default(2) @map("local_min_qty")
crossMinQty Int @default(6) @map("cross_min_qty")
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
partner Partner? @relation(fields: [partnerId], references: [id], onDelete: SetNull)
commissionRule CityCommissionRule?
stores Store[]
orders Order[]
@@index([partnerId])
@@index([status])
@@map("cities")
}
model CityCommissionRule {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
cityId BigInt @unique @map("city_id") @db.UnsignedBigInt
orderCommissionRate Decimal @map("order_commission_rate") @db.Decimal(5, 4)
redeemCommissionRate Decimal @map("redeem_commission_rate") @db.Decimal(5, 4)
partnerProfitRate Decimal @default(0.35) @map("partner_profit_rate") @db.Decimal(5, 4)
storeSettlementRate Decimal @default(0.60) @map("store_settlement_rate") @db.Decimal(5, 4)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
city City @relation(fields: [cityId], references: [id], onDelete: Cascade)
@@map("city_commission_rules")
}
model PartnerContract {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
contractNo String @map("contract_no") @db.VarChar(64)
fileUrl String @map("file_url") @db.VarChar(512)
signedAt DateTime @map("signed_at") @db.DateTime(3)
expireAt DateTime? @map("expire_at") @db.DateTime(3)
status String @default("ACTIVE") @db.VarChar(16)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict)
@@index([partnerId])
@@map("partner_contracts")
}
// ─── 商品 ─────────────────────────────────────────────
model Product {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
skuCode String @unique @map("sku_code") @db.VarChar(32)
name String @db.VarChar(128)
subtitle String? @db.VarChar(256)
aromaType AromaType @map("aroma_type")
spec String @db.VarChar(128)
price Decimal @db.Decimal(10, 2)
benefitAmount Decimal? @map("benefit_amount") @db.Decimal(10, 2)
status ProductStatus @default(DRAFT)
sortOrder Int @default(0) @map("sort_order")
mainImageUrl String @map("main_image_url") @db.VarChar(512)
detailContent Json? @map("detail_content")
carouselUrls Json? @map("carousel_urls")
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
orderItems OrderItem[]
@@index([status, aromaType])
@@map("products")
}
// ─── 门店 ─────────────────────────────────────────────
model StoreCategory {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
code String @unique @db.VarChar(32)
name String @db.VarChar(64)
sort Int @default(0)
stores Store[]
@@map("store_categories")
}
model Store {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
cityId BigInt @map("city_id") @db.UnsignedBigInt
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
categoryId BigInt? @map("category_id") @db.UnsignedBigInt
name String @db.VarChar(128)
phone String @db.VarChar(20)
province String @db.VarChar(32)
cityName String @map("city") @db.VarChar(32)
district String @db.VarChar(32)
address String @db.VarChar(256)
latitude Decimal? @db.Decimal(10, 7)
longitude Decimal? @db.Decimal(10, 7)
intro String? @db.Text
coverUrl String? @map("cover_url") @db.VarChar(512)
avgPrice Decimal? @map("avg_price") @db.Decimal(10, 2)
rating Decimal? @db.Decimal(3, 2)
tags Json?
status StoreStatus @default(PAUSED)
openTime String? @map("open_time") @db.VarChar(8)
closeTime String? @map("close_time") @db.VarChar(8)
bankAccountName String? @map("bank_account_name") @db.VarChar(64)
bankAccountNo String? @map("bank_account_no") @db.VarChar(32)
bankBranch String? @map("bank_branch") @db.VarChar(128)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
cityRef City @relation(fields: [cityId], references: [id], onDelete: Restrict)
partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict)
category StoreCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
account StoreAccount?
media StoreMedia[]
audits StoreAudit[]
redeemRecords RedeemRecord[]
storePayouts StorePayout[]
ratings StoreRating[]
@@index([cityId, status])
@@index([partnerId])
@@map("stores")
}
model StoreMedia {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
storeId BigInt @map("store_id") @db.UnsignedBigInt
mediaType String @map("media_type") @db.VarChar(16)
url String @db.VarChar(512)
sortOrder Int @default(0) @map("sort_order")
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
@@index([storeId])
@@map("store_media")
}
model StoreAudit {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
storeId BigInt @map("store_id") @db.UnsignedBigInt
auditType StoreAuditType @map("audit_type")
status StoreAuditStatus @default(PENDING)
submitData Json? @map("submit_data")
rejectReason String? @map("reject_reason") @db.VarChar(512)
reviewerId BigInt? @map("reviewer_id") @db.UnsignedBigInt
submittedAt DateTime @default(now()) @map("submitted_at") @db.DateTime(3)
reviewedAt DateTime? @map("reviewed_at") @db.DateTime(3)
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
reviewer HqAccount? @relation(fields: [reviewerId], references: [id], onDelete: SetNull)
@@index([storeId, status])
@@index([reviewerId])
@@map("store_audits")
}
// ─── 推广码 ───────────────────────────────────────────
model PromoCode {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
code String @unique @db.VarChar(32)
name String @db.VarChar(128)
status PromoCodeStatus @default(ACTIVE)
wxQrcodeUrl String? @map("wx_qrcode_url") @db.VarChar(512)
scanCount Int @default(0) @map("scan_count")
orderCount Int @default(0) @map("order_count")
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
orders Order[]
attributions UserPromoAttribution[]
@@map("promo_codes")
}
model UserPromoAttribution {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
userId BigInt @unique @map("user_id") @db.UnsignedBigInt
promoCodeId BigInt @map("promo_code_id") @db.UnsignedBigInt
channelName String @map("channel_name") @db.VarChar(128)
firstTouchAt DateTime @map("first_touch_at") @db.DateTime(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
promoCode PromoCode @relation(fields: [promoCodeId], references: [id], onDelete: Restrict)
@@index([promoCodeId])
@@map("user_promo_attributions")
}
// ─── 订单与支付 ─────────────────────────────────────
model Order {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
orderNo String @unique @map("order_no") @db.VarChar(32)
orderType OrderType @default(NORMAL) @map("order_type")
userId BigInt @map("user_id") @db.UnsignedBigInt
cityId BigInt @map("city_id") @db.UnsignedBigInt
status OrderStatus @default(PENDING_PAY)
deliveryType DeliveryType @map("delivery_type")
originOrderId BigInt? @map("origin_order_id") @db.UnsignedBigInt
promoCodeId BigInt? @map("promo_code_id") @db.UnsignedBigInt
channelSource String? @map("channel_source") @db.VarChar(128)
receiverName String @map("receiver_name") @db.VarChar(32)
receiverPhone String @map("receiver_phone") @db.VarChar(20)
receiverAddress String @map("receiver_address") @db.Text
receiverProvince String @map("receiver_province") @db.VarChar(32)
receiverCity String @map("receiver_city") @db.VarChar(32)
receiverDistrict String @map("receiver_district") @db.VarChar(32)
productAmount Decimal @map("product_amount") @db.Decimal(10, 2)
freightAmount Decimal @default(0) @map("freight_amount") @db.Decimal(10, 2)
freightPayType String? @map("freight_pay_type") @db.VarChar(8)
payAmount Decimal @map("pay_amount") @db.Decimal(10, 2)
benefitAmount Decimal @default(0) @map("benefit_amount") @db.Decimal(10, 2)
paidAt DateTime? @map("paid_at") @db.DateTime(3)
shippedAt DateTime? @map("shipped_at") @db.DateTime(3)
completedAt DateTime? @map("completed_at") @db.DateTime(3)
cancelledAt DateTime? @map("cancelled_at") @db.DateTime(3)
payExpireAt DateTime? @map("pay_expire_at") @db.DateTime(3)
remark String? @db.VarChar(512)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
user User @relation(fields: [userId], references: [id], onDelete: Restrict)
city City @relation(fields: [cityId], references: [id], onDelete: Restrict)
originOrder Order? @relation("ReshipmentOrder", fields: [originOrderId], references: [id], onDelete: SetNull)
reshipments Order[] @relation("ReshipmentOrder")
promoCode PromoCode? @relation(fields: [promoCodeId], references: [id], onDelete: SetNull)
items OrderItem[]
statusLogs OrderStatusLog[]
delivery OrderDelivery?
payment Payment?
refunds Refund[]
benefitCoupons BenefitCoupon[]
intercepts DeliveryIntercept[]
afterSales AfterSaleTicket[]
commissions OrderCommission[]
@@index([userId, status])
@@index([cityId, createdAt])
@@index([receiverPhone])
@@index([originOrderId])
@@map("orders")
}
model OrderItem {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
orderId BigInt @map("order_id") @db.UnsignedBigInt
productId BigInt @map("product_id") @db.UnsignedBigInt
productName String @map("product_name") @db.VarChar(128)
productSpec String @map("product_spec") @db.VarChar(128)
productImage String @map("product_image") @db.VarChar(512)
unitPrice Decimal @map("unit_price") @db.Decimal(10, 2)
quantity Int
subtotal Decimal @db.Decimal(10, 2)
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Restrict)
@@index([orderId])
@@map("order_items")
}
model OrderStatusLog {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
orderId BigInt @map("order_id") @db.UnsignedBigInt
fromStatus String? @map("from_status") @db.VarChar(32)
toStatus String @map("to_status") @db.VarChar(32)
operator String? @db.VarChar(64)
remark String? @db.VarChar(256)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
@@index([orderId])
@@map("order_status_logs")
}
model OrderDelivery {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
orderId BigInt @unique @map("order_id") @db.UnsignedBigInt
provider String @db.VarChar(16)
providerOrderNo String? @map("provider_order_no") @db.VarChar(64)
trackingNo String? @map("tracking_no") @db.VarChar(64)
outWarehouseAt DateTime? @map("out_warehouse_at") @db.DateTime(3)
shippingAt DateTime? @map("shipping_at") @db.DateTime(3)
deliveredAt DateTime? @map("delivered_at") @db.DateTime(3)
rawPayload Json? @map("raw_payload")
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
@@map("order_deliveries")
}
model Payment {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
orderId BigInt @unique @map("order_id") @db.UnsignedBigInt
paymentNo String @unique @map("payment_no") @db.VarChar(32)
amount Decimal @db.Decimal(10, 2)
status PaymentStatus @default(PENDING)
wxTransactionId String? @unique @map("wx_transaction_id") @db.VarChar(64)
wxPrepayId String? @map("wx_prepay_id") @db.VarChar(64)
paidAt DateTime? @map("paid_at") @db.DateTime(3)
notifyPayload Json? @map("notify_payload")
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
@@map("payments")
}
model Refund {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
orderId BigInt @map("order_id") @db.UnsignedBigInt
refundNo String @unique @map("refund_no") @db.VarChar(32)
amount Decimal @db.Decimal(10, 2)
reason String? @db.VarChar(512)
status RefundStatus @default(PENDING)
wxRefundId String? @map("wx_refund_id") @db.VarChar(64)
benefitAdjust Decimal? @map("benefit_adjust") @db.Decimal(10, 2)
operatorId BigInt? @map("operator_id") @db.UnsignedBigInt
approvedAt DateTime? @map("approved_at") @db.DateTime(3)
refundedAt DateTime? @map("refunded_at") @db.DateTime(3)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
order Order @relation(fields: [orderId], references: [id], onDelete: Restrict)
operator HqAccount? @relation(fields: [operatorId], references: [id], onDelete: SetNull)
@@index([orderId])
@@index([operatorId])
@@map("refunds")
}
model DeliveryIntercept {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
orderId BigInt @map("order_id") @db.UnsignedBigInt
status InterceptStatus @default(PENDING)
oldAddress Json @map("old_address")
newAddress Json @map("new_address")
partnerId BigInt? @map("partner_id") @db.UnsignedBigInt
interceptAt DateTime? @map("intercept_at") @db.DateTime(3)
redeliveryOrderId BigInt? @map("redelivery_order_id") @db.UnsignedBigInt
remark String? @db.VarChar(512)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
order Order @relation(fields: [orderId], references: [id], onDelete: Restrict)
@@index([orderId, status])
@@map("delivery_intercepts")
}
// ─── 好客权益 ─────────────────────────────────────────
model BenefitCoupon {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
couponNo String @unique @map("coupon_no") @db.VarChar(32)
userId BigInt @map("user_id") @db.UnsignedBigInt
orderId BigInt @map("order_id") @db.UnsignedBigInt
totalAmount Decimal @map("total_amount") @db.Decimal(10, 2)
usedAmount Decimal @default(0) @map("used_amount") @db.Decimal(10, 2)
balance Decimal @db.Decimal(10, 2)
status BenefitCouponStatus @default(ACTIVE)
sourceProduct String @map("source_product") @db.VarChar(128)
version Int @default(0)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
user User @relation(fields: [userId], references: [id], onDelete: Restrict)
order Order @relation(fields: [orderId], references: [id], onDelete: Restrict)
ledgers BenefitLedger[]
redeemRecords RedeemRecord[]
redeemTokens RedeemToken[]
@@index([userId, status])
@@index([orderId])
@@map("benefit_coupons")
}
model BenefitLedger {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
userId BigInt @map("user_id") @db.UnsignedBigInt
couponId BigInt @map("coupon_id") @db.UnsignedBigInt
type BenefitLedgerType
amount Decimal @db.Decimal(10, 2)
balanceAfter Decimal @map("balance_after") @db.Decimal(10, 2)
refType String? @map("ref_type") @db.VarChar(32)
refId BigInt? @map("ref_id") @db.UnsignedBigInt
remark String? @db.VarChar(256)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
user User @relation(fields: [userId], references: [id], onDelete: Restrict)
coupon BenefitCoupon @relation(fields: [couponId], references: [id], onDelete: Restrict)
@@index([userId, createdAt])
@@index([couponId])
@@map("benefit_ledgers")
}
// ─── 核销 ─────────────────────────────────────────────
model RedeemToken {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
token String @unique @db.VarChar(64)
userId BigInt @map("user_id") @db.UnsignedBigInt
couponId BigInt @map("coupon_id") @db.UnsignedBigInt
storeId BigInt? @map("store_id") @db.UnsignedBigInt
amount Decimal @db.Decimal(10, 2)
status RedeemTokenStatus @default(ACTIVE)
expireAt DateTime @map("expire_at") @db.DateTime(3)
usedAt DateTime? @map("used_at") @db.DateTime(3)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
coupon BenefitCoupon @relation(fields: [couponId], references: [id], onDelete: Restrict)
@@index([couponId])
@@map("redeem_tokens")
}
model RedeemRecord {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
redeemNo String @unique @map("redeem_no") @db.VarChar(32)
userId BigInt @map("user_id") @db.UnsignedBigInt
couponId BigInt @map("coupon_id") @db.UnsignedBigInt
storeId BigInt @map("store_id") @db.UnsignedBigInt
amount Decimal @db.Decimal(10, 2)
settleAmount Decimal @map("settle_amount") @db.Decimal(10, 2)
tokenId BigInt? @map("token_id") @db.UnsignedBigInt
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
user User @relation(fields: [userId], references: [id], onDelete: Restrict)
coupon BenefitCoupon @relation(fields: [couponId], references: [id], onDelete: Restrict)
store Store @relation(fields: [storeId], references: [id], onDelete: Restrict)
payout StorePayout?
rating StoreRating?
commissions OrderCommission[]
@@index([storeId, createdAt])
@@index([userId])
@@map("redeem_records")
}
model StoreRating {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
redeemRecordId BigInt @unique @map("redeem_record_id") @db.UnsignedBigInt
storeId BigInt @map("store_id") @db.UnsignedBigInt
serviceScore Int @map("service_score") @db.TinyInt
envScore Int @map("env_score") @db.TinyInt
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
redeemRecord RedeemRecord @relation(fields: [redeemRecordId], references: [id], onDelete: Cascade)
store Store @relation(fields: [storeId], references: [id], onDelete: Restrict)
@@map("store_ratings")
}
// ─── 结算 ─────────────────────────────────────────────
model StorePayout {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
redeemRecordId BigInt @unique @map("redeem_record_id") @db.UnsignedBigInt
storeId BigInt @map("store_id") @db.UnsignedBigInt
redeemAmount Decimal @map("redeem_amount") @db.Decimal(10, 2)
payoutAmount Decimal @map("payout_amount") @db.Decimal(10, 2)
settlementRate Decimal @map("settlement_rate") @db.Decimal(5, 4)
status StorePayoutStatus @default(PENDING)
expectedPayAt DateTime @map("expected_pay_at") @db.DateTime(3)
paidAt DateTime? @map("paid_at") @db.DateTime(3)
batchNo String? @map("batch_no") @db.VarChar(32)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
redeemRecord RedeemRecord @relation(fields: [redeemRecordId], references: [id], onDelete: Restrict)
store Store @relation(fields: [storeId], references: [id], onDelete: Restrict)
@@index([storeId, status])
@@index([expectedPayAt])
@@map("store_payouts")
}
model OrderCommission {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
orderId BigInt? @map("order_id") @db.UnsignedBigInt
redeemId BigInt? @map("redeem_id") @db.UnsignedBigInt
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
commissionType String @map("commission_type") @db.VarChar(16)
baseAmount Decimal @map("base_amount") @db.Decimal(10, 2)
rate Decimal @db.Decimal(5, 4)
amount Decimal @db.Decimal(10, 2)
billId BigInt? @map("bill_id") @db.UnsignedBigInt
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
order Order? @relation(fields: [orderId], references: [id], onDelete: SetNull)
redeem RedeemRecord? @relation(fields: [redeemId], references: [id], onDelete: SetNull)
partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict)
bill PartnerBill? @relation(fields: [billId], references: [id], onDelete: SetNull)
@@index([partnerId])
@@index([billId])
@@map("order_commissions")
}
model PartnerBill {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
billNo String @unique @map("bill_no") @db.VarChar(32)
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
periodStart DateTime @map("period_start") @db.DateTime(3)
periodEnd DateTime @map("period_end") @db.DateTime(3)
orderCommission Decimal @map("order_commission") @db.Decimal(10, 2)
redeemCommission Decimal @map("redeem_commission") @db.Decimal(10, 2)
totalAmount Decimal @map("total_amount") @db.Decimal(10, 2)
status PartnerBillStatus @default(DRAFT)
confirmedAt DateTime? @map("confirmed_at") @db.DateTime(3)
paidAt DateTime? @map("paid_at") @db.DateTime(3)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict)
commissions OrderCommission[]
@@index([partnerId, status])
@@map("partner_bills")
}
model PartnerWithdrawal {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
amount Decimal @db.Decimal(10, 2)
status WithdrawalStatus @default(PENDING)
remark String? @db.VarChar(256)
appliedAt DateTime @default(now()) @map("applied_at") @db.DateTime(3)
processedAt DateTime? @map("processed_at") @db.DateTime(3)
partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict)
@@index([partnerId])
@@map("partner_withdrawals")
}
// ─── 埋点 ─────────────────────────────────────────────
model EventLog {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
userId BigInt? @map("user_id") @db.UnsignedBigInt
eventName String @map("event_name") @db.VarChar(64)
params Json?
clientApp String? @map("client_app") @db.VarChar(32)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
@@index([eventName, createdAt])
@@index([userId])
@@map("event_logs")
}
// ─── 运营与售后 ─────────────────────────────────────
model AfterSaleTicket {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
ticketNo String @unique @map("ticket_no") @db.VarChar(32)
type AfterSaleType
status AfterSaleStatus @default(PENDING)
orderId BigInt @map("order_id") @db.UnsignedBigInt
reshipmentOrderId BigInt? @map("reshipment_order_id") @db.UnsignedBigInt
refundId BigInt? @map("refund_id") @db.UnsignedBigInt
reason String? @db.VarChar(512)
operatorId BigInt? @map("operator_id") @db.UnsignedBigInt
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
completedAt DateTime? @map("completed_at") @db.DateTime(3)
order Order @relation(fields: [orderId], references: [id], onDelete: Restrict)
operator HqAccount? @relation(fields: [operatorId], references: [id], onDelete: SetNull)
@@index([orderId])
@@index([status, type])
@@index([operatorId])
@@map("after_sale_tickets")
}
model Alert {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
alertType AlertType @map("alert_type")
status AlertStatus @default(OPEN)
refType String @map("ref_type") @db.VarChar(32)
refId BigInt @map("ref_id") @db.UnsignedBigInt
title String @db.VarChar(128)
content String @db.Text
resolvedAt DateTime? @map("resolved_at") @db.DateTime(3)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
@@index([status, alertType])
@@map("alerts")
}
model OperationLog {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
hqAccountId BigInt @map("hq_account_id") @db.UnsignedBigInt
action String @db.VarChar(64)
refType String? @map("ref_type") @db.VarChar(32)
refId BigInt? @map("ref_id") @db.UnsignedBigInt
detail Json?
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
hqAccount HqAccount @relation(fields: [hqAccountId], references: [id], onDelete: Restrict)
@@index([hqAccountId, createdAt])
@@map("operation_logs")
}
model SmsLog {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
phone String @db.VarChar(20)
template String @db.VarChar(64)
content String @db.Text
status String @db.VarChar(16)
refType String? @map("ref_type") @db.VarChar(32)
refId BigInt? @map("ref_id") @db.UnsignedBigInt
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
@@index([phone, createdAt])
@@map("sms_logs")
}
+249
View File
@@ -0,0 +1,249 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('Seeding preV1 data...');
await prisma.partnerBill.deleteMany();
await prisma.storePayout.deleteMany();
await prisma.redeemRecord.deleteMany();
await prisma.benefitLedger.deleteMany();
await prisma.benefitCoupon.deleteMany();
await prisma.orderStatusLog.deleteMany();
await prisma.orderDelivery.deleteMany();
await prisma.payment.deleteMany();
await prisma.orderItem.deleteMany();
await prisma.order.deleteMany();
await prisma.storeAudit.deleteMany();
await prisma.storeAccount.deleteMany();
await prisma.storeMedia.deleteMany();
await prisma.store.deleteMany();
await prisma.userCityPreference.deleteMany();
await prisma.userAddress.deleteMany();
await prisma.user.deleteMany();
await prisma.partnerAccount.deleteMany();
await prisma.cityCommissionRule.deleteMany();
await prisma.city.deleteMany();
await prisma.partner.deleteMany();
await prisma.product.deleteMany();
await prisma.storeCategory.deleteMany();
await prisma.hqAccount.deleteMany();
const partner = await prisma.partner.create({
data: {
companyName: '郑州城市合伙人',
address: '河南省郑州市金水区',
contactPhone: '13700000001',
bankAccountName: '郑州合伙人公司',
bankAccountNo: '6222021234567890',
bankBranch: '工商银行郑州分行',
},
});
const city = await prisma.city.create({
data: {
code: '410100',
name: '郑州市',
province: '河南省',
status: 'ACTIVE',
partnerId: partner.id,
localMinQty: 2,
crossMinQty: 6,
},
});
await prisma.cityCommissionRule.create({
data: {
cityId: city.id,
orderCommissionRate: 0.05,
redeemCommissionRate: 0.03,
partnerProfitRate: 0.35,
storeSettlementRate: 0.6,
},
});
const categories = await Promise.all([
prisma.storeCategory.create({ data: { code: 'HOTPOT', name: '火锅', sort: 1 } }),
prisma.storeCategory.create({ data: { code: 'LOCAL', name: '地方菜', sort: 2 } }),
]);
const products = await Promise.all([
prisma.product.create({
data: {
skuCode: 'QX-001',
name: '杜康·白水古酿 500ml',
subtitle: '清香型 52度 礼盒装',
aromaType: 'QINGXIANG',
spec: '500ml | 52度',
price: 599,
benefitAmount: 599,
status: 'ON_SALE',
sortOrder: 1,
mainImageUrl: 'https://picsum.photos/seed/dukang1/400/400',
},
}),
prisma.product.create({
data: {
skuCode: 'QX-002',
name: '杜康·年份陈酿(十年)',
subtitle: '清香型 42度 纯粮酿造',
aromaType: 'QINGXIANG',
spec: '500ml | 42度',
price: 880,
benefitAmount: 880,
status: 'ON_SALE',
sortOrder: 2,
mainImageUrl: 'https://picsum.photos/seed/dukang2/400/400',
},
}),
prisma.product.create({
data: {
skuCode: 'QX-003',
name: '杜康·御享1号 珍藏版',
subtitle: '高端定制 限量发售',
aromaType: 'QINGXIANG',
spec: '500ml | 52度',
price: 1299,
benefitAmount: 1299,
status: 'ON_SALE',
sortOrder: 3,
mainImageUrl: 'https://picsum.photos/seed/dukang3/400/400',
},
}),
prisma.product.create({
data: {
skuCode: 'QX-004',
name: '杜康·经典传承',
subtitle: '清香型 纯粮固态',
aromaType: 'QINGXIANG',
spec: '500ml | 46度',
price: 399,
benefitAmount: 399,
status: 'ON_SALE',
sortOrder: 4,
mainImageUrl: 'https://picsum.photos/seed/dukang4/400/400',
},
}),
]);
await prisma.partnerAccount.create({
data: {
partnerId: partner.id,
phone: '13700000001',
name: '郑州合伙人主账号',
isPrimary: 1,
staffRole: 'PARTNER',
},
});
const store1 = await prisma.store.create({
data: {
cityId: city.id,
partnerId: partner.id,
categoryId: categories[0].id,
name: '郑州老城店',
phone: '0371-88880001',
province: '河南省',
cityName: '郑州市',
district: '金水区',
address: '花园路100号',
intro: '正宗河南菜,欢迎核销好客权益',
coverUrl: 'https://picsum.photos/seed/store1/400/300',
status: 'OPEN',
openTime: '10:00',
closeTime: '22:00',
bankAccountName: '郑州老城店',
bankAccountNo: '6222029876543210',
bankBranch: '建设银行郑州分行',
},
});
const store2 = await prisma.store.create({
data: {
cityId: city.id,
partnerId: partner.id,
categoryId: categories[1].id,
name: '郑州美食城店',
phone: '0371-88880002',
province: '河南省',
cityName: '郑州市',
district: '二七区',
address: '大学路200号',
intro: '地方特色餐饮',
coverUrl: 'https://picsum.photos/seed/store2/400/300',
status: 'OPEN',
openTime: '10:00',
closeTime: '22:00',
},
});
await prisma.storeAccount.create({
data: { storeId: store1.id, phone: '13900000001', name: '老城店店长' },
});
await prisma.storeAccount.create({
data: { storeId: store2.id, phone: '13900000002', name: '美食城店长' },
});
await prisma.user.create({
data: {
userNo: 'DK88293401',
phone: '13800000001',
nickname: '测试用户',
cityPref: {
create: {
selectedCityCode: '410100',
selectedDistrict: '郑州市',
locateCityCode: '410100',
locateDistrict: '金水区',
},
},
},
});
await prisma.hqAccount.create({
data: {
phone: '13600000001',
name: '总部管理员',
adminRole: 'SUPER_ADMIN',
},
});
const now = new Date();
const periodStart = new Date(now.getFullYear(), now.getMonth(), 1);
const periodEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
await prisma.partnerBill.create({
data: {
billNo: `PB${Date.now()}`,
partnerId: partner.id,
periodStart,
periodEnd,
orderCommission: 1200,
redeemCommission: 800,
totalAmount: 2000,
status: 'CONFIRMED',
confirmedAt: now,
},
});
console.log('Seed complete:', {
city: city.name,
products: products.length,
stores: 2,
testPhones: {
user: '13800000001',
store: '13900000001',
partner: '13700000001',
},
});
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
@@ -0,0 +1,44 @@
/**
* 将 products.benefit_amount 同步为与 price 相同(全额好客权益)。
* 用法:pnpm db:sync-benefit
*/
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
const products = await prisma.product.findMany({
select: { id: true, skuCode: true, name: true, price: true, benefitAmount: true },
orderBy: { sortOrder: 'asc' },
});
if (products.length === 0) {
console.log('No products found. Run pnpm db:seed first.');
return;
}
console.log(`Syncing benefit_amount = price for ${products.length} product(s)...\n`);
for (const p of products) {
const price = Number(p.price);
const before = p.benefitAmount != null ? Number(p.benefitAmount) : null;
await prisma.product.update({
where: { id: p.id },
data: { benefitAmount: p.price },
});
const beforeLabel = before == null ? 'NULL' : `¥${before}`;
console.log(` ${p.skuCode} ${p.name}`);
console.log(` benefit: ${beforeLabel} → ¥${price}`);
}
console.log('\nDone.');
}
main()
.catch((err) => {
console.error(err);
process.exit(1);
})
.finally(() => prisma.$disconnect());