78 KiB
合并后数据库表设计
项目内基线快照:来源于用户确认的新版数据库设计。
强制约束:已有表名和已有字段定义不可修改;本文附录中的示例 SQL 不得绕过 Alembic 和02-数据库建表设计.md的增量规则直接执行。
后续只允许通过新增表、新增字段及兼容索引扩展。
1. 文档目的与结论
本文合并以下两份输入设计:
- 整体项目的
数据库表设计.md。 - 风控模块的
数据库表结构对接说明.md。
最终采用“统一身份主数据 + 按业务域分表”的方案:客户和员工只在 sys_user 中各有一条身份记录;客户的金融画像、交易、资金、持仓由金融域表维护;风控模块不再维护 risk_customers、risk_products、risk_transactions、risk_capital_flows、risk_holdings、risk_login_records 六张重复表。
本方案共 33 张 MySQL 表。为兼容现有风控代码,在原 30 张表基础上增加 fin_nav_history、fin_capital_flow 和 user_facts;其中 fin_capital_flow 由原兼容视图调整为独立表。fin_risk_alert、fin_risk_notification、biz_work_order、conversation_message 和 interaction_audit 是风控模块当前会写入的业务表;其他表是其只读研判数据源。该边界能避免风控模块与交易、账户、鉴权模块相互耦合。
交易范围明确为场内基金模拟交易:外部行情 API 只提供实时价格和日线数据,委托、成交、资金和持仓均为系统内部虚拟数据。系统不提供充值、提现、银行流水匹配或场外基金申购赎回能力。
本文是待评审的逻辑和物理设计,不执行任何数据库变更。实际落库必须使用项目统一的数据库迁移脚本。
2. 已修正的冲突
| 问题 | 原因 | 合并后的处理 |
|---|---|---|
| 用户表重复 | 整体设计使用 sys_user,风控设计另建 risk_customers |
删除 risk_customers 的设计地位;客户与员工统一使用 sys_user.id |
| 客户风险字段放置不当 | 客户与员工共表时,年龄、职业、资产等字段不适用于员工 | 新增一对一的 fin_customer_profile,仅客户拥有该画像 |
| 产品、交易、持仓重复 | risk_* 与 fin_* 表表达相同业务对象 |
全部以 fin_product、fin_sim_order、fin_transaction、fin_holding 为准 |
| 交易市场语义冲突 | 原设计混用了场外基金净值确认、银行流水和场内实时成交 | 统一为场内模拟交易:外部 API 提供行情,内部维护虚拟委托、成交、资金和持仓 |
| 登录失败次数语义不清 | 单条登录记录中保存累计失败数会造成重复统计 | 使用 login_result 记录每次尝试,失败次数由时间窗聚合 |
| 预警状态冲突 | 两份文档分别使用“处理中/已确认”和“调查中/已升级” | 使用统一状态 待处理/调查中/已排除/已结案;“升级”改为独立标记,非终态 |
| 风险等级表述不一致 | 整体设计是“低/中/高”,风控演示是“低风险/中风险/高风险” | 数据库存 低/中/高,接口展示层映射为 低风险/中风险/高风险 |
| 交易委托与处置工单混淆 | fin_sim_order 表达交易委托,不能替代风险调查、上报和处置工单 |
交易合规留痕放入 fin_sim_order;风险处置流程使用独立 biz_work_order |
| 表数量统计不一致 | 整体文档标题为 14 张、正文说明 16 张,但总览实际列出 17 张 | 本兼容版按实际表逐张列出,共 33 张,不再使用不准确统计 |
3. 命名、主键与边界
| 项目 | 约定 |
|---|---|
| 数据库 | MySQL 8.0,utf8mb4,默认库名 aaa |
| 主键 | 统一 BIGINT UNSIGNED AUTO_INCREMENT,业务编号另设唯一键 |
| 用户引用 | 统一使用 user_id、customer_id、employee_id,均逻辑关联 sys_user.id |
| 产品引用 | 统一使用 product_id,逻辑关联 fin_product.id |
| 时间 | 统一 DATETIME,字段以 _at 结尾 |
| 金额、价格与数量 | 金额 DECIMAL(18,2);价格 DECIMAL(18,6);场内基金数量 DECIMAL(18,4),具体最小单位由产品表控制 |
| 删除策略 | 首版不物理删除交易、预警、审计、对话和通知记录;如需要删除业务对象,使用状态字段处理 |
| 外键策略 | 用户、产品、委托、成交、账户、持仓和权限等核心关系首版即建立外键;跨存储关系继续由应用层校验 |
| 风控写边界 | 风控写 fin_risk_alert、fin_risk_notification、biz_work_order、conversation_message 与 interaction_audit;规则扫描只读交易、资金、持仓和账户数据 |
| 行情边界 | 实时行情放 Redis 并设置短 TTL;MySQL 保存日线数据与委托/成交时采用的不可变行情快照 |
4. 表总览
| 域 | 表名 | 归属 | 风控访问方式 | 用途 |
|---|---|---|---|---|
| 账号 | sys_user |
平台 | 只读 | 客户、员工的唯一身份主数据 |
| 权限 | sys_role |
平台 | 只读 | 角色定义 |
| 权限 | sys_permission |
平台 | 只读 | 资源、动作、数据范围和字段策略 |
| 权限 | sys_user_role |
平台 | 只读 | 用户与角色多对多关系 |
| 权限 | sys_role_permission |
平台 | 只读 | 角色与权限多对多关系 |
| 账号 | sys_customer_assignment |
平台 | 只读 | 客户与投顾/运营人员归属 |
| 客户 | fin_customer_profile |
金融 | 只读 | 客户金融画像、资产和行为评分 |
| 客户 | fin_risk_assessment |
金融 | 只读 | 风险测评历史 |
| 记忆 | user_facts |
智能体 | 只读 | 当前代码使用的客户软事实兼容表 |
| 记忆 | memory_unit |
智能体 | 只读 | 中期记忆单元及置信度状态 |
| 记忆 | memory_evidence |
智能体 | 只读 | 记忆的独立证据与来源引用 |
| 记忆 | memory_conflict |
智能体 | 只读 | 记忆之间的冲突及解决结果 |
| 记忆 | profile_snapshots |
智能体 | 只读 | 长期画像的 MySQL 版本投影 |
| 记忆 | memory_sync_outbox |
智能体 | 只读 | Milvus、Neo4j可靠同步事件 |
| 产品 | fin_product |
金融 | 只读 | 产品目录及适当性要求 |
| 行情 | fin_market_price |
行情 | 只读 | 外部 API 同步的场内日线行情 |
| 行情 | fin_nav_history |
金融 | 只读 | 当前代码使用的基金净值历史表 |
| 资金 | fin_sim_account |
交易 | 只读 | 客户虚拟资金账户及冻结余额 |
| 资金 | fin_cash_ledger |
交易 | 只读 | 虚拟资金变动明细,不提供充值提现入口 |
| 资金 | fin_capital_flow |
金融/风控 | 只读 | 当前规则扫描使用的标准化资金流水表 |
| 费用 | fin_fee_rule |
交易 | 只读 | 场内模拟交易费率及客户分层规则 |
| 交易 | fin_sim_order |
交易 | 只读 | 场内基金买卖委托及合规留痕 |
| 交易 | fin_transaction |
交易 | 只读 | 模拟成交明细,一笔委托可对应多笔成交 |
| 交易 | fin_holding |
金融 | 只读 | 当前持仓汇总 |
| 工单 | biz_work_order |
风控/运营 | 读写 | 风险处置工单,并保留当前交易申请工单兼容字段;不替代场内撮合委托 |
| 账号 | sys_login_record |
账号 | 只读 | 每次登录/登录失败记录 |
| 风控 | fin_risk_alert |
风控 | 读写 | 规则命中、证据、人工处置、智能体辅助结果 |
| 风控 | fin_risk_notification |
风控 | 读写 | 站内提醒和邮件发送记录 |
| 知识 | fin_knowledge_meta |
智能体 | 只读 | 知识库原文件元数据 |
| 记忆 | episodes |
智能体 | 只读 | 会话摘要 |
| 记忆 | conversation_message |
智能体 | 读写 | 完整对话留存 |
| 审计 | interaction_audit |
平台 | 追加写入 | 操作审计与预警处置轨迹 |
| 内容 | client_facing_content |
投顾 | 只读 | 面向客户内容的审核与发布状态 |
fin_capital_flow 是兼容现有规则扫描的独立标准化流水表,由资金域同步或受控导入;风控模块只读,不通过该表发起充值、提现或修改账户余额。
5. 核心关系
sys_user(客户) 1 --- 1 fin_customer_profile
sys_user(客户) 1 --- N fin_risk_assessment
sys_user(客户) 1 --- N user_facts
sys_user(客户) 1 --- 1 fin_sim_account
sys_user(客户) 1 --- N fin_sim_order --- N:1 fin_product
fin_sim_order 1 --- 0..N fin_transaction --- N:1 fin_product
fin_sim_account 1 --- N fin_cash_ledger
sys_user(客户) 1 --- N fin_capital_flow
fin_product 1 --- N fin_nav_history
sys_user(客户) 1 --- N fin_transaction
sys_user(客户) 1 --- N sys_login_record
sys_user(客户) 1 --- N fin_risk_alert 1 --- N fin_risk_notification
fin_risk_alert 1 --- 0..N biz_work_order
fin_sim_order 1 --- 0..N fin_risk_alert
sys_user(员工) 1 --- N sys_customer_assignment --- N:1 sys_user(客户)
sys_user N --- N sys_role
sys_role N --- N sys_permission
sys_user(客户) 1 --- N episodes 1 --- N conversation_message(按session/message范围逻辑关联)
sys_user(客户) 1 --- N memory_unit --- N:1 memory_evidence
memory_unit N --- N memory_unit(通过memory_conflict关联)
sys_user(客户) 1 --- N profile_snapshots
memory_unit/profile_snapshots 1 --- N memory_sync_outbox
场内模拟交易与风控的固定读取链路如下:
外部行情 API -> Redis 实时行情 / fin_market_price 日线行情
-> fin_sim_order(买卖委托、行情快照、适当性和风控结果)
-> fin_transaction(模拟成交)
-> fin_sim_account + fin_cash_ledger + fin_holding(原子更新)
-> fin_risk_alert(命中后写入预警)
-> biz_work_order(需要人工调查、上报或处置时创建)
-> fin_risk_notification(高风险时写入通知)
项目没有充值、提现和银行流水匹配通道,因此依赖真实入出金的 AML 规则不在当前可执行范围内;不得用虚构的充值接口伪装为已实现能力。交易频率、金额与画像偏离、异常时段、老年客户大额交易等可由现有模拟委托与成交数据执行。
6. 详细字段设计
6.1 账号与权限
sys_user 统一用户表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 用户主键 |
| user_no | VARCHAR(32) | 唯一 | 面向业务的客户号或员工号 |
| username | VARCHAR(64) | 唯一 | 登录账号 |
| VARCHAR(128) | 唯一、可空 | 登录或通知邮箱;存储前规范化为小写,展示时按权限脱敏 | |
| password_hash | VARCHAR(255) | 非空 | 密码摘要,不向风控模块开放 |
| user_type | VARCHAR(16) | 索引、非空 | CUSTOMER 或 EMPLOYEE |
| employee_role | VARCHAR(32) | 可空、索引 | 员工主岗位兼容缓存;仅供旧查询和展示,不作为授权依据,权威角色在 sys_user_role |
| customer_tier | VARCHAR(16) | 可空 | 客户分层,仅客户使用 |
| investor_type | VARCHAR(8) | 可空 | C1-C5 缓存值;权威历史在风险测评表 |
| investor_type_assessed_at | DATETIME | 可空 | 当前风险测评时间 |
| is_professional_investor | TINYINT(1) | 非空,默认 0 | 是否已认定专业投资者 |
| professional_investor_status | VARCHAR(16) | 非空 | 未申请、审核中、已认定、已拒绝 |
| professional_investor_certified_at | DATETIME | 可空 | 专业投资者认定时间 |
| fund_account_status | VARCHAR(16) | 非空 | 未开户、已开户 |
| fund_account_opened_at | DATETIME | 可空 | 基金账户开户时间 |
| status | VARCHAR(16) | 索引、非空 | 待审核、正常、禁用、冻结 |
| created_at | DATETIME | 非空 | 创建时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
不保留原整体设计的 net_worth_flag:它可由 customer_tier 或 fin_customer_profile.total_asset 计算,冗余存储会造成不一致。
employee_role 只兼容旧代码中按单一主岗位扫描员工的查询。员工可以拥有多个角色,授权、数据范围和JWT角色声明仍由 sys_user_role 与 sys_role 生成;超级管理员调整主岗位时,应在同一事务中更新该缓存与对应角色关系。服务端不能使用 employee_role 做鉴权,也不能只信任令牌中的角色缓存。
RBAC 权限表
| 表名 | 关键字段 | 关键约束/索引 | 说明 |
|---|---|---|---|
sys_role |
id、role_code、role_name、status、created_at、updated_at | role_code 唯一 |
角色定义,如 customer、advisor、operator、risk_operator、admin |
sys_permission |
id、permission_code、resource、action、data_scope、field_policy(JSON)、created_at、updated_at | permission_code 唯一;索引 (resource, action) |
data_scope 取 self/own_customers/all;字段策略定义允许读取和脱敏的字段 |
sys_user_role |
id、user_id、role_id、assigned_at、expires_at | 唯一键 (user_id, role_id);分别索引用户和角色 |
一个用户可以拥有多个角色;过期角色不参与授权 |
sys_role_permission |
id、role_id、permission_id、created_at | 唯一键 (role_id, permission_id) |
角色与权限多对多关系 |
权限校验顺序为:认证用户 -> 加载有效角色 -> 合并角色权限 -> 应用 data_scope -> 应用字段策略。own_customers 必须通过 sys_customer_assignment 在服务端约束客户范围,NL2SQL 生成内容不能绕过该过滤。
RBAC 授权采用“权限分配给角色、角色分配给用户”的方式,不直接为单个用户配置权限:
- 客户账号审核通过时,系统在同一事务中将
sys_user.status更新为正常,并向sys_user_role自动写入内置customer角色;重复审核必须依靠唯一键(user_id, role_id)保证幂等。 - 员工账号审核通过时只启用账号,不自动授予业务角色。超级管理员根据岗位向
sys_user_role分配advisor、operator、risk_operator或admin,一个员工可以拥有多个有效角色。 - 超级管理员通过
sys_role_permission维护每个角色的权限集合;除内置客户角色的自动分配外,业务角色的授予、撤销和有效期调整均由超级管理员执行。 user_type只区分客户与员工账号,不能作为授权依据;实际访问权限始终由有效的sys_user_role -> sys_role -> sys_role_permission -> sys_permission链路计算。super_admin是系统初始化的内置角色,首个超级管理员由初始化脚本或受控部署流程创建,不能依赖后台用户自行授权。该角色以及关键系统权限不得通过普通管理接口删除。- 客户审核、员工角色授予或撤销、角色权限变更都必须向
interaction_audit追加记录,至少保存操作者、目标用户或角色、变更前后内容和发生时间。
sys_customer_assignment 客户归属表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 记录主键 |
| customer_id | BIGINT UNSIGNED | 联合唯一键、索引 | 客户用户 ID |
| employee_id | BIGINT UNSIGNED | 索引 | 员工用户 ID |
| employee_role | VARCHAR(32) | 联合唯一键 | 投顾或运营 |
| assigned_at | DATETIME | 非空 | 归属生效时间 |
| unassigned_at | DATETIME | 可空 | 归属失效时间 |
唯一约束为 (customer_id, employee_role, unassigned_at) 不适合 MySQL 的 NULL 唯一语义。首版应只保存当前归属,并使用唯一键 (customer_id, employee_role);若后续要求保存归属历史,应拆出专用历史表或增加 is_current 参与唯一键。
6.2 客户、产品与交易
fin_customer_profile 客户金融画像表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| customer_id | BIGINT UNSIGNED | 主键 | 一对一关联 sys_user.id |
| trade_account | VARCHAR(32) | 唯一、非空 | 交易账号;关联客户主表,一个客户对应一个交易账号 |
| real_name | VARCHAR(64) | 非空 | 客户真实姓名;展示时按权限脱敏 |
| birth_date | DATE | 可空 | 使用出生日期替代易过期的 age |
| occupation | VARCHAR(64) | 可空 | 职业 |
| mobile_masked | VARCHAR(32) | 可空 | 脱敏手机号 |
| investor_type | VARCHAR(8) | 非空 | 客户风险承受能力等级:C1/C2/C3/C4/C5;与最新有效风险测评一致 |
| investment_horizon | VARCHAR(32) | 可空 | 投资期限偏好:短期/中期/中长期/长期 |
| preferred_asset_class | JSON | 可空 | 偏好资产类别数组,例如 ["股票型","混合型"] |
| trading_frequency | VARCHAR(32) | 可空 | 交易频率特征:高频/中频/低频 |
| last_active_at | DATETIME | 可空 | 最后活跃时间;按本文统一时间字段 _at 命名(原 last_active_time) |
| total_asset | DECIMAL(18,2) | 非空 | 风控研判所用资产快照 |
| behavior_score | INT | 非空 | 行为异常评分,定义范围 0-100 |
| risk_tags | JSON | 可空 | 风险标签数组,禁止逗号拼接字符串 |
| opened_at | DATETIME | 可空 | 客户开户时间 |
| updated_at | DATETIME | 非空 | 最后同步时间 |
画像字段合并说明:is_high_net_worth 不单独落库,可由 customer_tier 或
total_asset 按统一口径计算;这样避免与用户表及资产快照产生不一致。trade_account
是客户的交易系统业务标识,不能用内部 customer_id 直接替代。
fin_risk_assessment 风险测评历史表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 测评记录主键 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, assessed_at) |
客户用户 ID |
| questionnaire_version | VARCHAR(32) | 索引、非空 | 风险测评问卷版本,用于还原证据口径 |
| answers | JSON | 非空 | 问卷答案及每题得分 |
| total_score | INT | 非空 | 总分 |
| investor_type | VARCHAR(8) | 非空 | C1-C5 |
| assessed_at | DATETIME | 非空 | 测评时间 |
| valid_until | DATETIME | 非空、索引 | 有效截止时间 |
| created_at | DATETIME | 非空 | 创建时间 |
user_facts 客户软事实兼容表(新增)
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 事实记录主键 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, fact_key)、非空 |
客户用户 ID |
| fact_key | VARCHAR(128) | 索引 (customer_id, fact_key)、非空 |
事实键 |
| fact_value | JSON | 非空 | 结构化事实值 |
| source_portal | VARCHAR(16) | 非空 | 事实来源端 |
| source_episode_id | BIGINT UNSIGNED | 可空 | 来源会话片段 ID |
| confidence | FLOAT | 非空、默认 1.0 | 置信度 |
| is_critical | TINYINT(1) | 非空、默认 0 | 是否为关键事实 |
| created_at | DATETIME | 非空 | 创建时间 |
该表保留当前 ORM 的完整字段,作为现有代码读取软事实的兼容入口;后续接入 memory_unit 后可由记忆编排服务同步,但不得覆盖正式风险测评、交易、持仓等权威事实。
fin_product 产品表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 产品主键 |
| product_code | VARCHAR(32) | 唯一 | 场内证券代码 |
| product_name | VARCHAR(128) | 非空 | 产品名称 |
| exchange_code | VARCHAR(16) | 索引、非空 | 交易所代码,如 SSE、SZSE |
| product_category | VARCHAR(32) | 索引、非空 | ETF、LOF、封闭式基金等场内类别 |
| risk_level | VARCHAR(8) | 索引、非空 | R1-R5 |
| fund_manager | VARCHAR(64) | 可空 | 基金管理人 |
| currency | VARCHAR(8) | 非空,默认 CNY | 交易币种 |
| lot_size | DECIMAL(18,4) | 非空 | 最小交易数量单位 |
| price_tick | DECIMAL(18,6) | 非空 | 最小价格变动单位 |
| current_nav | DECIMAL(18,6) | 可空 | 外部 API 最新单位净值缓存,不作为场内成交价 |
| current_nav_at | DATETIME | 可空 | 最新单位净值对应时间或净值日期 |
| min_amount | DECIMAL(18,2) | 非空,默认 0 | 系统允许的最低委托金额;场内数量仍须同时满足 lot_size |
| open_start_at | DATETIME | 可空 | 产品对本系统开放交易的开始时间 |
| open_end_at | DATETIME | 可空 | 产品对本系统开放交易的结束时间;为空表示未设结束时间 |
| open_period_start | DATE | 可空 | 当前 ORM 使用的开放开始日期兼容字段 |
| open_period_end | DATE | 可空 | 当前 ORM 使用的开放结束日期兼容字段 |
| transaction_fee_rate | DECIMAL(10,6) | 可空 | 默认展示费率兼容缓存;实际费用必须按 fin_fee_rule 计算并固化 |
| single_investor_max_holding_ratio | DECIMAL(7,4) | 非空,默认 100 | 单一投资者持有基金份额占基金总份额的比例上限,单位%;取值范围大于0且不超过100 |
| management_fee_rate | DECIMAL(7,4) | 可空 | 管理费率 |
| custodian_fee_rate | DECIMAL(7,4) | 可空 | 托管费率 |
| risk_disclosure_required | TINYINT(1) | 非空,默认 0 | 是否需要风险揭示 |
| second_confirmation_required | TINYINT(1) | 非空,默认 0 | 是否需要二次确认 |
| recording_required | TINYINT(1) | 非空,默认 0 | 是否需要双录 |
| status | VARCHAR(16) | 索引、非空 | 上市、停牌、退市 |
| created_at | DATETIME | 非空 | 创建时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
产品主数据不保存实时成交价格。current_nav 和 transaction_fee_rate 是为现有查询保留的非权威缓存:前者不能代替场内成交价,后者不能代替费率规则。实时行情和最新基金总份额从外部 API 拉取后写入 Redis;每日最新基金总份额随日线行情写入 fin_market_price。委托与成交必须保存所采用的行情时间和价格快照,保证结果可复现。
fin_market_price 场内行情历史表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 记录主键 |
| product_id | BIGINT UNSIGNED | 唯一键 (product_id, trade_date) |
产品 ID |
| trade_date | DATE | 唯一键 (product_id, trade_date) |
交易日 |
| open_price | DECIMAL(18,6) | 非空 | 开盘价 |
| high_price | DECIMAL(18,6) | 非空 | 最高价 |
| low_price | DECIMAL(18,6) | 非空 | 最低价 |
| close_price | DECIMAL(18,6) | 非空 | 收盘价 |
| volume | DECIMAL(24,4) | 可空 | 成交数量 |
| turnover_amount | DECIMAL(24,2) | 可空 | 成交金额 |
| total_fund_shares | DECIMAL(24,4) | 非空 | 该交易日最新基金总份额;必须大于0 |
| source | VARCHAR(32) | 非空 | 行情 API 来源 |
| source_updated_at | DATETIME | 非空 | 外部数据更新时间 |
| created_at | DATETIME | 非空 | 入库时间 |
fin_nav_history 基金净值历史兼容表(新增)
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 净值记录主键 |
| product_id | BIGINT UNSIGNED | 唯一键 (product_id, nav_date)、非空 |
产品 ID |
| nav_date | DATE | 唯一键 (product_id, nav_date)、非空 |
净值日期 |
| nav | DECIMAL(12,6) | 非空 | 单位净值 |
| created_at | DATETIME | 非空 | 入库时间 |
该表供当前 NavHistory ORM 使用,与 fin_market_price 并存:前者保留基金净值语义,后者保存完整场内日线行情,不删除或替代原行情字段。
fin_sim_account 虚拟资金账户表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 虚拟账户主键 |
| account_no | VARCHAR(32) | 唯一、非空 | 虚拟资金账号 |
| customer_id | BIGINT UNSIGNED | 唯一、非空 | 客户用户 ID |
| currency | VARCHAR(8) | 非空,默认 CNY | 币种 |
| cash_balance | DECIMAL(18,2) | 非空 | 现金总余额 |
| available_cash | DECIMAL(18,2) | 非空 | 可用于买入的余额 |
| frozen_cash | DECIMAL(18,2) | 非空,默认 0 | 委托冻结余额 |
| initial_balance | DECIMAL(18,2) | 非空 | 测试数据初始化金额,只能由种子数据或管理员初始化任务写入 |
| status | VARCHAR(16) | 索引、非空 | 正常、冻结、停用 |
| version | BIGINT UNSIGNED | 非空,默认 0 | 乐观锁版本号 |
| created_at | DATETIME | 非空 | 创建时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
系统不提供充值或提现 API。任何余额变化都必须来自初始化、买入扣款、卖出回款、费用扣除或冲正,并在资金明细账中留痕。
fin_cash_ledger 虚拟资金明细表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 明细主键 |
| ledger_no | VARCHAR(64) | 唯一、非空 | 资金明细业务编号 |
| account_id | BIGINT UNSIGNED | 索引 (account_id, occurred_at) |
虚拟账户 ID |
| transaction_id | BIGINT UNSIGNED | 可空、索引 | 关联模拟成交 ID |
| entry_type | VARCHAR(24) | 非空 | 初始化、买入扣款、卖出回款、费用扣除、冻结、解冻、冲正 |
| amount | DECIMAL(18,2) | 非空 | 有符号变动金额 |
| balance_after | DECIMAL(18,2) | 非空 | 变动后的现金余额 |
| available_cash_after | DECIMAL(18,2) | 非空 | 变动后的可用余额 |
| frozen_cash_after | DECIMAL(18,2) | 非空 | 变动后的冻结余额 |
| idempotency_key | VARCHAR(128) | 唯一、非空 | 防止重复记账 |
| occurred_at | DATETIME | 索引、非空 | 发生时间 |
| created_at | DATETIME | 非空 | 入库时间 |
fin_capital_flow 标准化资金流水兼容表(由视图调整为表)
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 资金流水主键 |
| flow_no | VARCHAR(64) | 唯一、非空 | 当前 ORM 使用的流水业务编号 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, settled_at)、非空 |
客户用户 ID |
| account_id | BIGINT UNSIGNED | 可空、索引 | 原兼容视图暴露的虚拟账户 ID |
| transaction_id | BIGINT UNSIGNED | 可空、索引 | 原兼容视图暴露的模拟成交 ID |
| flow_type | VARCHAR(16) | 非空 | 入金、出金或由资金明细映射的标准化方向 |
| amount | DECIMAL(18,2) | 非空 | 流水金额 |
| balance_after | DECIMAL(18,2) | 可空 | 原兼容视图暴露的变动后余额 |
| status | VARCHAR(16) | 索引、非空 | 成功、处理中、失败;只有成功记录参与规则扫描 |
| settled_at | DATETIME | 索引、可空 | 当前 ORM 使用的实际到账时间 |
| occurred_at | DATETIME | 索引、可空 | 原兼容视图暴露的资金发生时间 |
| payer_name | VARCHAR(64) | 可空 | 付款人名称或脱敏标识 |
| source_type | VARCHAR(16) | 非空 | 银行流水、人工补录、外部同步、虚拟账本 |
| related_work_order_id | BIGINT UNSIGNED | 可空、索引 | 当前 ORM 使用的关联业务工单 ID |
| match_status | VARCHAR(16) | 非空、默认“不适用” | 未匹配、已匹配、异常、不适用 |
| created_at | DATETIME | 非空 | 入库时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
该表保留原兼容视图暴露的全部字段,并新增当前 CapitalFlow ORM 所需字段。资金域可从 fin_cash_ledger 同步生成记录,风控模块只读;不得通过该表直接修改虚拟账户余额或伪造充值、提现能力。
fin_fee_rule 场内模拟交易费率规则表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 规则主键 |
| rule_code | VARCHAR(64) | 唯一、非空 | 费率规则编号 |
| product_id | BIGINT UNSIGNED | 可空、索引 | 指定产品;为空时表示通用规则 |
| exchange_code | VARCHAR(16) | 可空、索引 | 适用交易所 |
| order_side | VARCHAR(8) | 非空 | 买入、卖出 |
| customer_tier | VARCHAR(16) | 可空、索引 | 适用客户层级;为空表示全部 |
| min_trade_amount | DECIMAL(18,2) | 可空 | 适用金额下限 |
| max_trade_amount | DECIMAL(18,2) | 可空 | 适用金额上限 |
| fee_rate | DECIMAL(10,6) | 非空 | 按成交金额计算的比例费率 |
| minimum_fee | DECIMAL(18,2) | 非空,默认 0 | 单笔最低费用 |
| fixed_fee | DECIMAL(18,2) | 非空,默认 0 | 固定费用 |
| priority | INT | 非空,默认 0 | 多条规则同时命中时的优先级 |
| effective_from | DATETIME | 非空 | 生效时间 |
| effective_until | DATETIME | 可空 | 失效时间 |
| status | VARCHAR(16) | 索引、非空 | 启用、停用 |
| created_at | DATETIME | 非空 | 创建时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
场内交易不使用场外基金按持有天数计算的赎回费。成交时将最终费率、最低费用处理结果和费用金额固化到成交记录,避免历史规则修改后无法复算。
fin_sim_order 场内模拟委托表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 委托主键 |
| order_no | VARCHAR(64) | 唯一、非空 | 委托编号 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, submitted_at) |
客户用户 ID |
| account_id | BIGINT UNSIGNED | 索引 | 虚拟资金账户 ID |
| product_id | BIGINT UNSIGNED | 索引 | 场内产品 ID |
| order_side | VARCHAR(8) | 非空 | 买入、卖出 |
| price_type | VARCHAR(8) | 非空 | 市价、限价;首版只执行市价 |
| quantity | DECIMAL(18,4) | 非空 | 委托数量,必须满足产品最小交易单位 |
| limit_price | DECIMAL(18,6) | 可空 | 限价委托价格;市价委托为空 |
| quote_price | DECIMAL(18,6) | 非空 | 提交时采用的实时行情快照 |
| quote_at | DATETIME | 非空 | 行情快照时间 |
| quote_source | VARCHAR(32) | 非空 | 行情 API 来源 |
| channel | VARCHAR(32) | 可空 | 提交渠道 |
| advisor_id | BIGINT UNSIGNED | 可空、索引 | 销售/投顾人员 ID |
| filled_quantity | DECIMAL(18,4) | 非空,默认 0 | 累计成交数量 |
| average_executed_price | DECIMAL(18,6) | 可空 | 加权平均成交价格 |
| status | VARCHAR(24) | 索引、非空 | 待风控、待运营确认、待合规复核、已接受、已成交、已拒绝、已撤单 |
| risk_rule_hits | JSON | 可空 | 交易准入规则命中码 |
| risk_disclosure_ack_at | DATETIME | 可空 | 风险揭示确认时间 |
| second_confirmation_at | DATETIME | 可空 | 二次确认时间 |
| recording_reference | VARCHAR(64) | 可空 | 双录编号 |
| ops_handler_id | BIGINT UNSIGNED | 可空 | 运营处理人 ID |
| ops_handled_at | DATETIME | 可空 | 运营处理时间 |
| compliance_handler_id | BIGINT UNSIGNED | 可空 | 合规处理人 ID |
| compliance_handled_at | DATETIME | 可空 | 合规处理时间 |
| reject_reason | TEXT | 可空 | 退回原因 |
| submitted_at | DATETIME | 索引、非空 | 委托提交时间 |
| cancelled_at | DATETIME | 可空 | 撤单时间 |
| created_at | DATETIME | 非空 | 创建时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
首版成交策略为:获取未过期实时行情 -> 适当性和风控校验 -> 检查可用资金/可用持仓 -> 市价委托立即全额模拟成交。表结构保留一对多成交能力,但首版不实现撮合队列和部分成交。
关键数据库约束:quantity > 0;市价委托的 limit_price IS NULL;限价委托的 limit_price > 0;0 <= filled_quantity <= quantity;买入前冻结预计成交金额和费用,卖出前冻结对应持仓数量。买入校验必须使用外部 API 最新且未过期的基金总份额,保证 (当前持仓数量 + 本次拟成交数量) / total_fund_shares * 100 <= single_investor_max_holding_ratio;基金总份额缺失、非正数或数据过期时不得接受买入委托。撤单只能发生在尚未成交的委托上,并须同时解冻资金或持仓。
fin_transaction 场内模拟成交表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 交易主键 |
| transaction_no | VARCHAR(64) | 唯一 | 交易编号 |
| order_id | BIGINT UNSIGNED | 索引 | 来源模拟委托;允许一笔委托对应多笔成交 |
| work_order_id | BIGINT UNSIGNED | 唯一、可空 | 当前 ORM 使用的来源业务工单 ID;与场内委托 order_id 分开 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, executed_at) |
客户用户 ID |
| account_id | BIGINT UNSIGNED | 索引 | 虚拟资金账户 ID |
| product_id | BIGINT UNSIGNED | 索引 | 产品 ID |
| order_side | VARCHAR(8) | 非空 | 买入、卖出 |
| transaction_type | VARCHAR(16) | 生成列、索引 | 兼容当前 ORM 的交易类型字段,值由 order_side 映射 |
| executed_price | DECIMAL(18,6) | 非空 | 模拟成交价格 |
| nav | DECIMAL(12,6) | 生成列 | 兼容当前 ORM 的净值字段,场内语义为成交价格,值等于 executed_price |
| executed_quantity | DECIMAL(18,4) | 非空 | 模拟成交数量 |
| shares | DECIMAL(18,4) | 生成列 | 兼容旧代码的成交份额字段,值等于 executed_quantity |
| gross_amount | DECIMAL(18,2) | 非空 | 成交价格乘成交数量 |
| amount | DECIMAL(18,2) | 生成列 | 兼容旧代码的成交金额字段,值等于 gross_amount |
| fee_rule_id | BIGINT UNSIGNED | 可空、索引 | 采用的费率规则 ID |
| fee_rate_snapshot | DECIMAL(10,6) | 非空 | 成交时费率快照 |
| fee_amount | DECIMAL(18,2) | 非空 | 实际费用 |
| fee | DECIMAL(18,2) | 生成列、可空 | 当前 ORM 使用的手续费兼容字段,值等于 fee_amount |
| net_amount | DECIMAL(18,2) | 非空 | 买入总扣款或卖出净回款 |
| quote_at | DATETIME | 非空 | 成交采用的行情时间 |
| quote_source | VARCHAR(32) | 非空 | 行情 API 来源 |
| executed_at | DATETIME | 索引、非空 | 模拟成交时间 |
| confirmed_at | DATETIME | 生成列、索引 | 兼容旧代码的确认时间字段,值等于 executed_at |
| confirmed_by | INT | 可空 | 当前 ORM 使用的确认人字段;自动确认时为空 |
| auto_confirmed | TINYINT(1) | 非空、默认 0 | 当前 ORM 使用的自动确认标记 |
| created_at | DATETIME | 非空 | 入库时间 |
成交、资金账和持仓更新必须在同一个数据库事务中完成。transaction_no 和资金明细的 idempotency_key 共同防止重复成交和重复扣款。
transaction_type、nav、shares、amount、fee、confirmed_at 均为只读生成列,只用于兼容现有查询和报表。新场内交易代码写入时必须使用 order_side、executed_price、executed_quantity、gross_amount、fee_amount、executed_at,不得对兼容列双写。现有风控代码只读取这些成交字段。
fin_holding 持仓汇总表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 记录主键 |
| customer_id | BIGINT UNSIGNED | 唯一键 (customer_id, product_id) |
客户用户 ID |
| trade_account | VARCHAR(32) | 索引 | 交易账号;与 fin_customer_profile.trade_account 对应,便于对接交易系统 |
| product_id | BIGINT UNSIGNED | 唯一键 (customer_id, product_id) |
产品 ID |
| total_quantity | DECIMAL(18,4) | 非空 | 当前总持仓数量 |
| shares | DECIMAL(18,4) | 生成列 | 兼容旧代码的持有份额字段,值等于 total_quantity |
| available_quantity | DECIMAL(18,4) | 非空 | 当前可卖数量 |
| frozen_quantity | DECIMAL(18,4) | 非空,默认 0 | 未完成卖出委托冻结数量 |
| average_cost | DECIMAL(18,6) | 非空 | 移动加权平均单位成本 |
| cost_amount | DECIMAL(18,2) | 非空 | 剩余持仓总成本 |
| market_value | DECIMAL(18,2) | 可空 | 按最新行情计算的持仓市值 |
| current_value | DECIMAL(18,2) | 生成列 | 兼容旧代码的当前市值字段,值等于 market_value |
| profit_loss | DECIMAL(18,2) | 可空 | 浮动盈亏,可由市值和成本重算 |
| profit_loss_ratio | DECIMAL(10,4) | 可空 | 浮动盈亏比例(%),可由盈亏与成本重算 |
| status | VARCHAR(16) | 索引、非空 | 持有中、已清仓 |
| first_acquired_at | DATETIME | 可空、索引 | 当前持仓周期首次买入时间;清仓后再次买入时重置 |
| version | BIGINT UNSIGNED | 非空,默认 0 | 乐观锁版本号 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
shares 和 current_value 是只读生成兼容列,新代码不得双写。fund_code 不在本表重复存储,直接使用 fin_product.product_code 作为基金代码;
market_value、profit_loss 和 profit_loss_ratio 是行情派生缓存。买入时按移动加权平均法更新 average_cost 和 cost_amount;卖出时按当前平均成本减少成本。场内模拟交易不再使用场外基金持有期和赎回费模型。
账户约束为 cash_balance = available_cash + frozen_cash;持仓约束为 total_quantity = available_quantity + frozen_quantity。所有金额、价格、数量、费用及余额字段均不得为负,冲正通过资金明细的有符号 amount 表达,不直接覆盖历史记录。
sys_login_record 登录记录表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 记录主键 |
| user_id | BIGINT UNSIGNED | 索引 (user_id, login_at) |
登录用户 ID |
| login_at | DATETIME | 索引 | 尝试登录时间 |
| login_result | VARCHAR(16) | 索引、非空 | 成功、失败、锁定 |
| ip_region | VARCHAR(64) | 可空 | IP 解析地区 |
| device_id | VARCHAR(128) | 可空 | 设备标识摘要 |
| is_common_device | TINYINT(1) | 非空,默认 1 | 是否为常用设备 |
| failure_reason | VARCHAR(128) | 可空 | 失败原因 |
| created_at | DATETIME | 非空 | 入库时间 |
6.3 风控与通知
biz_work_order 风险处置工单表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 工单主键 |
| work_order_no | VARCHAR(64) | 唯一、非空 | 工单业务编号 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, created_at) |
关联客户用户 ID |
| order_type | VARCHAR(16) | 可空 | 当前 ORM 使用的交易申请类型:申购、赎回;风险处置工单可为空 |
| product_id | BIGINT UNSIGNED | 可空、索引 | 当前 ORM 使用的关联产品 ID;非交易类工单可为空 |
| amount | DECIMAL(18,2) | 可空 | 当前 ORM 使用的申请金额;非交易类工单可为空 |
| channel | VARCHAR(32) | 可空 | 当前 ORM 使用的提交渠道 |
| advisor_id | BIGINT UNSIGNED | 可空、索引 | 当前 ORM 使用的销售或投顾人员 ID |
| risk_rule_hits | JSON | 可空 | 当前 ORM 使用的交易准入规则命中码 |
| risk_disclosure_ack_at | DATETIME | 可空 | 当前 ORM 使用的风险揭示确认时间 |
| second_confirmation_at | DATETIME | 可空 | 当前 ORM 使用的二次确认时间 |
| recording_reference | VARCHAR(64) | 可空 | 当前 ORM 使用的双录编号 |
| ops_handler_id | BIGINT UNSIGNED | 可空 | 当前 ORM 使用的运营处理人 ID |
| ops_handled_at | DATETIME | 可空 | 当前 ORM 使用的运营处理时间 |
| compliance_handler_id | BIGINT UNSIGNED | 可空 | 当前 ORM 使用的合规处理人 ID |
| compliance_handled_at | DATETIME | 可空 | 当前 ORM 使用的合规处理时间 |
| reject_reason | TEXT | 可空 | 当前 ORM 使用的退回原因 |
| alert_id | BIGINT UNSIGNED | 可空、索引 | 来源风险预警 ID;交易申请兼容记录可为空 |
| work_order_type | VARCHAR(32) | 可空、索引 | 风险调查、合规复核、可疑交易上报、客户回访;交易申请兼容记录可为空 |
| submitter_id | BIGINT UNSIGNED | 可空、索引 | 提交人员 ID |
| handler_id | BIGINT UNSIGNED | 可空、索引 | 当前处理人员 ID |
| priority | VARCHAR(8) | 可空、索引 | 低、中、高 |
| status | VARCHAR(24) | 索引、非空、默认“待风控扫描” | 同时容纳现有交易申请状态和风险处置状态 |
| request_detail | JSON | 可空 | 工单发起原因、证据引用和处理要求 |
| handle_result | JSON | 可空 | 结构化处理结果和结论 |
| submitted_at | DATETIME | 可空、索引 | 提交时间 |
| accepted_at | DATETIME | 可空 | 接单时间 |
| completed_at | DATETIME | 可空 | 完成时间 |
| created_at | DATETIME | 非空 | 创建时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
biz_work_order 保留原风险处置工单全部字段,并补充当前 ORM 读取的交易申请兼容字段。work_order_type 非空的记录表达预警后的人工流程;order_type 非空的记录表达现有代码中的交易申请留痕。它不替代 fin_sim_order 的场内委托和撮合职责。一个预警可以按不同处置目的生成多个工单;工单分配、状态变化和结论修改必须向 interaction_audit 追加记录。
fin_risk_alert 风险预警表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 预警主键 |
| alert_no | VARCHAR(64) | 唯一 | 预警业务编号 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, created_at) |
客户用户 ID |
| related_transaction_id | BIGINT UNSIGNED | 可空、索引 | 关联模拟成交;非成交类预警为空 |
| related_order_id | BIGINT UNSIGNED | 可空、索引 | 关联模拟委托;仅委托阶段预警使用 |
| related_work_order_id | BIGINT UNSIGNED | 可空、索引 | 当前 ORM 使用的关联交易申请工单 ID |
| primary_risk_work_order_id | BIGINT UNSIGNED | 可空、索引 | 当前主风险处置工单 ID;完整工单历史仍按 biz_work_order.alert_id 查询 |
| alert_type | VARCHAR(64) | 索引、非空 | 大额交易、适当性错配、账户行为异常等 |
| alert_level | VARCHAR(8) | 索引、非空 | 低、中、高 |
| trigger_rule_codes | JSON | 非空 | 命中规则编码数组 |
| evidence_summary | TEXT | 非空 | 人工可读的证据摘要 |
| evidence_snapshot | JSON | 非空 | 规则命中时的不可变证据快照 |
| priority_score | INT | 索引、非空 | 排序分数 |
| event_status | VARCHAR(16) | 非空 | 正在发生、刚刚发生、已完成、盘后预警 |
| status | VARCHAR(16) | 索引、非空 | 待处理、调查中、已排除、已结案 |
| ack_status | VARCHAR(16) | 非空 | 未确认、已确认 |
| ack_at | DATETIME | 可空 | 接收确认时间 |
| handler_id | BIGINT UNSIGNED | 可空、索引 | 当前风控处理人 ID |
| due_at | DATETIME | 可空、索引 | 处理截止时间 |
| is_escalated | TINYINT(1) | 非空,默认 0 | 是否已升级 |
| escalated_at | DATETIME | 可空 | 升级时间 |
| escalation_reason | TEXT | 可空 | 升级原因 |
| handle_result | TEXT | 可空 | 处置结论 |
| closed_at | DATETIME | 可空 | 排除或结案时间 |
| close_reason | TEXT | 可空 | 排除或结案原因 |
| ai_analysis | JSON | 可空 | 大模型辅助研判的结构化结果,不影响规则命中 |
| manual_remark | TEXT | 可空 | 人工备注 |
| created_at | DATETIME | 索引、非空 | 生成时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
预警状态流转为:待处理 -> 调查中 -> 已排除/已结案。is_escalated 可以在任一未结案状态设置,不作为状态终点。所有状态、确认、升级动作必须在 interaction_audit 追加审计记录。
related_order_id、related_work_order_id 和 primary_risk_work_order_id 语义不同:三者分别定位场内交易委托、当前代码中的交易申请工单和当前主风险处置工单。创建风险处置工单时先写 biz_work_order.alert_id,再按需更新 primary_risk_work_order_id;不得将三类编号混用。
fin_risk_notification 风控通知表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 通知主键 |
| notification_no | VARCHAR(64) | 唯一 | 通知业务编号 |
| alert_id | BIGINT UNSIGNED | 索引 | 关联预警 ID |
| channel | VARCHAR(16) | 索引、非空 | 站内提醒、邮件 |
| receiver_user_id | BIGINT UNSIGNED | 可空、索引 | 站内提醒的接收用户 |
| receiver_email | VARCHAR(128) | 可空 | 邮件收件人;演示阶段可使用固定邮箱 |
| title | VARCHAR(128) | 非空 | 通知标题 |
| content | TEXT | 非空 | 通知正文 |
| send_status | VARCHAR(16) | 索引、非空 | 待发送、已发送、发送失败 |
| sent_at | DATETIME | 可空 | 发送时间 |
| read_at | DATETIME | 可空 | 站内已读时间 |
| acknowledged_at | DATETIME | 可空 | 通知确认时间 |
| fail_reason | TEXT | 可空 | 失败原因 |
| created_at | DATETIME | 非空 | 创建时间 |
6.4 知识、记忆、审计与内容
记忆系统采用明确的三层架构:短期记忆使用 Redis;中期记忆使用 MySQL 持久化并用 Redis 做热缓存;长期语义记忆写入 Milvus,长期实体关系写入 Neo4j。MySQL 同时保存画像版本投影和跨库同步状态,用于精确查询、审计和故障恢复,但不代替 Milvus、Neo4j 的长期记忆职责。
6.4.1 数据权威性
记忆系统不得覆盖权威业务事实。发生冲突时按以下顺序取值:
正式风险测评、账户、成交、持仓等系统记录
> 可重复验证的交易行为特征
> 用户明确自述
> AI 从对话中提取或推断
> 系统默认值
fin_risk_assessment、fin_sim_account、fin_transaction、fin_holding 是权威数据源。C1-C5、资金余额、持仓数量、专业投资者状态等字段只能由对应业务流程修改。记忆可以保存“用户自述为C5”这类事实候选,但不能据此修改正式风险等级。
6.4.2 短期记忆:Redis
| Key 模式 | 类型 | TTL/容量 | 用途 |
|---|---|---|---|
mem:stm:{customer_id}:{session_id} |
List | 滑动 TTL 30 分钟;最多20轮并受Token预算限制 | 当前会话消息和工具结果 |
mem:session:{session_id} |
Hash | 滑动 TTL 30 分钟 | 当前意图、端类型、最近摘要、消息序号 |
mem:mid:hot:{customer_id} |
ZSet + Hash | 滑动 TTL 6 小时 | 当前用户高频中期记忆,按召回分排序 |
mem:profile:{customer_id} |
Hash | TTL 30 分钟 | 最新画像投影缓存,值中必须包含画像版本 |
mem:lock:{customer_id} |
String | 30 秒 | 防止同一客户画像被并发更新 |
Redis 只保存可重建的数据。会话结束、达到Token阈值或用户明确转人工时,系统生成 episodes 记录并触发中期记忆提取;Redis丢失不得造成MySQL权威数据或长期记忆损坏。
6.4.3 中期记忆:MySQL + Redis
episodes 会话片段表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 会话片段主键 |
| episode_uuid | CHAR(36) | 唯一、可空 | 跨存储稳定标识;当前 ORM 写入时可暂为空 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, ended_at) |
客户用户 ID |
| session_id | VARCHAR(64) | 索引 | 会话 ID |
| start_message_no | INT | 可空 | 起始消息序号;当前 ORM 写入时可暂为空 |
| end_message_no | INT | 可空 | 结束消息序号;当前 ORM 写入时可暂为空 |
| portals_involved | JSON | 非空 | 涉及的端 |
| summary | TEXT | 非空 | 脱敏后的会话摘要 |
| extraction_status | VARCHAR(16) | 索引、非空、默认“待提取” | 待提取、处理中、已完成、失败 |
| content_hash | CHAR(64) | 唯一、可空 | 防止同一片段重复提取;当前 ORM 写入时可暂为空 |
| retry_count | INT | 非空,默认 0 | 提取重试次数 |
| started_at | DATETIME | 可空 | 片段开始时间;可由兼容字段 start_at 回填 |
| ended_at | DATETIME | 可空 | 片段结束时间;可由兼容字段 end_at 回填 |
| handoff_to_employee_id | BIGINT UNSIGNED | 可空 | 当前 ORM 使用的转人工员工 ID |
| start_at | DATETIME | 可空 | 当前 ORM 使用的片段开始时间兼容字段 |
| end_at | DATETIME | 可空 | 当前 ORM 使用的片段结束时间兼容字段 |
| promoted_to_ltm | TINYINT(1) | 非空、默认 0 | 当前 ORM 使用的长期记忆晋升标记 |
| created_at | DATETIME | 非空 | 入库时间 |
memory_unit 中期记忆单元表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | MySQL 主键 |
| memory_uuid | CHAR(36) | 唯一、非空 | MySQL、Milvus、Neo4j通用标识 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, status, confidence) |
客户用户 ID |
| memory_key | VARCHAR(128) | 索引 (customer_id, memory_key) |
规范化键,如 preference.asset_class |
| content | TEXT | 非空 | 脱敏后的自然语言内容 |
| structured_value | JSON | 可空 | 枚举、区间或结构化值 |
| memory_type | VARCHAR(24) | 索引、非空 | 事实候选、观点、偏好、目标、事件摘要 |
| source_type | VARCHAR(24) | 索引、非空 | 系统记录、行为推断、用户自述、AI对话提取、系统默认 |
| source_confidence | DECIMAL(5,4) | 非空 | 来源基础置信度 |
| confidence | DECIMAL(5,4) | 索引、非空 | 当前综合置信度,范围0至1 |
| evidence_count | INT | 非空,默认 0 | 有效独立证据数量 |
| conflict_count | INT | 非空,默认 0 | 未解决冲突数量 |
| recall_count | INT | 非空,默认 0 | 被召回次数 |
| status | VARCHAR(16) | 索引、非空 | 候选、有效、降权、已归档、已失效 |
| valid_from | DATETIME | 非空 | 开始有效时间 |
| valid_until | DATETIME | 可空、索引 | 失效或重新验证时间 |
| last_evidenced_at | DATETIME | 可空 | 最近获得证据时间 |
| last_recall_at | DATETIME | 可空 | 最近召回时间 |
| promoted_at | DATETIME | 可空 | 晋升长期记忆时间 |
| version | BIGINT UNSIGNED | 非空,默认 1 | 乐观锁和跨库同步版本 |
| created_at | DATETIME | 非空 | 创建时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
同一 customer_id + memory_key 可以存在历史版本和待验证候选,但只能有一条当前“有效”记录。实现时增加生成列 active_memory_key:仅当状态为有效时返回 memory_key,否则返回 NULL,并建立唯一键 (customer_id, active_memory_key)。新候选与旧有效记录可以并存,完成冲突处理后再以事务切换有效版本。
memory_evidence 记忆证据表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 证据主键 |
| memory_id | BIGINT UNSIGNED | 索引 (memory_id, occurred_at) |
关联记忆单元 |
| evidence_type | VARCHAR(24) | 非空 | 风评、成交、持仓、对话、人工确认、行为特征 |
| source_table | VARCHAR(64) | 可空 | 来源业务表名 |
| source_record_id | VARCHAR(64) | 可空 | 来源记录 ID |
| source_episode_id | BIGINT UNSIGNED | 可空、索引 | 来源会话片段 |
| evidence_excerpt | TEXT | 可空 | 脱敏后的证据摘录 |
| evidence_snapshot | JSON | 可空 | 生成结论时采用的不可变特征快照 |
| weight | DECIMAL(5,4) | 非空 | 证据权重,范围0至1 |
| idempotency_key | VARCHAR(128) | 唯一、非空 | 防止重复计入同一证据 |
| occurred_at | DATETIME | 非空 | 证据发生时间 |
| created_at | DATETIME | 非空 | 入库时间 |
memory_conflict 记忆冲突表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 冲突主键 |
| left_memory_id | BIGINT UNSIGNED | 索引 | 冲突记忆一 |
| right_memory_id | BIGINT UNSIGNED | 索引 | 冲突记忆二 |
| conflict_type | VARCHAR(24) | 非空 | 值冲突、时间变化、来源冲突 |
| severity | VARCHAR(8) | 非空 | 低、中、高 |
| status | VARCHAR(16) | 索引、非空 | 待解决、自动解决、人工解决、已忽略 |
| resolution | TEXT | 可空 | 解决结论及理由 |
| winner_memory_id | BIGINT UNSIGNED | 可空 | 采用的记忆;时间变化可不设胜者 |
| resolved_by | BIGINT UNSIGNED | 可空 | 人工解决人 |
| resolved_at | DATETIME | 可空 | 解决时间 |
| created_at | DATETIME | 非空 | 创建时间 |
中期记忆写入MySQL后删除或刷新 mem:mid:hot:{customer_id},下一次读取再回填热缓存。缓存值必须携带 memory_uuid 和 version,旧版本不得覆盖新版本。
6.4.4 长期记忆:Milvus
Milvus集合使用 user_long_term_memory_v1,不按客户创建独立集合或分区。检索必须使用 customer_id、status=active 和有效期进行标量过滤,防止跨客户召回。
| 字段 | Milvus 类型 | 说明 |
|---|---|---|
| memory_uuid | VARCHAR,主键 | 与MySQL记忆单元一致 |
| customer_id | INT64 | 客户 ID,强制过滤字段 |
| content | VARCHAR | 脱敏后的长期记忆文本 |
| embedding | FLOAT_VECTOR | 文本向量,维度由选定Embedding模型固定 |
| memory_type | VARCHAR | 偏好、目标、稳定事实候选、事件摘要 |
| memory_key | VARCHAR | 规范化记忆键 |
| confidence | FLOAT | 同步时的置信度 |
| version | INT64 | 同步版本,只允许新版本覆盖旧版本 |
| status | VARCHAR | active、archived、deleted |
| valid_until_ts | INT64 | 可空有效截止时间戳 |
| updated_at_ts | INT64 | 更新时间戳 |
Milvus不存身份证号、手机号、银行卡号、精确账户余额或完整原始对话。长期记忆被撤销或删除时先写 deleted 墓碑事件,再由后台维护任务物理清理向量。
6.4.5 长期记忆:Neo4j
Neo4j保存关系型长期认知,不作为资金、持仓或风险等级的权威数据库。
| 节点 | 关键属性 | 说明 |
|---|---|---|
Customer |
customer_id、profile_version、updated_at | 客户匿名业务标识,不存敏感身份信息 |
Preference |
key、value、memory_uuid、version | 稳定偏好 |
Goal |
key、value、memory_uuid、version | 养老、教育、流动性等目标 |
Product |
product_id、product_code、risk_level | 产品投影,权威值仍来自MySQL |
Category |
code、name | 资产或产品类别 |
Industry |
code、name | 行业实体 |
RiskEvent |
alert_id、alert_type、level | 风险事件投影 |
主要关系为 PREFERS、HAS_GOAL、INTERESTED_IN、TRADED、HOLDS、TRIGGERED_RISK、BELONGS_TO_CATEGORY、EXPOSED_TO_INDUSTRY。由记忆产生的关系必须带 memory_uuid、confidence、version、valid_from、valid_until;由交易和持仓产生的关系带来源业务记录 ID。GraphRAG默认限制在1至2跳,并在返回结果时保留关系来源。
6.4.6 画像版本与多存储同步
profile_snapshots 长期画像版本投影表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 快照主键 |
| profile_uuid | CHAR(36) | 唯一、可空 | 当前画像版本的跨存储稳定标识;当前 ORM 写入时可暂为空 |
| customer_id | BIGINT UNSIGNED | 索引 (customer_id, version) |
客户用户 ID |
| version | BIGINT UNSIGNED | 联合唯一键 (customer_id, version) |
画像版本 |
| snapshot | JSON | 非空 | 当前画像的结构化投影 |
| generation_basis | JSON | 可空 | 使用的测评版本、交易窗口和记忆版本列表 |
| snapshot_hash | CHAR(64) | 可空 | 内容校验及幂等判断 |
| is_current | TINYINT(1) | 非空,默认 0 | 是否为当前画像 |
| current_customer_id | BIGINT UNSIGNED | 生成列、唯一、可空 | is_current=1时等于客户ID,否则为NULL,保证每客户只有一个当前版本 |
| generated_at | DATETIME | 可空 | 画像生成时间 |
| created_at | DATETIME | 非空、默认 CURRENT_TIMESTAMP | 入库时间 |
| updated_at | DATETIME | 非空 | 当前 ORM 使用的最后更新时间 |
画像生成只允许读取已提交的业务事实和状态为“有效”的中期记忆。新画像和两条同步事件必须在同一个MySQL事务中写入,旧画像的 is_current 同时置为0。
memory_sync_outbox 长期记忆同步事件表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 事件主键 |
| event_uuid | CHAR(36) | 非空 | 业务事件标识 |
| aggregate_type | VARCHAR(24) | 非空 | memory、profile、relationship、deletion |
| aggregate_uuid | CHAR(36) | 非空 | 记忆或画像稳定标识 |
| aggregate_version | BIGINT UNSIGNED | 非空 | 同步版本 |
| target_store | VARCHAR(16) | 非空 | MILVUS 或 NEO4J |
| operation | VARCHAR(16) | 非空 | UPSERT、ARCHIVE、DELETE |
| payload | JSON | 非空 | 已脱敏的同步载荷 |
| status | VARCHAR(16) | 索引、非空 | 待处理、处理中、已完成、失败 |
| retry_count | INT | 非空,默认 0 | 已重试次数 |
| next_retry_at | DATETIME | 可空、索引 | 下次重试时间 |
| last_error | TEXT | 可空 | 最近错误摘要 |
| created_at | DATETIME | 非空 | 创建时间 |
| processed_at | DATETIME | 可空 | 完成时间 |
唯一键为 (event_uuid, target_store)。同步消费者按 aggregate_uuid + aggregate_version 幂等写入;目标存储中的版本大于等于事件版本时直接确认成功。失败采用指数退避,超过5次进入失败状态并告警,不允许静默丢弃。
6.4.7 提取、晋升、更新、遗忘与召回
完整处理链路:
Redis短期会话
-> episodes会话片段
-> LLM提取事实候选/观点/偏好/目标
-> 结构校验、脱敏、去重
-> memory_unit + memory_evidence
-> 冲突检测与置信度计算
-> 结合风评、成交、持仓重新生成画像版本
-> 同事务写profile_snapshots和memory_sync_outbox
-> 异步幂等同步Milvus和Neo4j
-> 清理Redis中期与画像缓存
来源基础置信度建议值:正式系统记录0.95、行为推断0.80、用户明确自述0.65、AI对话提取0.45、系统默认0.20。每条独立证据最多增加0.05,累计增益上限0.20;每条未解决中冲突扣0.10、高冲突扣0.25,最终钳制在0至1。
晋升长期记忆满足以下任一条件:
- 来源为已验证系统记录,且适合以语义或关系形式长期使用;
- 综合置信度不低于0.75,至少有2条独立证据,并且没有未解决的高冲突;
- 用户明确要求长期记住,完成敏感信息检查后标记为用户确认记忆。
事实型业务记录本身不做时间衰减;偏好半衰期180天、目标半衰期365天、临时关注半衰期30天。置信度低于0.45进入降权,低于0.30且超过观察期进入归档。归档不是物理删除,监管要求保留的数据不得被遗忘任务删除。
召回顺序为:Redis当前会话 -> Redis/MySQL有效中期记忆 -> Milvus语义TopK -> Neo4j一至二跳关系 -> MySQL权威事实校验。综合排序建议使用“语义相关度45% + 置信度25% + 时效性15% + 场景权重15%”,最终默认返回5条,并限制记忆上下文不超过模型输入Token预算的30%。任何与MySQL权威事实冲突的召回结果必须被过滤或显式标注为历史观点。
6.4.8 对话归档、知识、审计与内容
conversation_message 对话留存表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 消息主键 |
| session_id | VARCHAR(64) | 唯一键 (session_id, message_no), 索引 |
会话 ID |
| message_no | INT | 可空、唯一键 (session_id, message_no) |
消息序号;当前 Agent 写入时可暂为空 |
| customer_id | BIGINT UNSIGNED | 可空、索引 (customer_id, created_at) |
客户用户 ID;当前 Agent 写入时可暂为空 |
| portal | VARCHAR(32) | 非空 | 客户端类型 |
| role | VARCHAR(16) | 非空 | 角色:user/assistant |
| content | MEDIUMTEXT | 非空 | 消息内容 |
| tool_calls | JSON | 可空 | LLM 工具调用记录 |
| trace_id | VARCHAR(64) | 可空、索引 | 全链路追踪 ID |
| intent | VARCHAR(32) | 可空、索引 | 🆕 本轮识别的意图:faq/product_inquiry/policy_explain/chitchat 等 |
| confidence | DECIMAL(5,4) | 可空、索引 | 🆕 置信度,范围 0~1 |
| source_references | JSON | 可空 | 🆕 检索来源,格式 [{collection: "fin_faq_collection", doc_id: "FAQ-001"}, ...] |
| created_at | DATETIME | 非空、索引 (session_id, created_at) |
创建时间 |
说明:完整对话合规归档,当前 Agent 写入时不提供消息序号和链路编号,因此两字段必须可空。三个新增字段 intent/confidence/source_references 用于支撑审计日志完整性和度量统计。
fin_knowledge_meta 知识库元数据表
| 字段 | 类型 | 约束/索引 | 说明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | 主键 | 元数据主键 |
| knowledge_type | VARCHAR(32) | 索引、非空 | 知识类型 |
| title | VARCHAR(256) | 非空 | 知识标题 |
| source_file | VARCHAR(256) | 可空 | 原始文件路径 |
| minio_path | VARCHAR(512) | 可空 | MinIO 存储路径 |
| milvus_collection | VARCHAR(64) | 非空 | Milvus 集合名 |
| version | VARCHAR(16) | 可空 | 版本号,如 v1.0 |
| effective_date | DATE | 可空 | 🆕 生效日期 |
| expire_date | DATE | 可空 | 🆕 失效日期,null 表示长期有效 |
| content_text | MEDIUMTEXT | 非空 | 🆕 Milvus 知识正文的 MySQL 镜像,用于故障时 LIKE 降级检索 |
| tags | JSON | 可空 | 🆕 知识标签,如 ["faq","redeem"] |
| reviewer_id | BIGINT UNSIGNED | 可空 | 🆕 审校人 ID,关联 sys_user.id |
| review_status | VARCHAR(16) | 非空、默认 'pending' | 🆕 审校状态:pending(待审)/approved(已审)/published(已发布)/archived(已归档) |
| status | VARCHAR(16) | 索引、非空 | 当前状态 |
| created_at | DATETIME | 非空 | 创建时间 |
| updated_at | DATETIME | 非空 | 最后更新时间 |
说明:公司知识库元数据,与用户长期记忆使用不同 Milvus 集合。新增的 7 个字段支撑知识运营(过期自动下线、版本管理、审校流程)和 Milvus 故障时的 MySQL 降级检索。
| interaction_audit | id、actor_type、actor_id、target_customer_id、session_id、portal、action_type、detail(JSON)、created_at | 索引 (actor_type, actor_id, created_at)、(target_customer_id, created_at) | 全系统操作审计;仅追加,不更新 |
| client_facing_content | id、customer_id、content_type、draft_content(JSON)、generated_by_portal、review_status、reviewer_user_id、reviewed_at、published_at、created_at、updated_at | 索引 (customer_id, review_status) | 客户可见内容审核状态机;补齐当前 ORM 的最后更新时间字段 |
用户请求删除记忆时,先在MySQL将对应记忆标记为已失效并写入两个目标存储的删除事件,再清理Redis缓存;Milvus和Neo4j完成删除后记录审计结果。依法必须保留的交易、审计和对话归档不随记忆删除,但应停止用于个性化召回,并向用户说明法定保留边界。
7. 原表到合并表映射
| 原风控表 | 合并后去向 | 处理 |
|---|---|---|
risk_customers |
sys_user + fin_customer_profile + fin_risk_assessment |
拆分为身份、客户画像、风险测评三类权威数据 |
risk_products |
fin_product |
合并,补入三项合规要求字段 |
risk_transactions |
fin_sim_order + fin_transaction |
拆分为场内模拟委托和模拟成交 |
risk_capital_flows |
fin_capital_flow 兼容表 + fin_cash_ledger |
保留现有规则扫描字段,并可同步可映射的虚拟初始化和交易资金记录 |
risk_holdings |
fin_holding |
合并为场内持仓汇总,使用移动加权平均成本 |
risk_login_records |
sys_login_record |
逐次登录记录替代累计失败数 |
risk_alerts |
fin_risk_alert |
合并保留证据快照、优先级、确认、升级、人工与 AI 辅助字段 |
risk_notifications |
fin_risk_notification |
更名,补入站内接收用户 |
biz_work_order |
biz_work_order |
保留为独立风险处置工单,不再由 fin_sim_order 替代 |
mock_bank_transfer |
不迁移 | 删除银行流水匹配语义,不建立充值或提现接口 |
8. 对现有代码的影响
当前 app/model/entities.py 已定义 20 张统一业务实体,不再包含独立 risk_* 演示表。本兼容版保留原设计全部字段,并补齐当前 ORM 的表名和字段;真正落库前仍需在同一迁移版本中完成以下事项:
- 先按当前 ORM 核对
sys_user、客户、产品、净值、工单、交易、资金、持仓、登录、预警、通知、知识、会话、审计和内容表;RBAC、模拟交易与分层记忆表作为后续模块保留。 - 保持预警和通知 ORM 使用
fin_risk_alert、fin_risk_notification;分别使用related_order_id、related_work_order_id与primary_risk_work_order_id,禁止混用场内委托、交易申请工单和风险处置工单。 - 当前规则引擎继续从
fin_capital_flow、成交、持仓、产品、交易申请工单和登录记录读取证据;资金域负责同步该兼容表,风控不得修改资金事实。 - 保持
app/api/deps.py为唯一鉴权入口,通过 RBAC 权限和data_scope做服务端强制过滤,风控服务层不自行解析令牌。 - 对历史演示数据编写一次性导入脚本,绝不通过删表重建覆盖已有环境。
- 新增记忆编排服务,负责会话切片、提取、去重、证据登记、冲突检测和置信度计算;Agent不得直接写Milvus或Neo4j。
- 新增画像生成服务,只读取已提交的权威业务事实和有效中期记忆,并在一个MySQL事务中写画像版本和Outbox事件。
- 新增两个独立的Outbox消费者,分别负责Milvus和Neo4j;两者使用相同事件版本规则,但失败和重试状态互不影响。
- 记忆召回服务统一执行客户隔离、有效期过滤、综合排序、MySQL权威校验和Token预算裁剪,具体Agent不得自行拼接跨客户记忆。
- 当前查询可读取交易和持仓生成列以及
fin_capital_flow兼容表;所有新增场内交易写入必须使用权威字段,风控会话继续写入conversation_message和interaction_audit。
9. 迁移与回滚建议
风险等级:HIGH。原因是本设计会改变主键类型、表归属和预警状态枚举,且涉及账户、交易、资金与审计数据。
升级顺序:
- 创建身份、RBAC、客户、交易、工单、风控和记忆 MySQL 表及必要索引,包括
fin_nav_history、fin_capital_flow和user_facts;不删除任何现有表或字段。 - 先导入
sys_user与fin_customer_profile,生成旧客户号到sys_user.id的映射表。 - 导入场内产品并同步日线行情;为测试客户初始化虚拟资金账户,再迁移可转换的委托、成交、持仓、登录、预警和通知数据。
- 部署Redis记忆Key规范和TTL配置,创建Milvus
user_long_term_memory_v1集合及标量索引,在Neo4j建立客户、偏好、目标、产品、类别、行业和风险事件的唯一约束。 - 将已有对话按会话切分为
episodes,提取中期记忆;先只写MySQL并进行人工抽样检查,不立即生成长期画像。 - 抽样通过后生成首版
profile_snapshots和memory_sync_outbox,启动Milvus、Neo4j同步消费者并核对每个目标存储的版本与数量。 - 对记录数、虚拟现金余额、资金明细汇总、成交金额、持仓数量、当前画像唯一性、记忆同步积压、每客户最新风险等级、预警状态和通知数进行迁移前后核对。
- 切换应用至新 ORM 和记忆召回流程后运行观察期;观察期结束且核对无误后,再单独评审旧表归档计划,不直接删除。
回滚方案:保留旧表、旧 ORM 版本和映射记录。切换出现问题时,应用回退至旧 ORM,已创建的新表保留以便排查;迁移期间不允许风控模块写入交易与资金表,避免双写不一致。
记忆系统单独回滚时,停止Outbox消费者并关闭长期记忆召回,继续使用MySQL权威业务数据。Milvus和Neo4j中的新版本数据不立即删除,待核对 memory_sync_outbox 后再处理;Redis缓存可直接清空并从MySQL重建。
10. 评审前需确认的事项
sys_user是否由主系统提供,且其id能被所有业务域作为统一外键使用。- 外部行情 API 的供应方、调用频率、交易时段和行情过期阈值;超过阈值时必须拒绝模拟成交。
- 虚拟账户初始资金的统一额度,以及是否允许管理员重置测试账户;无论如何均不开放充值、提现接口。
fin_customer_profile.total_asset的来源与更新频率。它影响大额阈值和持仓占比,不能由风控模块自行维护。- 登录日志是否由统一认证系统提供。若无法提供,账户行为异常规则不能在合并后继续运行。
- 预警的“上报”是否只是人工记录,还是要对接外部反洗钱报送系统;后者需要独立报送表和接口,不应直接塞进预警状态。
- 是否需要保留完整客户归属历史。当前设计只保证当前投顾/运营归属的唯一性。
- 记忆提取使用的LLM与Embedding模型版本。模型变更时必须创建新的Milvus集合版本并重建向量,不在原集合中混用不同维度或模型。
- 原始对话的合规保留期限和用户删除请求流程;这决定
conversation_message的归档周期,但不改变短期Redis的30分钟TTL。
11. 验证清单
| 场景 | 类型 | 操作 | 预期结果 | 验证方式 |
|---|---|---|---|---|
| 用户主数据唯一性 | 数据库集成 | 为同一账号重复写入用户号 | 被唯一约束拒绝 | 执行迁移测试 |
| 行情过期保护 | 交易集成 | 使用超过约定时限的行情提交市价委托 | 拒绝成交且不改变资金和持仓 | 交易集成测试 |
| 买入原子性 | 数据库集成 | 对有效市价委托执行模拟成交 | 成交、扣款、资金明细和持仓在同一事务成功或回滚 | 数据库集成测试 |
| 卖出可用量 | 交易单元 | 卖出数量大于可用持仓 | 拒绝委托且不产生资金明细 | 交易单元测试 |
| 幂等成交 | 数据库集成 | 使用同一成交编号重复执行 | 第二次写入被唯一约束拒绝,余额不重复扣减 | 数据库集成测试 |
| 费率分层 | 交易单元 | 不同客户层级执行同金额买卖 | 命中正确费率规则并固化费用快照 | 交易单元测试 |
| RBAC数据范围 | 接口集成 | 投顾查询非归属客户 | own_customers 范围校验拒绝访问 |
鉴权集成测试 |
| 登录失败聚合 | 规则单元 | 同一用户短时间写入多条失败登录 | 正确按时间窗统计 | 规则单元测试 |
| 预警状态 | 服务集成 | 待处理预警依次确认、调查、结案 | 合法流转成功,非法跳转失败 | 服务测试 |
| 预警升级 | 服务集成 | 对调查中预警标记升级 | 状态不变,升级字段和审计记录更新 | 服务测试 |
| 预警工单关联 | 服务集成 | 从同一预警创建调查和复核工单 | 两张工单均关联该预警,主工单指针不与交易委托 ID 混用 | 服务测试 |
| 旧字段兼容 | 数据库集成 | 使用旧字段查询成交和持仓 | 生成列返回值与权威字段完全一致,且禁止向生成列写入 | 兼容性测试 |
| 资金兼容表 | 数据库集成 | 使用当前 ORM 查询 fin_capital_flow |
可读取 flow_no/status/settled_at/source_type/match_status 等现有规则字段 |
数据库集成测试 |
| 当前 ORM 覆盖 | 静态核对 | 对比 app/model/entities.py 与本文表名、字段 |
20 个实体的表名和全部字段均存在于设计中 | 自动字段清单核对 |
| 迁移一致性 | 数据库集成 | 导入历史演示数据 | 记录数、金额、最新风险等级一致 | 迁移核对脚本 |
| 越权访问 | 接口集成 | 非风控角色访问预警接口 | 后端拒绝请求 | 鉴权集成测试 |
| 短期记忆过期 | 缓存单元 | 会话30分钟无活动后继续提问 | 原Redis上下文过期,新会话不误用旧上下文 | Redis集成测试 |
| Episode幂等 | 数据库集成 | 对同一消息范围重复触发摘要 | content_hash唯一约束阻止重复提取 |
数据库集成测试 |
| 权威事实优先 | 记忆单元 | 用户自述C5但正式风评为C2 | 可保留自述候选,当前画像仍为C2 | 画像规则单元测试 |
| 记忆晋升 | 记忆单元 | 置信度0.75、两条独立证据且无高冲突 | 记忆晋升并生成两个目标存储同步事件 | 记忆服务测试 |
| 冲突切换 | 数据库集成 | 新候选与旧有效偏好发生冲突 | 两者并存待处理,解决后仅一条保持有效 | 数据库集成测试 |
| 当前画像唯一 | 数据库集成 | 并发生成同一客户的新画像 | 生成列唯一约束保证只有一个当前版本 | 并发集成测试 |
| 跨库部分失败 | 同步集成 | Milvus成功而Neo4j写入失败 | Neo4j事件独立重试,Milvus不重复降级写入 | Outbox集成测试 |
| 版本幂等 | 同步单元 | 先到达版本3,再收到版本2 | 目标存储保留版本3并确认旧事件 | 同步单元测试 |
| 跨客户隔离 | 安全集成 | 用客户A查询向量但构造客户B相关文本 | Milvus标量过滤与服务端权限均不返回客户B记忆 | 安全集成测试 |
| Redis丢失恢复 | 故障集成 | 清空全部记忆缓存后查询客户画像 | 从MySQL当前画像和中期记忆恢复,业务事实不丢失 | 故障恢复测试 |
| 记忆删除传播 | 同步集成 | 用户删除一条可删除的偏好记忆 | MySQL失效、缓存清除、Milvus/Neo4j收到墓碑并完成审计 | 隐私流程测试 |
🆕 附录:智能客服Agent相关修改说明
A. 修改概述
本次修改是为支撑智能客服Agent业务流程的五大场景(闲聊、政策问答、产品咨询、高频问答、转人工)而进行的数据库优化。
修改原则:
- ✅ 只补充字段,不修改现有字段的定义或约束
- ✅ 不创建新表的映射或关联,新增表独立运作
- ✅ 保持兼容性,所有修改都是向后兼容的
B. 现有表的字段增补
conversation_message 表补充 3 个字段
背景:智能客服需要记录完整的对话审计链路,包括意图识别结果、置信度和知识来源。
新增字段:
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| intent | VARCHAR(32) | 可空, INDEX | 识别出的意图:faq/product_inquiry/policy_explain/chitchat 等;用户消息的 intent 为空 |
| confidence | DECIMAL(5,4) | 可空, INDEX | 意图识别的置信度,范围 0~1;用户消息的 confidence 为空 |
| source_references | JSON | 可空 | 检索来源列表,格式 [{collection: "fin_faq_collection", doc_id: "FAQ-001"}, ...];直接 LLM 生成的回复此字段为空 |
使用场景:
- 闲聊:intent=chitchat, source_references=null
- 政策问答:intent=policy_explain, source_references 包含 fin_policy_collection
- 产品咨询:intent=product_inquiry, source_references 包含 fin_product_collection
- 高频问答:intent=faq, source_references 包含 fin_faq_collection
- 转人工:记录各流程的识别结果
索引建议:INDEX idx_intent (intent), INDEX idx_confidence (confidence)
fin_knowledge_meta 表补充 7 个字段
背景:知识库运营需要版本管理、过期自动下线、审校流程,以及 Milvus 故障时的 MySQL 降级检索。
新增字段:
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| content_text | MEDIUMTEXT | 非空 | Milvus 知识正文的 MySQL 镜像,用于 Milvus 故障时的 LIKE 降级检索 |
| tags | JSON | 可空 | 知识标签数组,如 ["faq","redeem"],用于查询分类和过滤 |
| effective_date | DATE | 可空 | 知识生效日期,用于自动下线过期内容 |
| expire_date | DATE | 可空 | 知识失效日期;null 表示长期有效 |
| version | VARCHAR(16) | 可空 | 版本号,如 v1.0,用于版本管理 |
| reviewer_id | BIGINT UNSIGNED | 可空 | 审校人 ID,关联 sys_user.id,用于审校流程溯源 |
| review_status | VARCHAR(16) | 非空, 默认 'pending' | 审校状态:pending(待审)/approved(已审)/published(已发布)/archived(已归档) |
使用场景:
- 政策问答:读取 reviewer_id/review_status 了解审校状态
- 产品咨询:读取 effective_date/expire_date 进行时间窗过滤
- 高频问答:读取 tags 做分类检索
- FAQ 沉淀:新创建的 FAQ 条目写入这些字段(review_status='pending' 等待审校)
索引建议:
INDEX idx_effective_date (effective_date)
INDEX idx_expire_date (expire_date)
INDEX idx_review_status (review_status)
INDEX idx_reviewer (reviewer_id)
C. 新增表单独文档
所有新增的表(agent_negative_word、svc_handover_ticket、agent_faq_synonym、conversation_feedback 等)已单独整理成 《智能客服Agent新增表详细设计.md》 文档,包含:
- agent_negative_word 表 — 负面词清单配置表
- svc_handover_ticket 表 — 客服转人工工单表(独立于风控的 biz_work_order)
- agent_faq_synonym 表 — FAQ 同义问法库
- conversation_feedback 表 — 用户反馈表(可选)
- agent_reply_template 表 — 回复模板配置表(Phase 2)
- agent_intent_config 表 — 意图路由配置表(Phase 2)
详见单独的新增表文档。
D. 迁移脚本生成顺序
建议按以下顺序执行 SQL 迁移脚本:
Week 1-2(必执行)
-- 第1步:fin_knowledge_meta 增补 7 个字段(优先!其他依赖它)
ALTER TABLE fin_knowledge_meta ADD COLUMN (
content_text MEDIUMTEXT NOT NULL COMMENT 'Milvus知识正文MySQL镜像,用于降级检索',
tags JSON NULL COMMENT '知识标签',
effective_date DATE NULL COMMENT '生效日期',
expire_date DATE NULL COMMENT '失效日期',
version VARCHAR(16) NULL COMMENT '版本号',
reviewer_id BIGINT UNSIGNED NULL COMMENT '审校人ID',
review_status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT '待审/已审/已发布/已归档'
);
-- 第2步:conversation_message 增补 3 个字段
ALTER TABLE conversation_message ADD COLUMN (
intent VARCHAR(32) NULL COMMENT '识别的意图',
confidence DECIMAL(5,4) NULL COMMENT '置信度',
source_references JSON NULL COMMENT '检索来源'
);
-- 第3步:新建 agent_negative_word 表
-- 第4步:新建 svc_handover_ticket 表
-- (详见新增表文档)
Week 3-4(建议执行)
-- 第5步:新建 agent_faq_synonym 表
-- 第6步:新建 conversation_feedback 表
-- (详见新增表文档)
E. 验证清单
完成修改后,请按以下清单验证:
conversation_message新增的 3 个字段可正常读写fin_knowledge_meta新增的 7 个字段可正常读写- 政策问答流程:MySQL LIKE on content_text 的降级检索工作正常
- 高频问答流程:expire_date 过滤正确删除过期 FAQ
- 转人工流程:svc_handover_ticket 工单创建、接单、处理完整链路通畅
- 所有新增表的索引已正确建立
F. 修改统计
现有表修改:2 张
conversation_message:+3 字段fin_knowledge_meta:+7 字段
新增表:见《智能客服Agent新增表详细设计.md》
- 第一优先级(Week 1-2):2 张
- 第二优先级(Week 3-4):2 张
- 第三优先级(Phase 2):2 张
总表数增长:33 张 → 39 张(+6 张新增)
文档更新日期:2026-09-08 版本:v1.4(添加智能客服 Agent 支持)