# dsh-stock-bar 实现笔记 接口细节、自检脚本、实现要点与已知限制。功能与安装见 [README](../README.md)。 ## HTTP 接口 全部走 DSH 自带的 HTTP 服务(`ctx.webServer.register`),客户端同源 `fetch`。 写类接口一律 `POST`;**任何失败都是 HTTP 200 + `{ ok: false, error }`**,不会给 4xx/5xx(响应头带 `Cache-Control: no-store`)。 ``` GET /api/stock-bar/state 行情 + 自选/持仓 + 汇总 + 设置 + 时段 + 触发的预警 GET /api/stock-bar/kline K 线(symbol / period / adjust / count) GET /api/stock-bar/trends 分时(symbol / days = 1|3|5) GET /api/stock-bar/orderbook 五档盘口 + 委比 GET /api/stock-bar/search 按代码/名称/拼音搜标的 GET /api/stock-bar/trades 交易流水 + 已实现盈亏 GET /api/stock-bar/health 自检:node 版本、store 路径、行情/K线/盘口是否可用、缓存规模 POST /api/stock-bar/items/save 加自选或改持仓(symbol 可写 600519 / sh600519 / 拼音) POST /api/stock-bar/items/remove 删自选 POST /api/stock-bar/settings/save 改设置(只认已知键,数值会夹到合法区间) POST /api/stock-bar/trades/save 记一笔流水(side=buy|sell、price、shares、fee、date) POST /api/stock-bar/trades/remove 删一笔流水 POST /api/stock-bar/trades/clear 清空某只(或全部)流水 POST /api/stock-bar/trades/apply 按流水反推持仓(覆盖股数与成本价) POST /api/stock-bar/alerts/save 加/改预警,响应直接带 hit 与 current POST /api/stock-bar/alerts/remove 删预警 POST /api/stock-bar/alerts/ack 确认触发(重新武装) GET /api/stock-bar/ai/models dsh-llm 里的供应商与模型目录 + 当前选择 POST /api/stock-bar/ai/analyze AI 分析:取数→组装数据包→调模型,NDJSON 流式回传 ``` `/ai/analyze` 的响应是 `Content-Type: application/x-ndjson`,一行一个事件: `meta` / `reasoning` / `delta` / `done` / `error`。模型没配、取不到行情、没指定模型这些情况也走 **合法 JSON + `ok:false`**,客户端先看 Content-Type 再决定怎么读。 请求里的 `symbol` 可以传 `600519`、`sh600519` 或拼音,宿主会先归一化再取数。 行情按「主源失败自动降级」实现(东方财富 / 腾讯 / 新浪),全部放在 `lib/providers.js`,`scripts/probe.mjs` 可以直接打真实接口跑连通性。 **持仓/流水/预警/设置都存在宿主侧的数据文件里**,不写浏览器 localStorage —— 换窗口、清缓存、换浏览器都不会丢。默认路径 `\dsh-stock-bar\portfolio.json` (可用 `cordis.patch.yml` 的 `config.storePath` 改)。写入是「先写 `.tmp-` 再 rename」的原子替换; 文件损坏时会重命名成 `portfolio.json.broken-<时间戳>` 留着,而不是静默丢掉你的持仓。 ## 开发与自检 四个脚本,共 **745 条断言**,全部不需要 DSH 在跑: ```powershell # 1) 宿主侧:路由注册、存储、盈亏/流水/预警算法、缓存 LRU、AI 数据包与流、错误兜底(假 ctx,进程内) node scripts/verify.mjs # 249 条 # 2) 客户端:模块包装契约、槽位注册、真实渲染、canvas 绘制、成本线、涨跌停标记、AI 面板流式解析、组件开关联动、卸载清理 node scripts/verify-client.mjs # 240 条 # 3) 真 HTTP 端到端:把 apply() 挂到一个真的 node:http 服务器上再打进去 node scripts/verify-live.mjs # 173 条 # 4) AI 数据包(不需要网络与模型):指标计算、消息组装、简版/详细两种口径 node scripts/verify-analysis.mjs # 83 条 ``` 再加三个探针: ```powershell # 真实上游接口连通性(东方财富/腾讯,含分时与盘口) node scripts/probe.mjs # 在线装配自检:直接问正在运行的 DSH 要本插件的客户端 bundle node scripts/probe-client-served.mjs "\node_modules\dsh-stock-bar\lib\client.js" # 人工看一眼「喂给 AI 的数据包」长什么样(真实行情,不打模型) node scripts/peek-pack.mjs sh600519 short ``` **为什么需要四个而不是一个。** `verify.mjs` 用假的 `req`/`res` 直接调 handler,跑得最快, 但绕过了两个真实环节:一是 `readBody()` 用的是 `for await (const chunk of req)`,依赖真实的 `IncomingMessage` 异步迭代器(假 req 只能"装作"能迭代);二是 `Content-Length` / `Cache-Control` 这类响应头只有真服务器上才有意义。`verify-live.mjs` 就是补这两个洞的——它建立真实 TCP 连接、 发真实 JSON body、验证响应头字节数,并且额外覆盖了「19 条路由都随插件卸载而注销」和 「上游挂了也返回 200 + ok:false,绝不 5xx」。`verify-analysis.mjs` 单独管 AI 数据包(指标、口径、消息组装), 不需要网络也不需要模型服务。`verify-client.mjs` 自带一个极简 DOM + React Hook 替身, 能在 Node 里把组件真的渲染一遍并跑 effect,用来在上浏览器前抓出渲染期异常、槽位注册错误和 canvas 没画的问题—— 客户端插件最难接受的失败模式是组件渲染时抛异常,框架只会把整个槽位清空,控制台留一句 `slot entry crashed in ''`。 `scripts/probe-client-served.mjs` 复盘了 DSH `dsh-client-modules` 的版本号算法 (`sha1("plugin-artifact" + mtimeMs/ctimeMs/size)` 取 12 位)并直接去请求那个 URL, 用来区分「200 → bundle 已经在对外提供」和「404 → 启动后文件被改过,rev 对不上了」。 ## 实现要点(给改代码的人) - **宿主半边**(`lib/index.js`):`export const inject = ['webServer']`,`apply()` 里对每条路由 `ctx.effect(() => ctx.webServer.register({ kind: 'exact', path, handler }), '说明')`, 这样插件卸载时路由会一起注销。handler 统一过了 `safe()`:任何异常都变成 200 + `{ ok:false, error }`, 不会让 DSH 的 HTTP 服务给用户抛 500。缓存 TTL:行情 2s / K线 15s / 分时 20s / 盘口 3s。 - **客户端半边**(`lib/client.js`):`window.__ModuleLoader__.load({ id, factory })`,`id` 必须等于包名, 只 `require('react')`(浏览器模块表里只有 react 等 9 个模块,DSH 文档明确禁止 require 它自己的内部包)。 唯一的对外请求是 `fetch('/api/stock-bar/...')`。 - **位置**:`shell.overlay` 是 `position:absolute; inset:0; pointer-events:none` 的图层, 且只有直接子元素才恢复 `pointer-events:auto`。所以条子自己 `position:fixed`, 顶部偏移取 `--dsh-frame-top-clearance`(Windows 上就是标题栏那 40px)+ 用户可调的 `topOffset`。 - **样式**:`