Files
group_xinghuo_jinrong/scripts/core/00-grant.sql
T
GaoYiYuan_0626 c5182f1910 feat(convert): 基金转换 T-0/T-0b 门禁 + T-1 数据层 + T-2/T-2b 纯函数与实算回填
T-0 / T-0b(门禁 · 2026-09-10)
- T-0:sqlite 与 MySQL 结构对齐 —— core_holding 统一为 qty/cost_amount/as_of/pnl_pct
  + PK + UNIQUE(customer_id, product_id);补 core_product_nav;新增建库自校验
  _assert_ddl_aligned()(R-g);test_db.py 增 3 条门禁用例(含反向验证门禁失效)
- T-0b:DB 账号分离(D20)—— 新增 scripts/core/00-grant.sql(三账号逐表授权);
  settings.py 增 3 组账号;db.py 改 get_engine(db, role),缓存键改为 (库名, 角色),
  账号未配置回退单账号;core_ro→ro / gateway_repository→rw / risk·session_repository→rw;
  tests/conftest.py 四处显式 role="admin"(R-e)

T-1(数据层)
- scripts/core/01-ddl.sql:新建 core_fee_rule / core_share_lot / core_convert_lot_detail;
  core_trade 加 convert_group_id + idx_convert_group;core_product 加 8 列 + fee_rate 补 COMMENT
- 新增 07-seed-fee-rule.sql(赎回费 5 档 × 14 产品,按 22 号文 §10)/ 08-seed-share-lot.sql
  (58 行持仓 → 61 行批次,Σ remain_qty 恒等于 qty)/ 09-seed-org.sql(管理人 + TA +
  申购费率 + 最低持有余额,v1.1 按「管理人全产品线」重排)
- reset.ps1 追加 07/08/09;02-mysql-agent专用.sql 追加 risk_convert_detail
- tests/_ddl.py 同步 4 表 + 新增 REQUIRED_CONVERT_TABLES 建库门禁
- 新增 scripts/dev/verify_convert_seed.py(pymysql 等价 reset 流程 + 8 条 DoD 断言,
  含断言 ⑧「费率档 ↔ product_type 匹配」,越档即 FAIL)

T-2 / T-2b(纯函数包 + 示例实算回填)
- 新增 app/service/convert/ 7 文件:__init__ / types / calc / fee / nav / lot_bootstrap / errors
  (纯函数,不查库、不碰 SQL;所有量化显式 ROUND_HALF_UP;lot_bootstrap 用 zlib.crc32
   保证 D18 跨进程同源)
- 新增 tests/test_convert_calc.py 93 用例(12 类:HALF_UP 反向自证 / 分档边界 /
  FIFO 含同 confirmed_at 兜底 / 双口径 / 强制全转与强制赎回 / PRD §5.3 全链自证 /
  纯函数零 IO 依赖断言)
- 重写 scripts/dev/calc_convert_demo.py:去掉脚本内公式副本,改为调用生产 calc.py,
  末尾与 PRD §5.3 逐项比对(不一致即退出码 1),兼作一致性门禁

验证
- pytest 609 passed / 3 skipped(516 → +93,零回归)
- verify_convert_seed.py 8/8 PASS;calc_convert_demo.py 15/15 与 PRD §5.3 一致

文档:PRD v0.9.1(费率分类修正)· 架构 §7 签名回填 / §8.3 错误码注 / §15 T-2 完成 ·
开发计划 §1.5 新增 R-h + §4.2·§4.3 执行记录 · AGENTS.md · docs/memory
2026-09-10 14:45:55 +08:00

