Files
group_xinghuo_jinrong/scripts/core/01-ddl.sql
T
GaoYiYuan_0626 f056fcd007 基金转换 T+1 模型:T-1~T-6 落地(DDL/数据层/语义收窄/锁/受理事务)
AIcoding 第 5 步 todo 开发(开发计划 v2.0)前半段:

- T-1 3 张新表 DDL(core_convert_request 6 态 ENUM / core_trade_calendar /
  core_share_rule)+ 种子 + sqlite 单一事实源同步 + 列清单断言
- T-2/T-2b calc 扩展(product_round/redeem_amount/partial_qty)+ 真实净值实算回填
- T-3 convert_request_repository(6 态 + 条件 UPDATE 守卫)+ core_ro 三读方法
  + share_lot_repository.available_qty_with_inflight(R-3 在途占用推导)
- T-4 convert_repository.sync_mirror 成为 risk_convert_detail 唯一进度镜像写入口
  (旧三方法标 Deprecated,T-7 后删)
- T-5 locks.py 锁键构造器 convert_req_lock_key / convert_confirm_lock_key
- T-6 convert_service.accept_convert 受理事务(八步:锁→幂等→校验→受理日顺延
  →在途占用校验→落单→镜像+审计→202;不扣份额/不折算/不写流水)
  + tests/test_convert_accept.py(16 用例)
  + scripts/dev/verify_convert_accept.py(真库 36/36 一致)
  + trading_calendar.py 纯函数包(R-5)+ 21 用例

T-6 真库实测暴露并修复:confirm_eta 在日历数据边界抛 ValueError,会让已落库
的受理单在调用方眼里变 500;改为展示性字段容错 + 单测守护。

基线:798 passed / 10 skipped,零回归。
2026-09-11 19:21:15 +08:00

