feat: 迭代03-05 交易记录接入、行情实时、QMT连接配置、data store 标准化

- R-004 QMT连接配置:多配置管理 + 会话头部快捷切换(03-QMT连接配置)
- R-005 WS盘中价格实时更新:服务端中转+缓存+前端轮询(04-WS盘中价格实时更新)
- R-006 策略数据 JSON data store 标准化(store.json/market.json/schema.json)
- R-007 交易记录接入:当日订单+成交单表合并(委托主行+展开成交明细)、时间段查询(今日/本周/本月+手动起止)、费用计算(佣金费率万m_dCommission+最低5元、印花税卖出万5、过户费双向万0.1)
- 架构治理:api 按领域拆分、component 目录、QmtHealthMonitor
This commit is contained in:
2026-09-01 13:21:49 +08:00
parent 5a723d514e
commit bb3f8bd28d
57 changed files with 5244 additions and 397 deletions
@@ -0,0 +1,55 @@
# 迭代 03:技术实现方案
> 依据:PLAN-003、技术约束-003/008/009、产品约束-005/006、UI约束-001/002
## 数据模型(settings namespace: one-divine-lot 新增字段 qmtConnections
```
qmtConnections: {
list: [{ id, name, baseUrl, order }], // order 决定列表展示顺序
activeId: string, // 当前激活配置 id'' = 无,展示层回退 defaultId/第一条)
defaultId: string, // 默认配置 id'' = 无;启动时自动激活的目标)
}
```
- id:复用策略 slug 生成逻辑(拼音映射 + 去重),fallback 前缀 qmt-conn
- baseUrl:归一化(trim + 去尾斜杠 + 校验 http(s):// 前缀);
- 生效连接解析顺序(getActiveConnection):activeId → defaultId → order 第一条 → null。
## 关键机制
1. **启动激活(Q1**apply 时 resolveStartupConnection(settings, config)——list 空 → cordis config.qmtBaseUrl 兜底(Q4 不迁移);否则 defaultId → activeId → 第一条;结果直接作为数据源构造参数并打日志。
2. **热切换(Q2**QmtBridgeRestDataSource 增加 setBaseUrl(url)——baseUrl 为按请求读取的实例字段,改字段即对后续请求生效,无需重建实例、无需重启。
3. **删除边界(Q6**removeQmtConnection 内处理——被删为激活 → activeId 指向 defaultId(若有效)否则第一条;被删为默认 → defaultId 转移到第一条;删除后为空 → activeId/defaultId 清空;**列表仅剩一条时拒绝删除**(返回约定错误)。
4. **测试连接(Q3**:服务端代理 GET {baseUrl}/health15s 超时),返回 { ok, latencyMs, status, error };支持传未保存的 baseUrl(表单内先测后存)。
5. **头部挂载(Q9**client 插件向 slot conversation.session.header.actions 注册 id=odl-qmt-switch、order=-9(内置 PTC 标签 order=-10,并排其后);控件用插件自身 connection RPC 读写配置,不依赖 slot 注入。
## 服务端 APIPOST /odl/api/<method>webServer 自开路由 · 技术约束-004)
| 端点 | 入参 | 行为 |
|---|---|---|
| qmt-connections | - | 返回 { list, activeId, defaultId, active }active 为生效连接对象) |
| qmt-connections/add | { name, baseUrl } | 新增;返回新配置 |
| qmt-connections/update | { id, name, baseUrl } | 编辑;若改的是激活配置且 baseUrl 变化 → 编排 setBaseUrl |
| qmt-connections/remove | { id } | 删除(含边界处理);生效连接变化 → 编排 setBaseUrl;仅剩一条时报错 |
| qmt-connections/activate | { id } | 激活(写 activeId+ setBaseUrl |
| qmt-connections/set-default | { id } | 设默认(写 defaultId |
| qmt-connections/test | { baseUrl } 或 { id } | 代理 /health,返回可达性与延迟 |
## 客户端
- **QmtConnectionChip.jsx(新增)**chip `QMT: <激活配置名> ▾` + 自实现下拉菜单(绝对定位 + 外点关闭;激活项勾选);点选 → activate 端点 → toast 反馈 + 本地状态刷新。
- **SettingsSection.jsx(改)**:第三个子 tab「QMT 连接配置」;配置列表(名称/地址/激活与默认徽标)+ 行内操作(激活 / 设为默认 / 编辑 / 测试 / 删除)+ 新增与编辑表单 + 删除确认弹窗(复用 ConfirmDialog+ Toast 反馈。
- **client/index.js(改)**:注册头部 slot;其余 tab 注册不动。
## 安装与构建
服务端直接改 src;客户端 bundle 走既有 tsdown + wrap 构建(技术约束-005/006);安装用 dsh plugin add(技术约束-007)。cordis.patch.yml 不改动(Q4)。
## 实施事实(2026-08-29
- **改动文件**src/settings.jsschema + 9 个 QMT 函数段)、src/data-source/qmt-bridge-rest.jssetBaseUrl)、src/index.js(启动解析 + dataSource 注入 api)、src/api.js7 个 qmt-connections 端点 + testQmtConnection 代理 + activate/update/remove 热切换编排)、src/client/index.js(头部 slot 注册 odl-qmt-switch order=-9)、src/client/views/QmtConnectionChip.jsx(新增)、src/client/views/SettingsSection.jsx(第三个子 tab QmtConnectionsSettings,追加于文件尾)。cordis.patch.yml 未改动。
- **构建**pnpm run build 一次通过(服务端 ESM 14 文件 + 客户端 CJS bundle 69.60kB + wrap 65907 bytes);node --check 服务端全通过;bundle 内已核对 chip 与端点调用代码。
- **安装方式更正**profile~/.dsh/profiles/web)中插件为 link 符号链接指向工作区,工作区 lib/ 即宿主加载产物,构建后无需再执行 dsh plugin add(技术约束-007 补充事实:link 安装形态下构建即生效)。
- **数据层单测**:14 项断言全部通过(mock settings scope):新增归一化去尾斜杠、order 排序、新增不自动激活、空激活态回退第一条、非法地址拒绝、激活/默认写入、生效连接解析、编辑激活配置地址、删除标记返回、删激活/默认→转移第一条并激活、禁删最后一条、空列表回退 cordis、启动激活默认(覆盖上次激活)、启动解析回写 activeId。
- **生效条件**:线上宿主进程(GUI http://127.0.0.1:3080)启动时已加载旧 libqmt-connections 端点 404 证实),需重启 DSH Web 或重载插件后新代码生效。