94 lines
6.8 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.
-- ============================================================================
-- 00-grant.sql · DB 账号与最小权限(D20 · 架构设计-基金转换交易 §11.1)
-- ============================================================================
-- 目的:把「Core 只读」「审计不可删改」从**代码约定**升级为 **DB 级强制**。
-- 依据主架构评审 C1:原备选「代码层 SQL 白名单」已否决(注释/子查询/
-- 存储过程易绕过),独立最小权限账号是唯一可靠手段。
--
-- 执行:管理员(root)执行,且**不进 reset.ps1**
-- —— DROP DATABASE 不清除 mysql.db 中的授权行,本脚本只需执行一次;
-- 且建账号需管理员权限,与 reset.ps1 的运行账号不是一回事。
-- 命令:mysql -u root -p < scripts/core/00-grant.sql
--
-- ⚠️ 执行前:把三处 REPLACE_ME 换成 .env 中同名配置的真实密码。
-- 本文件**禁止提交真实密码**;改完执行,执行完不要提交本次改动。
-- 若 PyMySQL 报认证插件错误(MySQL 8.0+ caching_sha2_password),
-- 把 IDENTIFIED BY 改为 IDENTIFIED WITH mysql_native_password BY。
--
-- 与 settings.py 的对应(账号留空 → db.get_engine 回退 mysql_user):
-- xh_core_ro → mysql_core_ro_user / mysql_core_ro_password
-- xh_core_rw → mysql_core_rw_user / mysql_core_rw_password
-- xh_agent_rw → mysql_agent_user / mysql_agent_password
-- ============================================================================
-- ---------------------------------------------------------------------------
-- 1) xh_core_ro · jinrong_core 全库只读
-- 使用者:core_ro / core_tools / deps 归属校验 / risk 扫描,
-- 以及 **gateway 的校验读**(架构 §11.1 关键边界)
-- ---------------------------------------------------------------------------
CREATE USER IF NOT EXISTS 'xh_core_ro'@'%' IDENTIFIED BY 'REPLACE_ME_core_ro';
GRANT SELECT ON jinrong_core.* TO 'xh_core_ro'@'%';
-- ---------------------------------------------------------------------------
-- 2) xh_core_rw · 仅 gateway 写路径:限 4 表,无 DELETE / 无 DDL / 无 GRANT
-- 使用者:gateway_repository.insert_trade · convert_core_repository.apply_convert
--
-- ⚠️ 关键边界:**gateway 读走 xh_core_ro、写走本账号**,两个 engine 并存。
-- 本账号**刻意不授** core_product / core_fee_rule / core_product_nav 的读权限,
-- 故 T-6 的 convert 计算链路读取产品/费率/净值**必须走只读账号**;
-- 不得为图省事给本账号补 SELECT ——否则 gateway 顺带获得全库读权限,
-- 「最小权限」落空(这是本任务最易走偏的一处)。
--
-- 表级 GRANT 可针对**尚不存在**的表:core_share_lot / core_convert_lot_detail
-- 由 T-1 创建,故本脚本可合法地先于 T-1 执行。
-- ---------------------------------------------------------------------------
CREATE USER IF NOT EXISTS 'xh_core_rw'@'%' IDENTIFIED BY 'REPLACE_ME_core_rw';
GRANT SELECT, INSERT, UPDATE ON jinrong_core.core_trade TO 'xh_core_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_core.core_share_lot TO 'xh_core_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_core.core_holding TO 'xh_core_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_core.core_convert_lot_detail TO 'xh_core_rw'@'%';
-- ---------------------------------------------------------------------------
-- 3) xh_agent_rw · jinrong_agent 业务表读写(无 DELETE)
-- 使用者:risk_repository / session_repository / convert_repository 等 Agent 侧全部
--
-- 不授 DELETE 的依据(T-0b 实测):app/ 与 scripts/ 全仓 **无任何
-- DELETE / TRUNCATE / DROP**,故不授 DELETE 不影响任何现有代码路径。
-- tests/conftest.py 的 teardown 需要 DELETE → 显式改用 role="admin"(R-e)。
-- ---------------------------------------------------------------------------
CREATE USER IF NOT EXISTS 'xh_agent_rw'@'%' IDENTIFIED BY 'REPLACE_ME_agent_rw';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.agent_session TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.agent_message TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.agent_tool_call TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.input_guard_log TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.customer_advisor_rel TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.customer_profile_l1 TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.customer_profile_l2 TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.customer_profile_l3 TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.risk_alert TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.risk_suitability_log TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.customer_threshold_config TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.customer_notify_log TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.advisor_draft TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.compliance_hit_log TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.analytics_query_log TO 'xh_agent_rw'@'%';
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.risk_aml_list TO 'xh_agent_rw'@'%';
-- risk_convert_detail 由 T-1 建表(本行可先于建表执行)
GRANT SELECT, INSERT, UPDATE ON jinrong_agent.risk_convert_detail TO 'xh_agent_rw'@'%';
-- 审计表:**追加写、不可改不可删**
-- 口径澄清(T-0b 修正架构表述):架构 §11.1 原文写「audit_log 只授 INSERT」,
-- 字面执行会连 SELECT 一并剥夺,导致 risk_repository.has_engine_error_audit
-- (:231 SELECT 1 FROM audit_log)与 list_audit_events(:420 SELECT *)双双失败。
-- 红线本意是「**只能追加,不能修改或删除**」→ 正确授权 = SELECT + INSERT。
GRANT SELECT, INSERT ON jinrong_agent.audit_log TO 'xh_agent_rw'@'%';
-- ---------------------------------------------------------------------------
-- 4) 执行后自检(管理员执行,非必需)
-- ---------------------------------------------------------------------------
-- SHOW GRANTS FOR 'xh_core_ro'@'%'; -- 期望:仅 SELECT ON jinrong_core.*
-- SHOW GRANTS FOR 'xh_core_rw'@'%'; -- 期望:4 表 SELECT/INSERT/UPDATE,无 DELETE、无 ALL
-- SHOW GRANTS FOR 'xh_agent_rw'@'%'; -- 期望:业务表三权限;audit_log 仅 SELECT, INSERT
-- 注:CREATE USER / GRANT 会自动刷新权限缓存,无需 FLUSH PRIVILEGES。