314 lines
21 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- jinrong_core 表结构 · 对齐客户资料(用户信息示例 / 适当性指南 / AML 规则)
USE jinrong_core;
-- 风险等级字典(客户 C1~C5 · 产品 R1~R5)
CREATE TABLE core_risk_grade (
code VARCHAR(4) NOT NULL PRIMARY KEY COMMENT 'C1~C5 或 R1~R5',
grade_type ENUM('customer','product') NOT NULL,
display_name VARCHAR(32) NOT NULL,
sort_order TINYINT UNSIGNED NOT NULL
) ENGINE=InnoDB COMMENT='风险等级字典';
CREATE TABLE core_industry (
industry_code VARCHAR(16) NOT NULL PRIMARY KEY,
industry_name VARCHAR(64) NOT NULL
) ENGINE=InnoDB COMMENT='行业分类';
-- C×R 适当性匹配矩阵(《个人投资者适当性管理指南》第十二条)
CREATE TABLE core_suitability_rule (
id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
customer_risk_code CHAR(2) NOT NULL COMMENT 'C1~C5',
product_risk_code CHAR(2) NOT NULL COMMENT 'R1~R5',
match_result ENUM('allowed','allowed_with_disclosure','forbidden') NOT NULL,
rule_ref VARCHAR(32) NOT NULL DEFAULT 'JR-AST-012',
UNIQUE KEY uk_cx_r (customer_risk_code, product_risk_code),
CONSTRAINT fk_suit_cust_risk FOREIGN KEY (customer_risk_code) REFERENCES core_risk_grade(code),
CONSTRAINT fk_suit_prod_risk FOREIGN KEY (product_risk_code) REFERENCES core_risk_grade(code)
) ENGINE=InnoDB COMMENT='适当性匹配规则(L0 权威)';
-- 内部员工(含 RBAC 角色种子,供 JWT/鉴权联调对照)
CREATE TABLE core_staff (
staff_id VARCHAR(64) NOT NULL PRIMARY KEY,
display_name VARCHAR(64) NOT NULL,
staff_type ENUM('advisor','analyst','risk_officer','compliance','ops') NOT NULL,
roles JSON NOT NULL COMMENT 'JWT roles 数组,如 ["advisor"]',
tenant_id VARCHAR(32) NOT NULL DEFAULT 'TENANT-001',
is_active TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
) ENGINE=InnoDB COMMENT='内部员工主档(模拟 IdP 账号源)';
-- 客户主档 L0(KYC + 财务概况 · 对齐用户信息数据示例)
CREATE TABLE core_customer (
customer_id VARCHAR(64) NOT NULL PRIMARY KEY,
display_name VARCHAR(64) NOT NULL COMMENT '脱敏展示名',
gender ENUM('M','F','U') NOT NULL DEFAULT 'U',
birth_date DATE NULL,
age TINYINT UNSIGNED NULL,
id_no_mask VARCHAR(32) NULL COMMENT '310101199903XXXXXX',
occupation VARCHAR(64) NULL,
employer VARCHAR(128) NULL,
education ENUM('high_school','associate','bachelor','master','doctor','other') NULL,
marital_status ENUM('single','married','widowed','divorced','other') NULL,
city VARCHAR(64) NULL,
address_mask VARCHAR(256) NULL,
phone_mask VARCHAR(16) NULL,
email_mask VARCHAR(64) NULL,
annual_income DECIMAL(18,2) NULL COMMENT '家庭年收入(元)',
financial_asset DECIMAL(18,2) NULL COMMENT '金融资产规模(不含房产)',
monthly_investable DECIMAL(18,2) NULL COMMENT '月可投资金额',
is_hnw TINYINT(1) NOT NULL DEFAULT 0 COMMENT '高净值客户',
service_tier ENUM('normal','vip','diamond') NOT NULL DEFAULT 'normal',
is_pep TINYINT(1) NOT NULL DEFAULT 0 COMMENT '政治公众人物',
aml_risk_level ENUM('low','medium','high') NOT NULL DEFAULT 'low',
invest_experience_years TINYINT UNSIGNED NULL,
first_invest_date DATE NULL,
tenant_id VARCHAR(32) NOT NULL DEFAULT 'TENANT-001',
open_date DATE NOT NULL,
is_active TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
KEY idx_service_tier (service_tier),
KEY idx_hnw (is_hnw),
KEY idx_aml (aml_risk_level)
) ENGINE=InnoDB COMMENT='客户主档 L0 · KYC';
-- 正式风险测评 L0(对齐适当性指南 16 题问卷 + FM-03 风评过期)
CREATE TABLE core_customer_risk (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
customer_id VARCHAR(64) NOT NULL,
risk_code CHAR(2) NOT NULL COMMENT 'C1~C5',
questionnaire_score SMALLINT UNSIGNED NULL COMMENT '问卷总分 20-100',
max_loss_tolerance_pct DECIMAL(5,2) NULL COMMENT '最大可承受亏损比例',
investment_goal VARCHAR(128) NULL,
investment_horizon ENUM('short','medium','long','flexible') NULL,
investor_category ENUM('ordinary','professional','professional_pending') NOT NULL DEFAULT 'ordinary',
professional_approved_at DATE NULL,
is_authoritative TINYINT(1) NOT NULL DEFAULT 1,
evaluated_at DATE NOT NULL,
expires_at DATE NOT NULL COMMENT '风评有效期(通常 evaluated_at+12 月)',
source VARCHAR(32) NOT NULL DEFAULT 'risk_questionnaire',
UNIQUE KEY uk_customer_current (customer_id),
KEY idx_risk (risk_code),
KEY idx_expires (expires_at),
KEY idx_investor_cat (investor_category),
CONSTRAINT fk_cust_risk_customer FOREIGN KEY (customer_id) REFERENCES core_customer(customer_id),
CONSTRAINT fk_cust_risk_code FOREIGN KEY (risk_code) REFERENCES core_risk_grade(code)
) ENGINE=InnoDB COMMENT='客户正式风险测评 L0';
CREATE TABLE core_customer_advisor (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
customer_id VARCHAR(64) NOT NULL,
advisor_id VARCHAR(64) NOT NULL COMMENT '对应 core_staff.staff_id',
rel_status ENUM('active','transferred','closed') NOT NULL DEFAULT 'active',
effective_from DATE NOT NULL,
effective_to DATE NULL,
UNIQUE KEY uk_cust_advisor_from (customer_id, advisor_id, effective_from),
KEY idx_advisor (advisor_id, rel_status),
CONSTRAINT fk_ca_customer FOREIGN KEY (customer_id) REFERENCES core_customer(customer_id),
CONSTRAINT fk_ca_advisor FOREIGN KEY (advisor_id) REFERENCES core_staff(staff_id)
) ENGINE=InnoDB COMMENT='客户-代理人归属';
-- 产品主档(对齐 C-11:风险等级 + 期限 + 起购金额)
CREATE TABLE core_product (
product_id VARCHAR(64) NOT NULL PRIMARY KEY,
product_name VARCHAR(128) NOT NULL,
product_type ENUM(
'money','bond','mixed','stock','index',
'wealth_mgmt','private_fund','insurance','structured'
) NOT NULL,
min_risk_code CHAR(2) NOT NULL COMMENT 'R1~R5 最低适配',
min_subscribe_amount DECIMAL(18,2) NOT NULL DEFAULT 1.00 COMMENT '起购金额(元)',
term_days INT UNSIGNED NULL COMMENT '产品期限(天),NULL=灵活开放',
requires_disclosure TINYINT(1) NOT NULL DEFAULT 0 COMMENT '购买前需签署风险揭示书',
industry_code VARCHAR(16) NULL,
fee_rate DECIMAL(6,4) NULL COMMENT '历史费率展示位(换算逻辑不读;申购费率见 subscribe_fee_rate)',
-- ===== 基金转换(convert)扩展列 · T-1 =====
can_subscribe TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否可申购(转换转入前置校验)',
can_redeem TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否可赎回(转换转出前置校验)',
min_hold_qty DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '最低持有份额阈值',
min_redeem_qty DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '最低赎回份额',
min_hold_action VARCHAR(32) NOT NULL DEFAULT 'force_transfer' COMMENT '低于 min_hold_qty 的动作:force_transfer=强制全转 / force_redeem=强制赎回剩余;用 VARCHAR 非 ENUM,加动作免 ALTER',
subscribe_fee_rate DECIMAL(6,4) NOT NULL DEFAULT 0 COMMENT '申购费率(补差费计算源,价外法)。22 号文 §8 上限:指数/债券 ≤0.0030、其他混合 ≤0.0050、主动偏股 ≤0.0080、货基 0',
fund_company VARCHAR(64) NULL COMMENT '基金管理人(同一笔转换需同管理人)',
ta_code VARCHAR(32) NULL COMMENT '注册登记机构代码(同一笔转换需同 TA)',
-- ===== T+1 模型扩展列 · D27/D30(T-1 新增)=====
share_class VARCHAR(16) NULL COMMENT '份额类别(A/C 等;D30 A/C 互转判定)',
allow_ac_convert TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否允许同基金 A/C 份额互转(D30;1 即两向放行)',
rounding_mode VARCHAR(16) NOT NULL DEFAULT 'half_up' COMMENT '产品级舍入模式(D27;与 core_share_rule 二选一优先,见 T-14 DoD)',
share_digits TINYINT UNSIGNED NOT NULL DEFAULT 2 COMMENT '产品级份额位数(D27;默认 2,个别指数/货基可 4)',
is_open TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
KEY idx_min_risk (min_risk_code),
KEY idx_product_type (product_type),
CONSTRAINT fk_product_risk FOREIGN KEY (min_risk_code) REFERENCES core_risk_grade(code),
CONSTRAINT fk_product_industry FOREIGN KEY (industry_code) REFERENCES core_industry(industry_code)
) ENGINE=InnoDB COMMENT='产品主档';
CREATE TABLE core_holding (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
customer_id VARCHAR(64) NOT NULL,
product_id VARCHAR(64) NOT NULL,
qty DECIMAL(18,4) NOT NULL,
cost_amount DECIMAL(18,2) NOT NULL,
market_value DECIMAL(18,2) NOT NULL,
pnl_pct DECIMAL(8,4) NOT NULL COMMENT '盈亏比例',
as_of DATE NOT NULL,
UNIQUE KEY uk_cust_product (customer_id, product_id),
KEY idx_customer (customer_id),
CONSTRAINT fk_hold_customer FOREIGN KEY (customer_id) REFERENCES core_customer(customer_id),
CONSTRAINT fk_hold_product FOREIGN KEY (product_id) REFERENCES core_product(product_id)
) ENGINE=InnoDB COMMENT='持仓快照';
-- 交易流水(扩展 AML 字段 · 对齐反洗钱规则 RW-001~020)
CREATE TABLE core_trade (
trade_id VARCHAR(64) NOT NULL PRIMARY KEY,
customer_id VARCHAR(64) NOT NULL,
product_id VARCHAR(64) NOT NULL,
trade_type ENUM('subscribe','redeem','convert') NOT NULL,
amount DECIMAL(18,2) NOT NULL,
qty DECIMAL(18,4) NULL,
convert_group_id VARCHAR(64) NULL COMMENT '基金转换组 ID:同组两条流水(转出 redeem / 转入 subscribe)共享;非 convert 场景为 NULL',
channel ENUM('online','mobile','counter','other') NOT NULL DEFAULT 'online',
counterparty_account_mask VARCHAR(32) NULL,
counterparty_name VARCHAR(64) NULL,
is_cash TINYINT(1) NOT NULL DEFAULT 0,
payer_name VARCHAR(64) NULL COMMENT '代付人(RW-014)',
is_third_party_pay TINYINT(1) NOT NULL DEFAULT 0,
trade_status ENUM('confirmed','pending','cancelled') NOT NULL DEFAULT 'confirmed',
traded_at DATETIME(3) NOT NULL,
KEY idx_customer_time (customer_id, traded_at),
KEY idx_convert_group (convert_group_id),
KEY idx_amount (amount, traded_at),
KEY idx_channel_time (channel, traded_at),
CONSTRAINT fk_trade_customer FOREIGN KEY (customer_id) REFERENCES core_customer(customer_id),
CONSTRAINT fk_trade_product FOREIGN KEY (product_id) REFERENCES core_product(product_id)
) ENGINE=InnoDB COMMENT='交易流水';
CREATE TABLE core_cash_flow (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
customer_id VARCHAR(64) NOT NULL,
flow_type ENUM('in','out') NOT NULL,
flow_subtype ENUM('deposit','withdraw','transfer_in','transfer_out','subscribe','redeem','other') NOT NULL DEFAULT 'other',
amount DECIMAL(18,2) NOT NULL,
channel ENUM('online','mobile','counter','other') NOT NULL DEFAULT 'online',
counterparty_account_mask VARCHAR(32) NULL,
counterparty_name VARCHAR(64) NULL,
remark VARCHAR(128) NULL,
occurred_at DATETIME(3) NOT NULL,
KEY idx_customer (customer_id, occurred_at),
KEY idx_flow_subtype (flow_subtype, occurred_at),
CONSTRAINT fk_cf_customer FOREIGN KEY (customer_id) REFERENCES core_customer(customer_id)
) ENGINE=InnoDB COMMENT='资金进出';
CREATE TABLE core_product_nav (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
product_id VARCHAR(64) NOT NULL,
nav DECIMAL(10,4) NOT NULL,
daily_chg_pct DECIMAL(8,4) NOT NULL,
nav_date DATE NOT NULL,
UNIQUE KEY uk_product_date (product_id, nav_date),
CONSTRAINT fk_nav_product FOREIGN KEY (product_id) REFERENCES core_product(product_id)
) ENGINE=InnoDB COMMENT='产品净值';
-- ============================================================
-- 基金转换(convert)新增表 · T-1(2026-09-10)
-- 依据:docs/PRD/PRD-基金转换交易.md §4.1 · 架构设计-基金转换交易.md §9
-- ============================================================
-- 费率规则:redeem 按持有期分档(本期启用);subscribe 金额分档为真实 Core 接入扩展位(本期不启用)
CREATE TABLE core_fee_rule (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
product_id VARCHAR(64) NOT NULL,
fee_type ENUM('subscribe','redeem') NOT NULL,
min_hold_days INT NULL COMMENT 'redeem:持有期下界(含)',
max_hold_days INT NULL COMMENT 'redeem:持有期上界(不含);NULL=无上限',
min_amount DECIMAL(18,2) NULL COMMENT 'subscribe:金额下界(含);本期不启用',
max_amount DECIMAL(18,2) NULL COMMENT 'subscribe:金额上界(不含);本期不启用',
rate DECIMAL(6,4) NOT NULL COMMENT '费率;redeem 档位按 22 号文 §10 下限(<7d 1.5% / 7-30d 1% / 30-180d 0.5% / ≥180d 合同约定)',
to_fund_ratio DECIMAL(5,4) NOT NULL DEFAULT 1.0000 COMMENT '赎回费计入基金财产比例。22 号文 §10:赎回费全额计入基金财产 → 恒 1.0(旧规 75%/50%/25% 分级已废止)。本期仅留痕、不参与计算',
KEY idx_product_type (product_id, fee_type),
CONSTRAINT fk_fee_rule_product FOREIGN KEY (product_id) REFERENCES core_product(product_id)
) ENGINE=InnoDB COMMENT='费率规则(赎回费分档 / 申购费扩展位)';
-- 份额批次:FIFO 计费的权威源(普通申赎无批次时由 lot_bootstrap 兜底补建,D8/D18)
CREATE TABLE core_share_lot (
lot_id VARCHAR(64) NOT NULL PRIMARY KEY,
customer_id VARCHAR(64) NOT NULL,
product_id VARCHAR(64) NOT NULL,
qty DECIMAL(18,4) NOT NULL COMMENT '原始份额',
remain_qty DECIMAL(18,4) NOT NULL COMMENT '剩余份额(FIFO 扣减后;归零保留行不删,保可追溯)',
nav DECIMAL(10,4) NOT NULL COMMENT '成交净值',
confirmed_at DATETIME(3) NOT NULL COMMENT '份额确认时间(持有期自 T+1 起算,D14)',
source_trade_id VARCHAR(64) NULL COMMENT '来源流水(兜底补建时为 NULL)',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
KEY idx_cust_prod_time (customer_id, product_id, confirmed_at),
CONSTRAINT fk_lot_customer FOREIGN KEY (customer_id) REFERENCES core_customer(customer_id),
CONSTRAINT fk_lot_product FOREIGN KEY (product_id) REFERENCES core_product(product_id)
) ENGINE=InnoDB COMMENT='份额批次(FIFO 计费权威源)';
-- 转换批次计费明细:含 nav/nav_date 使 Core 侧自包含,供 rebuild_alerts 补偿(D6)
CREATE TABLE core_convert_lot_detail (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
convert_group_id VARCHAR(64) NOT NULL,
lot_id VARCHAR(64) NOT NULL COMMENT '被扣减的原批次(对应 core_share_lot.lot_id)',
qty DECIMAL(18,4) NOT NULL COMMENT '本批转出份额',
hold_days INT NOT NULL COMMENT '本批持有天数(自然日,不含申请日)',
amount DECIMAL(18,2) NOT NULL COMMENT '本批金额(已按 2 位四舍五入)',
fee_rate DECIMAL(6,4) NOT NULL COMMENT '本批适用费率',
fee_amount DECIMAL(18,2) NOT NULL COMMENT '本批费用(已按 2 位四舍五入)',
nav DECIMAL(10,4) NOT NULL COMMENT '本批成交净值',
nav_date DATE NOT NULL COMMENT '净值日期',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
KEY idx_group (convert_group_id),
KEY idx_lot (lot_id)
) ENGINE=InnoDB COMMENT='转换批次计费明细(Core 侧自包含,补偿数据源)';
-- ============================================================
-- T+1 受理/确认分离模型新增表(T-1 · 2026-09-11 · 开发计划 v2.0)
-- 依据:docs/PRD/PRD-基金转换交易.md §4 · 架构设计-基金转换交易.md v2.1 §9
-- ============================================================
-- 基金转换受理单(6 态权威状态机 · D21):T 日受理只落单不扣份额,
-- 在途占用由本表 left join 推导(FR-C21,不新增冻结列);T+1 确认段消费。
CREATE TABLE core_convert_request (
convert_group_id VARCHAR(64) NOT NULL PRIMARY KEY COMMENT '转换受理单号(= 后续确认段 convert_group_id)',
client_request_id VARCHAR(64) NULL COMMENT '客户端幂等键(uk_idem;NULL 可多行)',
customer_id VARCHAR(64) NOT NULL,
from_product_id VARCHAR(64) NOT NULL COMMENT '转出产品',
to_product_id VARCHAR(64) NOT NULL COMMENT '转入产品',
qty DECIMAL(18,4) NOT NULL COMMENT '申请转出份额',
actual_qty DECIMAL(18,4) NULL COMMENT '实际确认份额(部分成交 R-10;NULL=未确认)',
status ENUM('accepted','nav_pending','confirmed','rejected','cancelled','expired') NOT NULL COMMENT '6 态权威状态机',
cancel_before DATETIME(3) NULL COMMENT '撤单截止时刻(受理日 15:00;R-9 窗口判定)',
requested_at DATETIME(3) NOT NULL COMMENT '受理日(非交易日/超截点顺延到下一交易日,R-5)',
confirmed_at DATETIME(3) NULL COMMENT '确认完成时刻(T+1 批处理写入)',
coupon DECIMAL(18,2) NULL COMMENT '补差费(确认段回填;价外法 amount_diff)',
remark VARCHAR(128) NULL COMMENT '扩展备注(partial=部分成交等)',
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),
UNIQUE KEY uk_idem (client_request_id),
KEY idx_customer_status (customer_id, status),
KEY idx_status_asof (status, requested_at),
CONSTRAINT fk_creq_customer FOREIGN KEY (customer_id) REFERENCES core_customer(customer_id),
CONSTRAINT fk_creq_from_product FOREIGN KEY (from_product_id) REFERENCES core_product(product_id),
CONSTRAINT fk_creq_to_product FOREIGN KEY (to_product_id) REFERENCES core_product(product_id)
) ENGINE=InnoDB COMMENT='基金转换受理单(6 态权威状态机;占用推导源)';
-- A 股交易日历(Q11 拍板:T+1 工作日确认 / 15:00 顺延均依赖本表)
CREATE TABLE core_trade_calendar (
cal_date DATE NOT NULL PRIMARY KEY COMMENT '日历日',
is_open TINYINT(1) NOT NULL DEFAULT 1 COMMENT '1=交易日 0=休市',
remark VARCHAR(64) NULL COMMENT '休市事由(周末/元旦/春节/…)'
) ENGINE=InnoDB COMMENT='A股交易日历(依据上交所休市公告)';
-- 产品级份额舍入规则(D27 / R-13):business_type 维度覆盖 product 级默认;
-- 与 core_product.share_digits 二选一优先(T-14 DoD:规则行优先)
CREATE TABLE core_share_rule (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
product_id VARCHAR(64) NOT NULL,
business_type ENUM('subscribe','redeem','convert') NOT NULL,
share_digits TINYINT UNSIGNED NOT NULL DEFAULT 2 COMMENT '份额位数(默认 2;个别指数/货基 4)',
rounding_mode VARCHAR(16) NOT NULL DEFAULT 'half_up' COMMENT '舍入模式(显式 ROUND_HALF_UP)',
UNIQUE KEY uk_product_biz (product_id, business_type),
CONSTRAINT fk_share_rule_product FOREIGN KEY (product_id) REFERENCES core_product(product_id)
) ENGINE=InnoDB COMMENT='产品份额舍入规则(D27)';