diff --git a/CHANGELOG.md b/CHANGELOG.md index 98b425c..5e318f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ All notable changes are recorded here. Versions follow [SemVer](https://semver.org/). +## v0.7.28 (2026-09-25) + +自 v0.7.27 起累计 20 个 PR(#274–#293)。**无 schema 变更(仍 15)、无 config 变更**。本版主线仍是那条已被反复证实的规律:**一个事实只能有一个真源,显示口径必须等于消费口径** —— 本批把它推进到「**状态**」与「**边界**」两类载体上:越界的状态(跨身份、跨请求、跨渲染)与越界的口径(页 vs 数据集、控件 vs 查询)。 + +- **身份边界要收的不只是数据(#278 fa6c399 / #279 d640ef9)** — 登出只清缓存、隐藏 `#app`,而 `#help-panel` / `#chat-modal` / `#tour-overlay` 是 `#app` 的**兄弟节点**、交易视图的 `txTable.*` 与 `txRange`/`txCustomStart`/`txCustomEnd` 是**模块级**变量 ⇒ 上一会话的浮层留在登录页上被下一个人继承(设置视图开着帮助面板登出 ⇒ `#help-context` 仍印 `View: Settings`,而 `renderHelp()` 只在**打开**时渲染 ⇒ 永不自愈);交易页则载荷被清而**证据留下**,新会话按上一位的页码请求、服务端回 `items: []` 而「共 N 条」照旧印 3 条,且不自愈。修法都是**具名复位函数**(`resetSessionOverlays()` / `resetTxView()`,回到**声明处的默认值**)+ 边界一行调用;两轴各有门禁(浮层集与「收起」操作、名册三路派生)。被拒的竞争修法:只复位 `page`/清成 `undefined`(每页退化成 1 行)/在渲染里清证据(实测 300 次请求风暴)。 +- **交易表:四张「说一套做一套」的脸一起收(#281 c448d31 / #282 abee8c3 / #284 e1cd51b / #285 54e0c65)** — ①一份 token 数量**三种拼写**(卡片 `Math.round(n/1000)+"K"`、行 `toFixed(1)`、CSV 导出写缩写文本),且卡片是那些行的合计 ⇒ 筛到只剩一行时同屏两个数不同写法;改为卡片**委派**同一函数、导出写**精确值**。②趋势图的**聚合粒度由控件决定**而不由实际请求窗口决定(`all` 与「自定义但两框都空」拼出逐字相同的无界请求,却分别要 `week`/`hour`)⇒ 同一窗口实测 **5 桶 vs 623 桶(124.6×)**;改为读请求串本身。③列头排序箭头说的是**整个数据集**,而 `buildDataTable` 只排**传进来的那一页**(请求从不带 `sort`、服务端硬编码 `ORDER BY t.id DESC`)⇒ 改为服务端**唯一白名单**(11 键,与前端 `TX_COLUMNS` 逐键相同)+恒尾 `, t.id DESC` 作分页锚 + 非法键/方向 **400**(校验先于取锁),前端只给列表请求带排序、签名覆盖排序(否则「箭头动而列表不动」比原缺陷更坏)。④分页器的省略号判据比它守的窗口**紧一格** ⇒ `page = 4` 与 `page = pages - 3` 两处**印出相邻页码却不放省略号**、一个真实页码消失(10 页时 `page 4` 显示 `1 3 4 5 … 10`);改为**由定义窗口的那行循环推导**(半宽 A=B=1 ⇒ 左 `page > A + 2`、右 `page < pages - (B + 1)`),门禁也写成推导式(接受任何自洽窗口,而不是把本次两个字面量钉死)。 +- **缓存/载荷的「收据」必须由发起它的那次请求写(#289 9fd93ee)** — `loadTransactions()` 给刚加载的载荷盖三条收据,前两条取自**发请求前的快照**,第三条却在响应落地后读**活状态** ⇒ 印章与守卫的投影同源、比对**恒成立**:同一 RTT 内两次控件变更时,被取代的那份响应**不带身份**,它若最后落地就被采纳**并认证为当前** ⇒ 表格永久停在用户已经离开的筛选条件上、此后零请求。改为在 `await` **之前**捕获签名。 +- **语言层与浮层/表单的收尾(#288 5e67055 / #293 25b9967 / #290 e081ffb)** — 切语言的重绘名册漏了**非模态**的快捷键面板(它不盖住设置页的语言下拉 ⇒ 用户能在它开着时切语言),而面板的四行快捷键与 `#help-context` 是 JS 建的、唯一调用点在**打开**分支 ⇒ 半翻译面板、整会话不自愈;登录按钮由 markup 声明 `login.enter`(「进入平台」),而提交处理器在 `finally` 里把它**恢复成别人的键** `T("login.submit")`(全仓唯一消费者)⇒ 输错一次密码按钮就改口,而登录页**没有语言切换器** ⇒ 整会话不自愈;忘记密码的成功提示用了一个样式表没声明的分级(`toast(..., "ok")`)—— `toast()` 不校验 `type`,没有 `.toast.ok` 规则的词**静默退回基类样式**。三处修法都只有一行级改动(补一次重绘 / 捕获 `origLabel` 回写并删掉那个键 / `"ok"`→`"success"`),但都补了门禁把「同一事实只有一个来源」钉住。 +- **输入与展示的口径(#283 52aec72 / #287 118eea5 / #276 bd715f1 / #277 c6da6b8 / #291 1f1bd68 / #275 ed74e32 / #280 858c0b0)** — ①口令下限四处宣告「至少 8 **位**」,而服务端三处校验用的是 `String::len()`(UTF-8 **字节**)⇒ 实际下限约 2–3 个汉字(`密码abc` 5 字符 / 9 字节**被 API 接受**);客户端又用 `pw.length`(UTF-16 码元)⇒ `😀😀😀😀` 读 8 被表单放行。改为统一的按**字符**计数(Rust 逐标量、客户端 `Array.from`)。②忘记密码把「非空但太短」报成「请输入密码」——而应用早有 `err.weakPassword`(正是服务端同一规则经词表渲染出的那句);条件 ↔ 消息错配,改一个标识符。③市场页计数数的是**模型行数**却印成「N 个在售 key」(夹具 4 行 / 6 把 key ⇒ 屏幕写 "4 keys on sale");改名而非改数。④运营成员表的「余额」列只印**永久额**,而列头用的是全站给**可花额**(`balance + gift_balance`)的那组词 ⇒ 用户自己看 101、运营者看 100,差的正是那半「可花、会过期、真划走」的赠送额;改为与兄弟表(admin 成员)逐字同形。⑤交易类型筛选的散文只点名**一部分**被写入的类型(`expire` 缺席)⇒ 补全并由门禁钉住(名册从 SQL 常量渲染处派生)。⑥交易 CSV 导出的单元格转义器漏了 **CR**(记录分隔符正是 `\r\n`)⇒ 带裸 CR 的字段把**一条记录切成两条**;按 RFC 4180 §2.6 补进字符类。⑦上架表单的「每天」快捷勾选用 **property** 禁用七枚单日 chip,而成功后的回收走 `form.reset()` —— 它只还原值与勾选态、**不清 property** ⇒ 上架一次之后七枚 chip「未勾选 + 禁用」、点不动、整会话不自愈。 +- **测试的声明 ≠ 它的输入(#274 7a078d8)** — SSE 分帧测试自称覆盖「跨块边界」,实际夹具把边界放在同一块内 ⇒ 补一条真正跨块的输入(仅测试,生产代码零改动)。 + +**新增门禁**(全部从被测算代码**派生**,不写快照):`the_identity_boundary_resets_the_transaction_view_state`、`the_identity_boundary_closes_the_panels_outside_the_app`、`the_transactions_view_states_one_token_quantity_one_way`、`the_trend_grain_derives_from_the_query_not_from_the_control`、`the_sort_indicator_and_the_order_by_share_one_source`、`the_pager_window_and_its_ellipsis_guards_agree`、`the_receipt_is_written_by_the_request_that_issued_it`、`the_escape_contract_reaches_every_dismissible_overlay`、`the_password_minimum_is_counted_in_the_unit_its_message_names`、`the_forgot_password_length_speaks_the_message_the_same_rule_gets_from_the_server`、`the_marketplace_count_is_expressed_in_the_unit_of_its_rows`、`the_ops_members_balance_cell_is_the_half_its_caption_names`、`every_toast_level_is_a_level_the_sheet_declares`、`a_form_control_disabled_by_the_property_is_cleared_on_the_recycle_path`、`the_language_switch_refreshes_every_overlay_it_can_show`、`the_control_that_rests_owns_its_label`、`the_csv_cell_escaper_quotes_every_rfc4180_special`。 + +**新增功能**:交易页趋势卡可在四档之间切换(**总点数(默认,窗口内累计净变化)** / 消费 / 收益 / 消费+收益)。四档**共用同一份载荷**(`trend` 接口本就返回 `net`/`income`/`expense`)⇒ 档位**不进**请求签名、不触发重拉;`net` 档用折线、y 域按带符号范围且含零基线、标题如实写口径(列筛选一加绝对余额不可定义);柱状三档的归一化/tooltip/图例**只按显示的系列**;复位在**视图入口**、不跨会话持久化(#292 c2f047d)。 + +`cargo test` 308 → **376**(新增 68 条门禁与回归测试)。前端资源令牌由各 PR 自行推格,本版最终为 `i18n.js` / `app.js` `?v=20260924-2`、`api.js` `?v=20260914-13`、`data.js` / `style.css` `?v=20260914-11`。 + ## v0.7.27 (2026-09-21) 自 v0.7.26 起 1 个 PR(#272)。本版修的是**同一族缺陷的第二次现身**:请求体上限分布在**两层**上、而两层是两个数。 diff --git a/Cargo.lock b/Cargo.lock index d09a919..b33664e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,7 +51,7 @@ dependencies = [ [[package]] name = "aitokenpool" -version = "0.7.27" +version = "0.7.28" dependencies = [ "aes-gcm", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 7436e38..94b8ddc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aitokenpool" -version = "0.7.27" +version = "0.7.28" edition = "2021" description = "AI Token 共享池 — 企业 key 池 + 公共共享市场" license = "MIT"