alphah

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • b8288ca349 feat(汇安融轮询): 退避 300s 平档延至第 9 次查询,关单检出压进 5 分钟 2026-10-15 裁定 B。上游未支付单有两个关单时刻:码被扫过的 5~6 分钟关、 没扫的 30~31 分钟关。旧表 Q5(540s) 之后步长跳成 900s,两类关单都被拖: - 未扫码窗(1800~1860s)落进 Q6(1440s)~Q7(2340s) 的空档,最坏晚 9 分钟才知道已关单,关单后订单还挂着 WAIT_PAY; - 迟扫的单同理:关单落在 540s 之后的都要等 Q6,最坏晚约 15 分钟。 新表 count 4..9 恒 300s —— 查询落点 30/60/120/240/540/840/1140/1440/ 1740/2040s,两个窗的检出延迟都 ≤5 分钟;count>=10 回到 900s。查询失败 路径(60/300 快退)与收银台 3s 轮询分支不动。 代价:到观察窗结束(2040s)为止,旧表 6 次查询、新表 10 次(+4/单)。 测试:新增 reschedulePoll_backoffLadder_cappedAt5minThroughUnpaidWindow 九格逐格钉值(0/1→30、2→60、3→120、4/5/9→300、10/12→900)。RED: count=5 期望 300 实得 900;GREEN 80/80;边界变异 count<=10 令 count=10 格变红;无害变异(加括号)仍绿;还原验证通过;全量 mvn test 1292/1292。 读数日志 logs/_t-har-backoff-1..7-*.txt(scratch,未入库)。

před 10 minutami

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • c36aadcf64 docs(汇安融开放文档): 逐单 notify_url 进 10.1/10.4/11,对接指南 docx 同步 har-openapi.md: - 10.1 请求表加 notify_url 行 + 注解框(本单专属、快照语义、不传回落); - 10.4 地址段改三级优先级:快照、Key return_url(注明该级是推送时现读、 不是快照)、租户默认;POST 行「你的 return_url」改「你的回调地址」; - 11 清单加一条(单笔指定用 notify_url,全局兜底配 Key)。 docx 对接指南(生成器 logs/_har-docx-gen.py 为 scratch,不入库): - 04 章「两个位置」表改三级:单笔订单上 / 单把 Key 上 / 租户默认,警示框 「两个都没配」改「三个都没配」; - 5.3 请求表加 notify_url 行 + 注解框;5.6 首句补三级优先级;FAQ 「收不到回调」补一句;06 清单加一条;文档版本 2026-10-14。 验证(logs/_har-docx-verify-notifyurl.py):负读数 5 词旧>0 新=0(含 "2026-10-09" 与两句被替换文案)、恒零护栏 6 词、正对照 20 词;顺序化逐行 diff(zipfile/XML 量具)与 python-docx 普查互为异种量具,diff 仅六处预期 变更、无漂移。dist 词普查:新三词命中 js/index.CEuo7SzW.js,被替旧句 0 命中;正控 5 个旧 dist vintage 同句均 =1(探针量得出正数)。 交付指纹(2026-10-14): docx sha256 ba92075936ceb4bd5014300a5c774f9ca6bb2778c4f152258c73e105c23350fb,50,213 B(旧 49,549 B) dist.zip sha256 708e48cc244809f6e4790f7e8fd118688d02c7ee003bf562d88687bbcacc0a42,9,374,452 B,281 条目
  • 8dcff1063a feat(汇安融下单): 开放 API 支持逐单 notify_url,全生命周期回调只推它 需求:下单可传本单专属回调地址;传了即这笔订单整个生命周期(支付成功、 退款各态、超时关闭 16、轮询收敛、重试)的回调全部走它。 - HarOrderCreateDTO 加 notify_url(选填,@Size(max=512),只校验长度不校验 格式,与乐收赢 LsyQrCreateDTO.notifyUrl 同口径);签名按整个请求体算, 本字段自动覆盖。四个入口共用此 DTO:开放/后台从 body 绑(后台无 UI 控件、 VO 不回显),收银台与自助收银台内部构造、恒 null,行为不变; - pay_har_order 加 notify_url varchar(512):下单时快照冻结(空白存 NULL, 不是空串)。新建 db/har-order-notify-url.sql,部署先跑脚本、再换 jar; - HarCallbackService.notifyEnterprise 顶部单点解析:快照非空白优先,否则 沿用旧两级(Key return_url、租户默认)。notifyEnterprise 是唯一推送入口, 快照腿因此覆盖全部生命周期,重试扫到的单同样只推快照; - 日志压平:新增 HarLogSanitizer(与 LsyLogSanitizer.flatten 逐字同集, 第三份平行拷贝,沿用通道间零互相 import 的既有先例;测试侧对 LSY 份做 等价断言)。pushOnce 的 url ×3 与 e.getMessage() 都过 flatten:地址自此 可由调用方直接可控(CWE-117),异常文本还会带上游或对端的 reason phrase。 测试 +9(1291 全绿,基线 1282):契约键集与蛇形绑定、服务快照与空白存 NULL、回调快照优先/空白回退/重试走快照/SQL 脚本外观、压平字符集与跨份 一致性。变异双向 M1~M5(丢快照 2 红 / isNotEmpty 1 红 / 无害改名全绿 / 丢 U+2029 1 红 / 误加 @JsonProperty("notifyUrl") 2 红),逐个还原并核 sha。 交付指纹(2026-10-14): jar sha256 d6bd3c4ceabd7dba5c6196ca02509c18791298d0267db7923b8616f6133a1298,143,155,897 B dist.zip sha256 708e48cc244809f6e4790f7e8fd118688d02c7ee003bf562d88687bbcacc0a42,9,374,452 B
  • Zobrazit porovnání pro tyto 2 revize »

před 19 hodinami

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 7d7b0470d6 feat(汇安融转译): 16 前端转译为「超时关闭」,下游文档同步告知 16 的定性见前一提交。本条补齐另外两张面:人看的界面必须转译,下游 (开放 API 调用方 / webhook 接收方)必须被告知 —— 报文字段一律不动, status 里的 16 原样发。 - har.ts:HAR_TRADE_STATUS_TEXT 加 "16": "超时关闭"、HAR_TRADE_STATUS_TAG 加 "16": "info";状态字段注释与退款拒单说明同步。列表、详情、筛选下拉、 同步提示共用这两张表,一处收口。 - cashier/index.vue:RESULT 加 16(muted,「这一笔没有收到钱,可以重新 发起」)。16 不在收银台 PENDING(={0}),apply(16) 停轮询进 done 是现状 行为,有断言回归。 - har-openapi.md:10.5 状态表加行与判断句、10.3 退款拒单行补 16、10.4 回调 加「16 也会收到」说明、§11 检查清单加一条。 - 对接 docx 重建:5.6 加框、5.7 表加行与说明、检查清单加一条(生成器 logs/_har-docx-gen.py 为 scratch,不入库)。 - 前端执行级验证台 .claude/plan/2026-10-10-har-status16-harness/:真 SFC + 真模板(订单页)+ 真映射表,16 条断言(含正控与表外值 99 兜底),两向 变异 M1~M4 与还原哈希见目录 README。 验证读数:docx 49,549 B,超时关闭×3 / 按未收款终态处理×1,负控 third_shop_code / 创建门店 0;ts:check 同机差分 289 条存量错误前后逐字节 一致、无新增;harness 终轮 16/16 绿,M1(删 TEXT 格)红 6 条、M2(删 TAG 格)只红 A5b、M3(删收银台 RESULT 格)只红 C1、M4 无害全绿,四次还原 sha256 全中。 无新 SQL;部署换 jar + dist 即可。
  • f4fdb8a391 feat(汇安融订单): 16 定性为未收款终态,不再排期轮询 上游对长时间未支付的单自行关单后回 trade_status=16。我方手上两份规范 (260924 / 261008)均未定义这个码,实测出现在长时间未支付的订单上 (下单约 9 分钟后被上游关闭,早于 pay_timeout=15)。2026-10-10 用户裁定 接纳这个值:本地按**未收款终态**分类,值逐字入库、逐字出网,不本地转义。 - HarOrderStatus 新增 TIMEOUT_CLOSED="16": · 进 FINAL ⇒ reschedulePoll 走「清空 next_query_time」那条路,不再排 一个永远查不出结果的轮询; · 不进 PENDING(={0,4}):钱没到,「还在等支付」不成立; · 不进 PAY_RECORDED(={1}):首页收款统计不多算; · 退款腿只认 PAID="1",它不在可退集合里,行为不变。 - HarOrderService.reschedulePoll 注释补 16 的归属说明,并记下「表外值排期 捞不回」(findPendingForPoll 只捞 PENDING)属历史遗留,本次不动。 - 测试:HarOrderServiceTest 增 reschedulePoll_timeoutClosed16_clearsNextQueryTime (字面量 "16",断言 nextQueryTime 被清空);新增 HarOrderStatusTest,三条 把 16 钉在字面量上(isFinal=true / isNotPending / isNotPayRecorded)。 验证:两向变异(logs/_mut-status16/)J1 把 16 塞进 PENDING ⇒ 恰好 status16_isNotPending 红、其余全绿(j1-pending-red.txt);J2 塞进 PAY_RECORDED ⇒ 恰好 status16_isNotPayRecorded 红(j2-payrecorded-red.txt);两次还原后 4 文件 sha256 与变异前一致(java-baseline-sha.txt),还原后聚焦复跑 80/80 绿 (j-final-green.txt);全量套件 1282/0(logs/_build-status16-full-suite.txt, 跑的是同一批字节)。 残留风险(已裁定接受):上游若把 16 悄悄翻成别的状态,本地不会跟;出口是 管理端「同步上游状态」手工兜。
  • Zobrazit porovnání pro tyto 2 revize »

před 1 dnem

alphah odeslal do větve java v repositáři gatsby/payment-platform

před 4 dny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 0dda1c8025 docs(汇安融对接指南): docx 重建,删建店章与门店字段 - 删「03 创建门店」整章(建店已改超管口径);章节重编号 04~07 → 03~06、 6.x → 5.x,内引同步(第 05 章→第 04 章、6.2→5.2、6.6→5.6、6.7→5.7), 接入流程四步改三步,封面副标题同改; - 5.3 请求表删 shop_code / third_shop_code 两行,响应与回调样例删 third_shop_code,删「自动挑店」框; - 挑店信息降级为去门店化的「`ip` 与地区」框:平台按 ip 判定顾客地区, 解析不出或该地区暂未开通收款 ⇒ 400,与新文案对齐(保留对商户有用的 拒单说明,去掉门店表述)。 验证(logs/_har-docx-verify.py,逐段+表格全文普查):旧件 门店 27 / shop 11 / third_shop_code 8 / 挑店 4 / SP2026 2 / 该地区没有可用门店 2 → 新件全 0;正控:六章 01~06、5.1~5.7、新文案与新框均在。 生成器 logs/_har-docx-gen.py 为 scratch,不入库。
  • c2ca495c1b docs(开放文档): 章节与通道名去门店,响应接口删门店键 隐藏门店(2026-10-09)的文档与前端接入面。 - har-openapi.md:10/11 两章标题去「门店与订单」;概述行内举例「多门店 主体」改「多经营主体」。⚠️ 标题与 harDoc.ts 的 parts 逐字耦合(切片器 精确匹配),改标题必须同步,否则该条在侧边栏静默消失——本提交已同步; - collection-openapi.md:通道名改「支付宝收单通道」(通道一览与交叉引用 两处)、删「创建门店」表行;「五类能力,共 11 个接口」改「四类能力, 共 10 个接口」。开放 API /shop/create 端点保留活体、文档从简(B 批 先例);收款链接一节的「固定门店」是银联通道泛化业务用语,保留; - har.ts:HarOrderVO 响应接口删两键(原位留注释:自 2026-10-09 起响应 不含这两键;请求侧仍可传,见 HarOrderCreateParams);index.vue 无日期 的现状注释同步通道名(带日期的历史记录注释不动)。 锚点核对(logs/_doc-anchor-check.py):harDoc 2 条 + collectionDoc 16 条 parts 与 md 标题逐字全中。dist 词普查(logs/_dist-finalize-shophide.py): 门店与订单/门店主体/共 11 个接口/门店表行 1→0,「本章接口路径前缀」与 merchant_code 句保留(正控)。
  • c7f00dbd15 feat(汇安融订单): 响应与回调不再暴露门店参数 门店隐藏(2026-10-09)的报文层:门店信息不再发给租户与调用方。 - HarOrderVO 删 shop_code / third_shop_code。开放 API 三个接口、后台五个 接口、收银台两个接口共用这一个应答体,删字段一处收口全部订单响应; - 回调 content 不再发 third_shop_code:content 落在商户服务器与对接人的 日志里,与订单响应同等可见; - 挑店失败 400 文案去门店化:「该地区没有可用门店」改「该地区暂未开通 收款」(省市码保留),这条 400 直达开放 API 调用方。 实体与库表保留两列(挑店、上游下单、对账在用);请求侧(HarOrderCreateDTO、 上游 client DTO)不动:上游下单必须带 third_shop_code。 测试先红后绿:契约测试按既有 biz 先例加 shop 键缺失断言(非空夹具 + order_no 正对照,防空报文假绿);回调测试捕获真实表单断言 content 键集 (正控 third_order_no=HAR1);三处文案断言与一处 mock 夹具同步。 HAR 包 369/369、全量 1278/1278。
  • Zobrazit porovnání pro tyto 3 revize »

před 5 dny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 92b8543625 docs(汇安融门店): 建店页两处注释同步超管腿口径 161-162(商户下拉说明)与 314(merchantOptions 说明)在 00267c25 后失真: 取值不再只「按本租户校验」,超管腿全局解析、下拉对超管是全部租户的商户。 纯注释:实测 dist 剥离 JS/模板注释(旧注释文本在产物 0 命中,可见文案 「留空即按企业与租户自动匹配」正控命中两处),不影响 4b87fe5f… 那批 dist 产物。
  • 00267c25db feat(汇安融门店): 建店改超管口径,商户按编号全局解析、归属取自商户行 用户裁定:「门店不再是租户自己创建,而是超管来创建,所以不要有租户操作限制校验, 选的是哪个商户就用商户表的租户id」。 - HarMerchantService#requireByCode 新增(超管腿,无租户谓词;merchant_code 全局唯一 uk_har_merchant_code,编号天生只指一行)。400 空编号 / 404 查不到。 - HarShopService#create ① 支分流:超管走 requireByCode,租户调用与开放 API 保持 requireByTenantAndCode。没有平铺去掉校验:显式 set 的 tenant_id 不会被拦截器改写, 平铺等于让持建店权限的租户用户拿别租户商户号跨租户写,上游还真会建出门店。 - ③ 支:超管不指定商户(也没有企业号)时 400 拒绝,不回退超管自己的租户 (回退会静默挂错租户)。用 400 对齐 requireByTenant 多条时的先例。 - entity.setTenantId(merchant.getTenantId()):归属取自商户行。AutoFill 只在值为 null 时兜底、不会覆盖显式 set,所以这一行是超管腿唯一的归属来源。 - 同类失真注释同步:HarShopCreateDTO / HarShopController / HarOpenController 的 javadoc、 前端 har/shop/index.vue 两处注释(前端本次无功能改动)。 - 测试:HarTestAuth 加 asSuperuser(含带租户重载);HarShopServiceTest +3、 HarMerchantServiceTest +3。先 RED(7 处编译错 + 2 条行为红)后 GREEN。 HAR 包 367/367、全量 1276/1276 绿。 生效要重打 jar;前端随 dist 批次一起重打包(仅注释)。未 push。
  • 38c82c53a6 feat(汇安融订单): 列表与详情隐藏门店栏目 用户裁定:列表删「门店编码」列,详情删「门店编码」「平台门店编号」 两项。创建弹窗的两个门店输入框按「列表和详情」的字面范围未动; 搜索表单本来就没有门店筛选项。生效要重打 dist。
  • 0b0e6494bc docs(开放文档): 10 章隐藏 shop_code / third_shop_code 用户裁定:这两个字段从开放文档撤下。只动文档、报文没简 —— 后端仍接收 (选填),挑店/回显/回调全保留。四处:章首「门店与下单的关系」段、 10.1 请求表两行、10.1 响应表一行、10.4 回调样例一行。 章节标题(## 10. 门店与订单接口 / ## 11. …联调检查清单)逐字保留 —— harDoc.ts 的切片依赖它们。生效要重打 dist,与 jar 无关。
  • Zobrazit porovnání pro tyto 4 revize »

před 5 dny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 2c87ea2868 docs(开放文档): 新增商户对接 docx《支付宝收单 接口对接指南》 从创建门店、创建 API Key、配置回调地址到开放接口联调的四步接入 指南,面向商户运营与技术人员。事实取自 har-openapi.md / collection-openapi.md 与 2026-10-05 实测菜单读数(275 支付宝收单、 213 API对接 /apikey、页签 API Key管理/应用配置/接入文档)。 正文无任何真实密钥(负对照实测 0 命中);生成器 logs/_har-docx-gen.py(scratch,不入库)。
  • d750c285b5 docs(交付): 补记 ③ —— 轮询 10 秒 + 回调上限 3 次,产物指纹换代 记录 2026-10-09 两条用户裁定的落地:f2ef6b13(订单轮询 60s→10s)、 5396cb4c(回调失败上限 8→3 含首推)。含现行 jar/dist 指纹 (46d7cad9… / 1fd3d9f9…,覆盖补记 ② 的 41c1b1ac 批)、判据与正对照 (javap 回读注解与常量、jar 内嵌 SQL、dist 文档 chunk 三命中 + 与旧件 规范化比对唯一差异)、全量 1270/0/0/0 两读一致。 部署注意与订正:已跑过 SQL 的环境无需重跑;yml/启动参数若显式配了 har.callback-max-retries 需同步改 3(配置覆盖默认);乐收赢仍 8 未动; 验收 §三 回调封顶按 3 次观察;push 状态订正(origin/java=41e6c634 此前全部已 push,本轮 3 提交待推)。
  • 5396cb4c7d feat(汇安融): 回调失败重试上限 8 → 3 次(含首推,用户裁定) 「回调失败重试只重试 3 次,不能一直重试」(用户 2026-10-09 裁定)。 原上限 8 与乐收赢默认对齐;本通道收紧到 3(= 首推 1 + 重试 2), 重试节奏不变(仍是每分钟一轮)。乐收赢 leshouying.downstream-notify-max-retries 仍为 8,未动(不在本次裁定范围)。 - HarProperties.callbackMaxRetries 默认 8 → 3(含首次;javadoc 记裁定与语义) - db/har-callback-retry.sql:存量封顶 UPDATE 8 → 3 + 注释与只读核对同步 (脚本封顶值与代码默认值的同步由 HarCallbackServiceTest 钉死) - HarCallbackServiceTest: - schema 用例改为「提取脚本封顶值」→ 断言 == HarProperties 默认值、== 3 (先红后绿;两条断言分别防「脚本/代码漂移」与「数值回退」) - retryFailed_pinsScanQuery 自定义上限 3 → 5:原值恰逢新默认值, 「写死默认常量」不再变红(判别力归零);改 5 后变异实测红 (expected [5,FAILED] but was [3,FAILED]),还原后绿 - har-openapi.md:两处「最多 8 次(含首推)」→ 3 次 部署注意:部署环境若在 application.yml / 启动参数显式配过 har.callback-max-retries,配置会覆盖 Java 默认值 —— 需一并改 3。 已跑过 har-callback-retry.sql 的环境无需重跑(列与索引已在); 脚本新值 3 供生产首次执行与清库重跑。 测试:聚焦 18/18 绿;全量 1270/0/0/0(见交付说明补记 ③)。
  • f2ef6b13be feat(汇安融): 订单轮询间隔 60 秒 → 10 秒(用户裁定) 退款/支付状态收敛的可见延迟 = 单笔查询游标(首档 30 秒)+ 调度器捞取间隔。 原 60 秒间隔实测让「退款点完」到「状态翻页」要等约 1~2 分钟(用户以当日 多笔退款联调单点名)。2026-10-09 裁定收紧到 10 秒。 - HarOrderPollScheduler#pollPendingOrders:fixedDelay / initialDelay 均 60s → 10s - 只改「发货」节奏:单笔订单两次查询的间距由各自的 next_query_time 游标控制 (30s 起步退避),对上游的查询密度不变,只减少「已到期却还没被捞走」的空等 - retryFailedCallbacks 保持 60s / 75s 不动(本次裁定未涉及回调重试节奏) - HarOrderPollSchedulerTest 新增两条钉住用例(先红后绿):poll = 10s、 retry = 60s(该文件此前没有任何调度间隔断言,改回大值曾可静默过) 测试:聚焦 18/18 绿;全量 1270/0/0/0(见交付说明补记 ③)。
  • Zobrazit porovnání pro tyto 4 revize »

před 5 dny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 41e6c63482 fix bug
  • ac2f4201cc docs(交付): 晚间补记 ② —— 文档收缩 + trade_type 兜底,现行 jar/dist 指纹换代 - 10-09 交付说明追加「晚间补记 ②」:两个新提交(eaaa742b / c07a10b6)、 新 jar `859a6c54…`(143,153,263 字节)与 dist.zip `41c1b1ac…`(9,373,646 字节) 覆盖此前所有指纹,附判据(含正对照)与全量测试读数(1268/0/0) - 同文标注「文档从简、报文没简」:查询响应与回调仍实际携带 trade_type - 10-08 门店地区说明加一条顶部横幅:产物以 10-09 那篇为准,§二/§六 指纹作废
  • c07a10b688 feat(汇安融): trade_type 留空由服务端兜底 alipay_un_stage 调用方不必再传交易方式(配套:上一条提交把该字段从开放文档里拿掉)。 biz 的派生读的就是这个取值 —— 留空不兜底的话,非分期单会派生出 null, 上游收到一笔没有 biz 的交易。 - HarOrderCreateDTO.tradeType 去掉 @NotBlank:选填,留空兜底;显式传值仍原样透传、 不校验取值(分期 alipay_stage 与后台表单走这条路) - HarOrderService 新增 TRADE_TYPE_ALIPAY_UN_STAGE 常量与 resolveTradeType(): 供落库、precreate、pay 三条腿与 resolveBiz 派生读同一份解析结果 —— 各读各的 dto.getTradeType() 会让留空时落库为空、上游收到默认值,对不上账 - 两条新测试先红后绿:null 走 precreate 腿(钉落库 + 上游请求 + biz 派生), 空串走 pay 腿(钉「空串与 null 同口径」且两条腿都兜底) 后台表单、分期、收银台与收款链接腿行为不变(那几处显式传值或走别的入口)。
  • eaaa742b17 docs(汇安融): 开放文档按裁定收缩 —— 删建店/被扫/挑店/分期等,trade_type 与 biz 退出调用方契约 调用方契约收敛(2026-10-09 逐项裁定),全部落在「10 章 + 11 联调清单」: - 删「创建门店」整节与门店 create_status 段:接口数四个 → 三个(两处计数同步) - 请求表删七行:auth_code / province_code / extra_params / stage_num / percent / trade_type / biz;ip 行收窄为「顾客用户端ip」 - 删主扫/被扫两场景表与全文相关定义(auth_code 那条腿是后台/收银台的能力,开放文档不再教) - 删「按地区自动挑店」说明块与章首门店地区引子 - 章首与第 5 章的差异说明(金额单位、幂等性对比)整块删除,改为一行接口路径前缀自述 - 响应表同步收窄:pay_url 行去掉主扫/被扫、`title / trade_type / stage_num / percent` 改为只列 `title`、门店编码行改「你传的门店编码原样返回」 - 回调示例删 `trade_type` 行(报文实际仍携带该键,示例从简) - 联调清单删四项(建号省码 / 挑店 / auth_code 两场景 / 门店状态),pay_url 项改口径 - 小节号整体上移一位:原 10.2~10.6 → 10.1~10.5(写前实测 30 处引用、写后两向分布核对) - harDoc.ts 仅同步侧边栏注释(条目与切片逻辑未动) 隐藏 ≠ 后端不再收:DTO 字段全部保留,后台表单与既有调用方照传不误。 其中 trade_type / biz 的后端配套(留空兜底 alipay_un_stage)见下一提交。
  • 9020e88830 docs(交付): 🟡1 行为选项裁定 —— 维持现状(不改交互与业务逻辑) 用户 2026-10-09 委托决策,约束原文「不能影响交互逻辑和业务逻辑」。 三类候选修法全部越线:粘住编号 = 改弹窗打开后的展示、加「重新生成」 按钮 = 新增控件(均属交互);后端拒绝重复建店 = 新增拒绝规则(业务)。 文案层修复(`946a3639`)已消除缺陷实质 —— 旧承诺「重复提交会被后台 拒绝」是假话,现已如实陈述(每次打开换新 + 待核实勿重开重提)。 当前生产 kf 建店路由尚未开通(2026-10-09 实测 404,上游侧待开) ⇒ 上游无法建出隐藏门店,生产无敞口。 交付说明 §五:🟡1 条目由「待拍板」改为裁定记录,小节标题同步。 本提交只动这一份说明(代码零改动,jar/dist 指纹不变)。
  • Zobrazit porovnání pro tyto 18 revize »

před 5 dny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • f09e9cce37 docs(交付): 门店地区交付说明 —— 两处时间改成量出来的(12:47 是我写的,实际 12:51:21) 写「12:47 复跑」时没量日志 mtime,实测 12:51:21(Total time 21.430 s); 第三次构建同理,改记 56.39s / 日志 mtime 12:53:05。
  • d0227fabd6 docs(交付): 门店地区交付说明 —— 前端读数补第三次构建与订正后 ts:check - ts:check 订正后再量:289(exit 2,与基线同数) - build 三次(12:42/12:46/12:52);标出旧 chunk 名只属于当时那一版,别再照着搜
  • 262e452f04 docs(交付): 门店地区交付说明 —— 提交数不写死(本说明自己在数里,自指)
  • 432872c6ca docs(交付): 门店地区交付说明 —— 提交数改成 git 量出来的 8(含本说明自身两刀)
  • a6bdcc4e11 docs(交付): 门店地区交付说明 —— 补 400 订正与产物重打读数 - 提交清单补第 6 刀 `300a27f9`(5 个 → 6 个未 push) - 产物指纹换成重打后的那一版(jar `b6b64c8a…` / dist.zip `80038a66…`), 旧指纹划掉;开包验法写成「从 jar 取 class 反汇编,两处 sipush 400」; dist 那 103 个改名 chunk 的来由(Rollup [hash] 含依赖哈希)与两向读数一并记上 - §二 整包读数补 12:47 复跑(400 订正后 1212/0/0/0) - §五 改成「两件拍板(你已裁定:① 我解决、② 保持现状)」,docx 那条记为保持现状; 另标出 third_shop_id 重复仍是 500 属**本次裁定范围之外,未动**
  • Zobrazit porovnání pro tyto 12 revize »

před 6 dny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • ed24480f4a fix(乐收赢): 收款链接 token 换 CSPRNG —— 与 HAR 同一处订正 用户令:「lsy 那个也改成一样的」(指上一轮 HAR 收款链接的 token 生成器订正,见 b97a0e93)。 乐收赢侧同一处问题:LsyCheckoutLinkService:86 用 IdUtil.fastSimpleUUID(), 而 LeshouyingCheckoutController 的类 javadoc 写着同一个失实的「122 bit 熵」论证。 五个文件。 改了什么 - LsyCheckoutLinkService.issue:IdUtil.fastSimpleUUID() → IdUtil.simpleUUID(),就地写清理由 (fastSimpleUUID 走 RandomUtil.getRandom() = ThreadLocalRandom,48 位 LCG;simpleUUID 走 SecureRandom;两者产出的字符串**形态一模一样**,这正是当初写错还看不出来的原因)。 ★ 已发出的 token 不受影响:库里旧值不动、照用;这里只影响新插入的行。 - LeshouyingCheckoutController 类 javadoc:订正「IdUtil.fastSimpleUUID,122 bit 熵」→ simpleUUID/SecureRandom,并留一段「2026-10-06 订正」说明 122 是 UUIDv4 的格式位、 本身不是不可预测性论证。 - LsyCheckoutLinkEntity.token 的字段注释:同步成 IdUtil.simpleUUID()(CSPRNG)。 - LsyCheckoutLinkServiceTest:新增守卫用例 + 订正形状断言的措辞。 - frontend/src/plugins/permission.ts 注释:那里原写「不要把前端钉死在后端 IdUtil.fastSimpleUUID() 这一种形状上」—— 换生成器后这句话点名的函数两边都不存在了。改成 「令牌生成器**当下**的形状(现在是 IdUtil.simpleUUID() 的 32 位十六进制)」, 既不点名到会腐烂的具体函数,也保留了它原本的论点(别按形状收紧)。 测试 - 新增 issue_tokenComesFromSimpleUuid_notFastSimpleUuid(形态同 HAR 那条): 形状断言对「换错生成器」永远是绿的(两者都是 32 位 hex),所以不测形状, 用 mockStatic 直接钉住**调的是哪一个方法**。 验证读数(本机实跑) - RED(修复前):mvn -Dtest=LsyCheckoutLinkServiceTest test → Tests run: 17, Failures: 1 (只有新守卫那条红;这正是它该有的样子)logs/_t-lsy-token-1-red.txt - GREEN(修复后):同命令 → Tests run: 17, Failures: 0,BUILD SUCCESS logs/_t-lsy-token-2-green.txt - 全量:mvn test → Tests run: 1152, Failures: 0, Errors: 0,BUILD SUCCESS (上一轮 HAR 修复后是 1151 ⇒ +1,正好这条守卫)logs/_t-lsy-token-3-full.txt - jar 已重打:target/payment-platform-1.0.0.jar,mtime 2026-10-06 13:41:27, sha256 90d2cb65bdb3ec31c5d7a5801971b4be3ad59f76ff63e931939990a9b8eeb5af,143117153 字节。 ★ 正对照做在**产物本身**上:从 jar 里取出 class 反汇编,LsyCheckoutLinkService 与 HarCheckoutLinkService 都显示 invokestatic IdUtil.simpleUUID(不是只信源码)。 (上一版 942d3be9… 作废。) - frontend/dist.zip **不变**:改了 permission.ts 的注释后重跑 pnpm build(✓ built in 58.27s, 产物 mtime 13:41:46 = 本次构建),277 个文件的 sha256 与改前逐个相同(0 差异) ⇒ 注释不进产物,dist.zip 仍是当前源码的正确构建,sha256 1e9716e3…。 ※ 第一次比对得到「0 差异」是**假读数**:那次跑在构建仍在编译时(dist 还是旧树,等于拿基线跟 自己比)。上面这组是在构建结束后复量的 —— 记在这里,免得下次又把它当成结论。 有意不动的 - 乐收赢其它 fastSimpleUUID 用处(pay_trace_no / trace_no / refund_trace_no / close_trace_no / cancel_trace_no / query_trace_no,遍布 checkout/cashier/open/refund/query/cancel/onboarding 各腿) **不改**:这些是我们发给上游的对账号,随请求体出网、由上游原样回传,不是任何免登录页面的定位键。 TenantInnerInterceptor:185 已钉住「要按业务键做未认证读必须先补唯一约束(trade_no / pay_trace_no 均无)」 ⇒ 它们不构成匿名定位原语。换成 SecureRandom 不买任何东西,只是更慢。 - .claude/plan/2026-09-30-lsy-checkout-link.md:73 仍写着「token = fastSimpleUUID」:那是**当日**的 设计文档(历史记录,且本就与代码有出入 —— 它写的是 resolve(),代码里是 requireByToken()), 不改历史文档,故只在此说明。 未验证(如实记) - .claude/plan/ops-manual.md 读取被权限闸拒 ⇒ **没有**检查它是否也点名了这个生成器; 若点名了,那一处仍是旧的。
  • b97a0e9311 fix(汇安融): 收款链接评审修复 —— 并发幂等、顾客语 404、token 换 CSPRNG 2026-10-06 独立评审(agent har-checkout-review,台账 logs/_ledger-append-har-checkout-review.md) 提了 F1..F5;用户授权「你决策就行哈」后:修 F2/F3/F4,不修 F1/F5(理由见台账「修复轮」)。 四个文件,全部是本特性自己的新代码。 F2 并发双击 ⇒ 500(HarCheckoutLinkService.issue) - 先读后插之间的窗口:两个请求都读到「没有这一行」⇒ 都 insert ⇒ 后到的撞 uk_har_checkout_tenant_mch,异常穿成 500,而链接其实已经建好 —— 「重复取链接返回同一条」这个承诺在并发下不成立。 - 改为 insert 包 catch DataIntegrityViolationException → 按同一 (租户, 商户编号) 重读: 命中 1 行 ⇒ 返回赢家那一行(log.info,同一条链接同一个 token); 仍为空(撞的是别的索引,例如 token 撞车)⇒ 原样抛出,绝不吞 —— 「链接没建出来却报了成功」比一次 500 坏得多。 - 两处读抽出 selectByTenantAndCode:一份谓词,免得两处 WHERE 慢慢长歪(少一个条件就是跨租户)。 - ★ 能捕到靠 mybatis-spring 的异常翻译(SQLIntegrityConstraintViolation → DuplicateKeyException); 裸 PersistenceException 会穿过去 —— 那是与修复前完全相同的行为,不是新增盲区。注释里写明了。 F3 管理端文案上屏给顾客(HarCheckoutController.create) - 本地闸 requireByTenantAndCode 抛的是「商户编号不存在或不属于当前租户: X」。 这句话写给我们自己的操作员是恰当的,取链接、收银台几条腿共用同一条消息、**不改**; 但这条腿的出口是免登录顾客页,前端 checkout/har/index.vue:395 把 res.msg 原样显示 ⇒ 顾客会读到「租户」「商户编号」这些内部词汇加一串内部编号。 - 改为就地 catch BusinessException → log.warn 留痕 + 换顾客语常量再抛。 状态码照旧 404(它是有意的,不变)。 - 文案「收款方暂时无法收款,请稍后重试或联系收款方」,与 MSG_PAY_FAILED 的差别是刻意的: 那条重按有用(再按生成新单号,重试安全);这条重按没用(商户配置不会自愈)⇒ 指引带「联系收款方」。 F4 token 随机源不是 CSPRNG + 注释失实(HarCheckoutLinkService / HarCheckoutController) - javap 复现(量具 hutool-all-5.8.34):IdUtil.fastSimpleUUID() → randomUUID(false) → RandomUtil.getRandom() = ThreadLocalRandom(48 位 LCG);只有无参 randomUUID() (= UUID.randomUUID().toString(true))走 SecureRandom。 - 两者产出的字符串**形态一模一样**(32 位 hex)—— 这正是当初写错还看不出来的原因, 而免登录路径的安全性全押在「这串字符猜不到」上(类 javadoc「(甲)·预防」第 2 条)。 - 类注释原文「IdUtil.fastSimpleUUID,122 bit 熵」两头都不对(122 是 UUIDv4 的格式位, 本身不是不可预测性论证)。改为 IdUtil.simpleUUID() + 注释订正,并留一段「评审订正」 说明为什么两者形态相同。未部署 ⇒ 无兼容包袱;已发出的 token 不受影响(库里旧值不动、照用)。 - ★ 2109cb6c 的**提交信息**里仍有那句失实断言 —— 不改历史提交,订正在本条正文里。 测试(四条新用例) - issue_whenConcurrentInsertLosesRace_reReadsAndReturnsTheWinnersRow(F2 正) - issue_whenInsertFails_andReReadStillEmpty_rethrows(F2 负:钉「不许吞」;前后都绿,是用例守卫不是 RED→GREEN) - create_missingMerchantRow_messageIsCustomerFacing_notTheAdminWording(F3) - issue_tokenComesFromSimpleUuid_notFastSimpleUuid(F4) · 这是 F4 **唯一可行**的守卫形态:形状断言对「换错生成器」永远是绿的(两者形态相同), 所以用 mockStatic 直接钉住调的是哪一个方法。本仓首次用 mockStatic(Mockito 5 inline mock maker,实测可用)。 验证读数(本机实跑) - RED(修复前):mvn -Dtest=HarCheckoutLinkServiceTest,HarCheckoutControllerTest test → Tests run: 32, Failures: 1, Errors: 1 · F3 用例:expected <收款方暂时无法收款…> but was <商户编号不存在或不属于当前租户: M001> · F2 用例:DuplicateKeyException uk_har_checkout_tenant_mch 穿出(ERROR) (logs/_t-harcheckout-fix-1-red.txt) - GREEN(修复后):同命令 → Tests run: 33, Failures: 0, Errors: 0,BUILD SUCCESS (logs/_t-harcheckout-fix-2-green.txt) - F4 变异两向:实现换回 fastSimpleUUID() → 恰好那条守卫 RED(19 run / 1 failure), 而**形状用例仍绿**(证明形状断言确实抓不到这一类);换回 simpleUUID() → 33/33 绿。 (logs/_t-harcheckout-fix-3-mutant.txt、_t-harcheckout-fix-4-restored.txt) - 全量:mvn test → Tests run: 1151, Failures: 0, Errors: 0,BUILD SUCCESS (修复前 1147 ⇒ +4,正好是这四条;logs/_t-harcheckout-fix-5-full.txt) - 交付件:jar 已重打 —— target/payment-platform-1.0.0.jar,mtime 2026-10-06 13:37:05, sha256 942d3be956884131cc8ced282499d71b0624618e76018c08d0cc4b06ff11fbcf, 143117157 字节,1180 条目;正对照 HarCheckoutLinkService.class / HarCheckoutController.class 都在 BOOT-INF(读回 true)。旧 jar 8e9ba10a… 作废。 frontend/dist.zip **不变**(本轮零前端改动),sha256 1e9716e3… 未验证(如实记) - F2 那条 catch 的**真实**触发路径未在真库复现(并发时序);且 pay_har_checkout_link 的建表 SQL 至今没在任何真库跑过 ⇒ 该表上的唯一索引冲突无法实测。单元测试抛的是异常翻译后的形态(应然)。 - pay_url 从未被真机扫过;顾客页从未用真实 token 在浏览器打开过(沿用首次交付未验证项)。 - F1(无限流/金额无上界)与 F5(订单号单前缀 HAR)**未改**,报用户拍板。
  • 3ae6bf8a9d feat(汇安融): 收款链接(四)—— 前端:顾客页 + 商户页收款二维码入口 后端三刀已齐(2109cb6c 存储与顾客页端点 / b00a8348 管理端取链接 / 8b4619ba 装配接线), 本刀把页面接上。五个文件,全部是前端。 路由与免登录声明 - router/index.ts 新增 /checkout/har/:token(name: HarCheckout),与乐收赢那条同段。 ★ 为什么是**三段**路径:后端 HarCheckoutLinkService#buildUrl 的 PATH_PREFIX 是 /#/checkout/har/,har 是路径里的**字面段**不是参数。若照抄乐收赢的 checkout/:token,"har" 会被当成 token ⇒ 顾客页拿着 "har" 去查链接,一律「链接无效」。 - plugins/permission.ts:PUBLIC_ROUTE_NAMES 加入 "HarCheckout"。 按名字放行 ⇒ 新增公开页必须有人在这里**显式**写一行(这正是当初不按路径前缀的用意)。 两处 name 必须一致,否则顾客页静默变回「跳登录页」—— 页面本身没有任何报错。 - main.ts 无需改:标题净化用的是 hash.startsWith("#/checkout/"),已覆盖 #/checkout/har/。 顾客页(新)views/checkout/har/index.vue - 与乐收赢顾客页同构(同模板、同键盘几何、同四相:加载/无效/可付/跳转),差异都在数据面: · GET /payment/har/checkout/<token> → 读 merchant_name · POST /payment/har/checkout/<token>/orders → body { price: Number(fenToYuan(fen)) } · 取回 pay_url / order_no / price - ★ 金额单位是**元**(HAR 全通道如此;乐收赢那份是分),下限 1 元 —— 与后端 HarAmountFloor.MIN 配对(2026-10-06 裁定:下限还是 1)。 内部仍以分做整数运算(MIN_FEN = 100),只在出网/回显时转元; 回显价走 echoedYuanToFen(字符串→分,不做浮点乘),转不出来才回退本地分。 - 自己的 axios 实例(不过全局拦截器):管理端那支会附 Authorization 并弹 ElMessage, 顾客页不能要(顾客页连登录态都没有)。 - defineOptions({ name: "HarCheckout" }) —— 与路由 name 一致(keep-alive 契约)。 商户页 views/module_payment/har/merchant/index.vue - 操作列 150 → 220,新增「收款二维码」按钮,排在超管专属的 编辑/删除 之前: 它**对两种身份都显示**(租户取自己名下那行、超管可代取,后端两道数据闸)。 - 弹窗:长期有效提示 + 商户(merchant_code / merchant_name 快照)+ 只读链接输入框 + 二维码画布(QRCode.toCanvas 240px),底部 复制链接 / 下载二维码 / 关闭。 - ★ 二维码内容与可复制内容都取后端返回的**整条 url**,前端不拼: 拼第二个副本的后果是「复制的链接」与「二维码里的链接」不是同一条。 - api/module_payment/har.ts:新增 HarCheckoutLinkVO(三格)与 HarAPI.checkoutLink(id), POST(首次调用会写一行,幂等返回同一条——这是刻意的,见那里 javadoc)。 验证(本机实跑读数) - pnpm ts:check:289 错 = 基线 289(logs/_tscheck-har-cashier.txt,12:36 实跑)。 唯一差分:har/merchant/index.vue 一条既有 TS2322 从 162 行挪到 167 行 —— 逐行打印两版都是 :value="t.id",同一句被新增行推移。**新增错误 0**。 - pnpm build:✓ built in 57.71s,exit 0(logs/_build-har-checkout.txt)。 - dist 产物核对(不止步于「构建成功」,按路由映射逐条钉): · checkout/har/index.vue → dist/js/index.DvK02O4b.js(内含 payment/har/checkout、 最低、请输入金额、重新获取链接); · module_payment/har/merchant/index.vue → dist/js/index.D3PpoOoz.js(内含 收款码_、长期有效); · 路由字面量 checkout/har/:token 在 dist/js/index.DKGjoO3J.js。 chunk mtime 均为 2026-10-06 13:16:33 = 本次构建产物(非旧残留)。 未验证(如实记) - pay_url 从未被真机扫过;顾客页从未用真实 token 在浏览器里打开过。 这条要等部署后拿真实商户取链接实测。
  • 8b4619ba3a feat(汇安融): 收款链接(三)—— 装配接线:白名单 + 条件表 + 守卫测试 顾客页两个端点(2109cb6c)与管理端取链接端点(b00a8348)都到了,但缺了这两处 装配它们就是打不开的。本刀只接线与守卫,不改业务代码。 SecurityConfig.WHITE_LIST - 新增 "/payment/har/checkout/**"(免登录,顾客页)。 - ★ 不能指望乐收赢那条 "/payment/checkout/**" 兜住:前缀不同(/payment/har/... 而非 /payment/checkout/...),Ant 模式匹配不到 —— 守卫测试里把这条差异写成了前提复核 (matcher.match("/payment/checkout/**", "/payment/har/checkout/tok") == false)。 - 漏了它 ⇒ 顾客打开链接被 Spring Security 挡成 401/403,而这条路径本来就不该有登录态。 TenantInnerInterceptor.CONDITIONAL_TABLES - 新增 "pay_har_checkout_link"(pay_har_order 补尾逗号)。 - ★ 漏登记 ⇒ 未认证查询被追加 AND tenant_id = 0 ⇒ **每条链接都查不到**, 表现是顾客页一律「链接无效」,而日志里一个字都没有。 - 论证沿用同形条目 pay_lsy_checkout_link 那段(通则 (甲)·预防):定位键是 token, 而 uk_har_checkout_token **单独**建在 token 上 ⇒ 同一 token 命中两行在 schema 层 建不出来 ⇒ 定位用 selectOne 是允许的。注释里写明与 LSY 条目的唯一差异(索引名)。 守卫测试(先看到红:2/4 失败于「未加入白名单 / 未加入条件表」) - HarCheckoutWiringTest 4 条,与 LeshouyingWiringTest 同形: · 白名单用真 Ant 匹配器撞**模式**(存的是 /** 形态,contains 字面量恒 false) + 前提复核(LSY 条目覆盖不到本前缀,所以本登记不可省); · 反面对照:管理端 /payment/har/merchant(含 checkout-link)**不得**被白名单覆盖 —— 否定式读数配正对照(先用确实放行的 /payment/openapi/har/** 证明量具是好的); · 条件表含四张 har 表(漏登记症状见上); · shouldNotFilter(顾客页) == true:顾客页不是下游系统,不该经 ApiKey 认证面 (反向守卫:哪天有人把它挪进 /payment/openapi/** 立刻红)。 - 邻居复跑:HarCheckoutWiringTest 4 + LeshouyingWiringTest 8 = 12/12 绿 (两处常量都是它们读的同一份)。
  • b00a83485c feat(汇安融): 收款链接(二)—— 管理端「取收款链接」端点 对标乐收赢 LeshouyingMerchantController#checkoutLink:商户页操作列取一条永久收款 链接(顾客免登录打开、自输金额;顾客页见上一刀 2109cb6c)。本刀是管理端入口的 **后端半边**;前端按钮/二维码对话框与装配接线在后续提交。 新增端点 - POST /payment/har/merchant/{id}/checkout-link,权限串复用 module_payment:har:merchant:list(与 list 同一条:能看见商户页的人本来就读得到 该行的 merchantCode,本端点没有多给出任何一项**读**的能力)⇒ 不需要新菜单 SQL。 - 幂等且会写库(首次调用建链接行)⇒ POST 不是 GET。 - 应答三格:merchantCode / merchantName / url。merchantName 取**链接行的快照** (操作员看到的名字必须与顾客打开链接时看到的一样),url 只从 HarCheckoutLinkService#buildUrl 出(控制器不自己拼 —— 那是第二个副本, 两处漂移的后果是「复制的链接」与「二维码里的链接」不是同一条)。 两种调用方,两条腿(逐条对标 LSY) - 租户用户:requireByIdForTenant(id, 自己的 tenantId);超管:requireById(id), 租户号取自**那一行** —— 超管的 LoginUser.tenantId 可以是 null(它跨租户看), 若照「取不到租户就 403」写,超管将完全无法使用这个按钮,而商户编号的录入 恰恰只有超管能做。 - 分岔判据 LoginUser#getIsSuperuser,与 TenantInnerInterceptor 用的是同一个开关 (用别的判据会出现「一边以为放行、一边仍在过滤」或更坏的反向组合)。 - 取不到主体 ⇒ 403(默认拒绝,先于任何库操作);非超管且 tenantId==null 仍走 带谓词的那条腿(这种主体在条件表上会被跳过过滤,落进超管腿就能按 id 读任意 租户的行)。404 不区分「不存在」与「不属于你」(不构成越权探测面),且 verifyNoInteractions(linkService) 钉住「拒绝发生在建链接之前」。 测试与验证 - 新增 HarMerchantControllerTest 6 条(先看到编译红:找不到符号 HarCheckoutLinkVO)。 - 定向变异两向验证: · 腿分岔取反(Boolean.TRUE.equals → !Boolean.TRUE.equals):4/6 红 (三条腿用例 + 404 那条); · 快照来源改成商户行 m.getMerchantName():精确 1/6 红(tenantUser 那条); · 两处还原后 6/6 绿(还原动作本身也验证过)。 - 含一处接口形状反射:权限串与 list 逐字相等(两处一起改也抓得住)+ POST + /payment/har/merchant/{id}/checkout-link 拼接路径(前端契约)。
  • Zobrazit porovnání pro tyto 6 revize »

před 1 týdnem

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 96c6d0a55f feat(汇安融): 扫码收款收银台 —— 直接取码主扫 + 3 秒轮询监听本单 对标乐收赢收银台(collection/cashier):操作员只填金额(+选填备注),点一次 就拿到收款链接并画出二维码,页面每 3 秒问一次后端直到出结果。下单与查询两条 腿原样复用 HarOrderService(create / syncFromUpstream / getByOrderNo),不新 增任何上游调用。 后端 - POST /payment/har/cashier/qrcode:装配主扫单(trade_type=alipay_un_stage、 biz=har_ali_un_stage、pay_timeout=15)交给既有下单腿;归属只来自登录身份 (报文里没有企业号),无租户身份直接 403。 - GET /payment/har/cashier/{orderNo}:终态短路;非终态且距上次查上游超过 3 秒 才问一次,不沿用通道那套 30 秒起步的退避游标(柜台前等不起);查询抛错降级 成按本地快照返回,一次上游抖动不变成操作员屏幕上的报错。 - 上游受理但响应里没有 pay_url ⇒ 500「二维码生成失败」:主扫的全部意义就是那 条链接,缺了它在界面上是一张画不出来的二维码。 - 未知字段或坏 JSON ⇒ 400 并回显字段名,而不是兜底的 500「服务器内部错误」。 与乐收赢收银台的三处有意不同 - 金额单位是元(HAR 全通道如此),下限 0.01。 - 没有撤销端点:HAR 上游没有关单接口,页面上「结束」是纯前端动作,界面如实 写明「上游这一单不会被关闭,顾客仍可能扫码付款」。 - 没有倒计时:precreate 响应里没有到期字段,编一个倒计时就是编数字。 前端 - views/module_payment/har/cashier/index.vue:input/waiting/done 三态、单一 apply() 入口、QRCode.toCanvas 260px、轮询带 silent 与失败退避(3s→15s)、 跨 await 的 stillMine 守卫、卸载与 keep-alive 失活都停表(激活时若仍在等待 则把轮询接上)。defineOptions name = payment-har-cashier,与菜单 route_name 逐字相同,keep-alive 才生效。 - api/module_payment/har.ts:cashierQr / cashierStatus 两个函数 + 入参类型。 - 多商户租户显示商户编号选择器(读商户列表失败则藏起,单商户租户不受影响)。 菜单 SQL(未执行,交用户) - db/har-cashier-menu.sql:在既有「汇安融通道」目录(锚 route_name= 'payment-har')下新增页面「扫码收款」与按钮「发起收款」,只授 role 1 —— 2026-10-05 口径下 role 2 在 HAR 子树里连目录都不持有,只授子行等于没授 (buildMenuTree 会静默丢弃父不在集合里的子节点)。脚本末尾备好放开段与 收回段。 验证 - 新增 HarCashierServiceTest 11 条、HarCashierControllerTest 6 条,均先看到 失败再写实现。 - 全量 mvn test:1099 条,0 失败,BUILD SUCCESS。 - vue-tsc --noEmit:两个新文件零诊断(仓库基线 289 行既有诊断,与本次无关)。 未做 / 待办 - pay_url 至今没被一部真实手机扫过,上线后需小额实弹验一次「取码→扫→变绿」。 - 尚未重新打 jar 与 dist;菜单 SQL 尚未执行。

před 1 týdnem

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 5d9a57bb56 docs(乐收赢): 人工标记失败 SQL 的前提段改用 11:40 实测读数(腿在跑、上游不回终局) lsy-refund-stuck-diag.sql 已由用户执行(11:40:43),读数:两行仍 REFUNDING,游标新鲜 (updated_time 11:39:15 + 60s = next_query_time 11:40:15 —— pushNextReconcileTime 的指纹) ⇒ 对账腿在跑且在问,行不收敛不是「腿没跑」。 原文「对账腿一个多小时没收敛」是未测量的表述(事故 11:15、诊断 11:40,实为 25 分钟), 按实测改掉,并把残余可能收敛成两条:①每轮答复都不终局(上游回在途/查无) ②查询调用每轮失败(日志里有 WARN/ERROR)。两种都不会自愈。
  • 5a034f71de chore(乐收赢): 卡死退款单只读诊断 + 人工标记失败 SQL 交付件(待用户执行) 2026-10-06 11:15:34 两笔 /v3/refundorder 同步回 PAY_FAIL(上游可用余额不足),后端按 R11/R-1 有意不落终态 ⇒ 行停在 REFUNDING,占着 uk_lsy_refund_one_pending 与在途闸门 ⇒ 订单一直「退款中」且不可再退。用户同日实测仍未转终态 ⇒ 对账腿没收敛。 - lsy-refund-stuck-diag.sql(只读):两行现状 + 全库非终态退款普查。判读法写在文件头 —— 对账腿每轮无条件推 next_query_time 并同写 updated_time(pushNextReconcileTime), 两列冻住与否即可判「腿有没有在碰它们」;且腿若问到 PAY_FAIL 早就自动落 FAIL 了。 - lsy-refund-manual-fail.sql:代码注释第③条出口「人工改库」。按 refund_trace_no(全局 唯一)+ out_refund_no + status IN (CREATED,REFUNDING) 守卫精确命中两行,写 status='FAIL'/finish_time/updated_time —— 字段集与 settleFromQuery → applyNotifyResult 的 FAIL 终态一致。前置(上游确认没退成功,防双重出金)与副作用(绕过应用层:不发 SSE、 不推下游退款回调)全部写在文件头。 两文件均待用户执行(执行读数只能来自执行者本人)。结构修复(权限化人工出口)另行拍板。
  • 2a55995f3f fix(退款): 批量退款对话框如实转述上游应答 + 失败原因可见 用户 2026-10-06 报障:批量退款时对话框一直转圈,关掉对话框刷新页面后,刚退款的 订单一直停在「退款中」。附线上日志(11:15:34):两笔 /v3/refundorder 均 200 OK, 随后两条 ERROR「退款返回 PAY_FAIL,状态不落终态、留在 REFUNDING(占用在途闸门…)」, result_code=PAY_FAIL、return_msg=业务执行失败[6666000245376951_F53933370可用余额不足]。 根因(读码+日志,后端零改动): - 后端按 R11/R-1 有意不把同步 PAY_FAIL 落终态(防上游误报时放闸、重复退款), 退款行停在 REFUNDING 占着在途闸门 ⇒ 订单显示「退款中」且不可再退; - 前端把同步应答里的 upstream_result_code/upstream_result_msg 整个吞掉: 提交时固定文案「已受理,等待上游结果」、说明列只在结果框(转圈那两分钟看不到)、 退款列表页与订单抽屉都不展示 upstream_result_msg ⇒ 界面上零原因。 本轮只做「如实化」: - 批量对话框表格新增「说明」列(轮询期间就能看到原因);提交与轮询消息按 PAY_FAIL 转述 upstream_result_msg,措辞「上游应答失败:<原话>(等待对账确认, 勿重复发起)」——**不**标 fail:按 R11 那只是同步应答,要等对账查询定案, 状态照旧算「退款中」、轮询照走到上限; - 轮询到 FAIL 终态时带上 upstream_result_msg;仍在途但上游已答失败的同样转述; 超时提示追加一句「其中 N 笔上游已明确应答失败(原因见下表说明列),需人工核对」; - 单笔退款的成功 toast 同口径:PAY_FAIL 时改 warning 转述原因,不报「已受理」; - 退款管理列表页 + 订单抽屉的退款单表新增「上游应答」列(upstream_result_msg || "-"); - 批量对话框跑批时禁用右上角 X(X 关的是对话框、不是轮询:关掉后轮询还会把结果框 弹出来);宽度 860→1040 —— 原表 6 列 min-width 合计 700,加说明列 200 到 900, 860 的内容区会把唯一讲原因的列挤出可视区。 验证(量法+读数): - pnpm build 通过;同机差分旧 dist.zip(274 文件)vs 新 dist(274 文件),chunk 哈希归一后仅 4 个文件有实质差异:订单页 chunk index.CWnRp9I4.js(21185→22510B, 新串全在此)、退款页 chunk index.zHt-3oO4.js(289366→289504B,唯一差异=新列 +130B)、订单页 scoped css(12 处 scope id d889a10b→f484f420,源码变 id 就变)、 入口 css(229905→229893B);其余 270 文件归一后逐字节相同; - 入口 css 那 -12B(.filter,[filter=""]→.filter)根因已用 UNoCSS 探针复现: presetAttributify 的 elementRE 把非 HTML 文本里 <…> 包住的片段当标签扫,旧源 某跨度内有个不带 = 的 filter,产出属性形态 token [filter=""],合并进 filter 工具类选择器;增删行改变了跨度切分。旧/新 token 集分别生成 .filter,[filter=""] 与 .filter,与真实构建逐条对上。运行时零影响:全仓无 filter="…" 属性 (正对照 filterable 可搜到),类规则 .filter 两版都在; - dist.zip 重打:size 9361525、sha256 948324afd00b7dbb90ba823e830240dd7afac2165c8845d51505733522359a7d; 回读 zip:新串命中 CWnRp9I4,上游应答命中 CWnRp9I4+zHt-3oO4,上一轮「收款二维码」仍在。 遗留(资金方向,等用户拍板,未动):对账腿未必收敛(settleFromQuery 对 PAY_IN_PROCESS 静默、对「订单信息不存在」只 WARN),卡死的退款行需要人工出口 (A 权限化「标记退款失败」释放闸门 / B 放宽 R11 让同步 PAY_FAIL 落终态 / C 问上游被拒退款的 queryrefund 应回什么)。
  • e879e004a4 fix(商户管理): 操作栏「收款链接」改「收款二维码」 用户 2026-10-06 点名:商户管理操作栏那个按钮的文案改成「收款二维码」(它打开的弹窗 本来就是链接 + 二维码,二维码是主要用法)。 - 按钮文案:merchant/index.vue(`openLink` 那颗); - 同文件那段说明「该按钮对两种身份都显示」的注释、以及 collection.ts 里指向该按钮的 引文,一并跟上(注释按旧名字指按钮会失真)。 不动:弹窗标题(`title="收款链接"`)与复制成功提示(「收款链接已复制」)—— 说的都是 链接本体,用户只点名操作栏;要一起改再说。 验证(同机差分,新旧 dist 逐文件比对 274/274): - 内容有实质差异的只有 2 个文件:本页 chunk(唯一差异 = 收款链接→收款二维码) 与其 CSS(SFC scope id 随源码变化,data-v-2e235964→data-v-3d5db13d,属预期); - 其余 272 个文件按 chunk 哈希归一后逐字节相同(差异只是级联改名的引用); - 全量字符串计数:收款链接 20→19、收款二维码 0→1、收款链接已复制 1→1。
  • faaf07a314 revert(订单归属): 撤销 created_id 全套 —— 订单列表回到按租户可见(用户裁定「按租户来」) 用户 2026-10-05 反转 6828f378 的裁定:「这个字段要来有何用?租户id不就行了吗」; 「什么跟什么啊,我不是说了按租户来吗?」。订单列表的可见性按**租户**,不按创建人, 故整提交反做,7 个文件对应回退: - 读:/page 移除 applyCreatorFilter(非超管恒加 created_id = 本人的谓词) - 写:收银台建单腿移除 created_id 赋值与 requireUserId() - 列:LsyOrderEntity.createdId、V1.8 建表语句、db/lsy-order-created-id.sql 一并撤销 ⇒ 部署不再需要先跑 ALTER;若某库已执行过该脚本,残留列无人读写,无害 - 测试:6828f378 新增的 5 条用例随之回退(回到先前的既有契约) 后果:非超管列表恢复「本租户可见」,收银台 / 开放 API / 免登录收款链接各腿建的 订单都在列表里;created_id 恒 NULL 的单(顾客自助、开放 API、全部历史行)不再被 「本人建的」谓词过滤 —— 这正是这轮「订单看不到」的成因。 全量测试 1082/0/0 BUILD SUCCESS(6828f378 时为 1087,差额即回退的 5 条用例)。
  • Zobrazit porovnání pro tyto 36 revize »

před 1 týdnem

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 0e501260f1 fix bug
  • 712e1c4dbe style(顾客页): 中间那块空白用键盘与金额吃掉(手机键 52→66、金额 34→46;宽屏键行高跟窗口走) 用户报文「中间留白太多了」,问到是哪种窗口时答「两个都看了,都空」(390 与 1440 都空)。 上一版留白 426(390×844)/ 482(1440×900),占屏 50% 上下;参照页在同尺寸下是 465, **比我们还空** —— 所以没有一个「参照页多少我们就多少」的锚点可用,只能照用户选的方向做: 把空白交给界面自己吃掉(加高键盘、放大金额),而不是把柱子收窄。 - 键行高 52 → 66、键上数字 21 → 24、退格图标 24 → 26;「付款」字号 19 → 21。 - 金额 34 → 46(占位 28 → 36 同比),¥ 26 → 32,金额行 min-height 46 → 62。 - 骨架跟着走:金额块 34 → 46;键区高度由写死的 226 改成 `calc(var(--key-row) * 4 + 18px)`,行高提成 `--key-row` 变量,两处只有一个源。 手机 390×844:留白 426 → 372,scrollHeight 仍 844(不滚动),键 89×66。 最长的合法金额 "9999999.99" 在 46px 下 canvas 实测 268px,输入框可用 330px。 宽屏另加一段 @media (min-width: 600px),让键盘自己长高。空白在桌面上唯一的用处就是 「别太挤」,不管它就会跟着显示器一起长。行高用 clamp(66px, 12vh, 112px) 而不是定值 —— 定 112 的话 1440×700 这类半屏窗口内容会到 675 而窗口只有 700,再矮一点就平白长出滚动条; 12vh 让它跟着窗口缩,下限 66 就是手机那一档,上限 112 = 键宽(正好见方)。 1440×900:键 112×108、留白 482 → 260;1440×600:键 112×72、留白 104; 1440×700 与 1920×1080 也量过,五档 scroll 都等于视口。390×844 下**逐格不变** (键仍 89×66、留白仍 372)—— 先往浏览器注入候选 CSS 量过,再改源码,改完复量一致。 量具换了一支:MCP 那个浏览器是用户正在用的窗口,setViewportSize 被钳住(要 1440×900, 读回 innerWidth=1249、outerHeight=1380)⇒ 视口不可控,基于它的读数作废。改用自己的 headless 实例(logs/_measure.mjs,--browser chrome),视口由命令行钉死;两支量具在重叠 处读数一致(390: 372 / 1440: 428)。 验证:键盘交互表 23/23(headless 真点键,含「被拒后再输入即复原」与下单遮罩 5ms 轮询抓取); 加载态骨架与真键区两档逐字节同框(390: 554/282/836;1440: 442/450/892); `node _verify-checkout/title.mjs` 4 通过 / 0 失败;`pnpm ts:check` 289 条,与改动前同数 (该命令恒 exit 2,基线非零),views/checkout 0 条。 顺带修了预览台一处会污染截图的东西:未知 `/api/*` 原先兜底成 index.html(200 + HTML), 应用启动拉的 `/api/v1/system/param/info` 拿到 HTML 就弹一个 Element Plus 报错吐司 (截图里压在商户名上的那个红块),过几秒自己消失。改成对未 mock 的 `/api/*` 回 404。 这是台子的毛病、不是页面的,但它会混进每一张截图。 只有前端产物变化,无后端改动 ⇒ 无需重打 jar。
  • 1a719c9a0b style(顾客页): 收款链接页照国内收银台重做(四列键盘 + 纵跨三行的「付款」),并补两处边界 前一版把这一屏做成「一张收据」:深桌面、撕口、条码、编号、作废印章。用户判它 **「AI 味太浓」**,并给了一张真码当基准(扫进去是 `https://p.eycard.cn/p?qr=rE3Erq`, 要用微信内置浏览器的 UA 才给渲染)。这一版照那一页做。 那些比喻的问题不是不好看,是**每一个都要额外画一样东西**(纸要撕口、要条码、要落款), 加起来就成了「设计过的海报」,而不是一个收银台。所以这一版照抄的是它的**比例与克制**, 不是它的配色:主色仍是我方 `--brand`(刻意不用参照页那个绿 —— 那个绿一眼就是微信支付)。 量出来的参照(390×844):商户名 16.6px/700 #333;「金额」标签 14.6px/700;¥ 27px/700; 占位 30px/500 #dedede;金额数字 35.4px/700;键 87×52、圆角 5.2px、白底、投影 `0 2px 2px rgb(0 0 0 / 18%)`;键上的数字 20.8px/700;「付款」键 87×166(纵跨最右三行); 「0」横跨两格;退格在右上角。我方成品在 390 下量到的是键 89×52、「付款」89×168、 「0」184 宽、退格在 (293,610)、键盘底沿 844 = 屏幕下沿。 **键盘**(沿用上一版的功能设计,只换皮)。4 列 × 4 行:最右列上面退格、下面「付款」 纵跨三行;「0」横跨两格。白键浮在同色底上、彼此留缝,不是出血的表格线。 退格是内联 SVG,不用 `⌫` 那个码位(部分安卓字体里会掉成方框)。 - 顾客因此**不必再点输入框**:`inputmode="none"` 让点它也不弹系统键盘(桌面上的实体键盘 照旧能打字),且进页面**不再自动聚焦** —— 那个聚焦只会在手机上弹出一块软键盘, 正好盖住刚画好的键盘。 - 过滤与上限**只有 `onInput` 一处**:`press()` 只把字符接上去再调它。两处各写一份, 迟早出现「物理键盘打不进去、点键盘能打进去」这类只在一种输入下成立的缺陷。 - 金额用 `font-variant-numeric: tabular-nums`(**不是**等宽字体栈):位数变化时字宽不变, 光标不跳。等宽字体是上一版那个「收据」比喻带进来的。 - 整数位上限 7 位(999 万元)与「去前导零」照旧,都只写在 `onInput`。 - 校验没过时金额行抖一下,`shaking` 只由 `reject()` 置真、由 `onInput()` 置假 —— 同一个类连着挂两次 CSS 动画不会重放,所以既不重挂也不计时。提示那句(键盘正上方) 同时转红;**只红金额与提示,键盘不参与报错**。 **用户报的边界:先按 `.` 再按数字,应当直接变成小数。** 旧行为是金额行上顶着一个光秃秃的 `.`,再按数字得 `.4`。成因在 `onInput` 的形状过滤 `^(\d*\.?\d{0,2})` 允许省略整数位,而 `yuanToFen` 也**确实**吃得下 `.5`(所以这是个 显示问题、不是算错)。修法是在同一条链末尾补一句 `.replace(/^\./, "0.")`:按 `.` 得 `0.`, 再按数字得 `0.4`。修在过滤链里,物理键盘一起受益。顺带把那句「容忍省略整数位的 .5」的 注释改准 —— 补零之后那条**不再从界面来**,只对直接调用本函数的代码成立。 **宽屏**:这一屏真正的入口是扫码,顾客一定在手机上;但商户自己会拿电脑点开这条链接试, 不管的话 1440 下键盘就铺满整屏(实测键宽 350px)。整页收成一根 480 的手机宽柱居中 (`align-items:center` + 直接子块 `width:100%;max-width:480px`)。 柱子给**子块**而不是给 `.checkout` 自己,页面底色才是出血的,否则两侧会留一道白边; `.redirect` 用 `:not()` 排除在外(它是 `position:fixed` 盖满屏的,收窄就废了)。 验证: - `node _verify-checkout/title.mjs` —— **4 通过 / 0 失败**。这次先炸了一回: `TypeError: object null is not iterable`。查下来不是模板错 —— compiler-dom 的 `stringifyStatic` 把**连续的 ≥5 个带属性静态元素**串成一段 HTML(`ec >= 5`), 这一版正好有两处够格:加载态骨架(`header.panel` + 3 个骨架格 + `div.pad-skeleton`)、 无效态文案(`header.panel>h1` + `div.notice` 两个 `p`)。编译产物里 `createStaticVNode` 两处、残留表达式占位符 0 个 ⇒ 串化内容与模板逐字一致,是**台子少实现了宿主钩子** (`insertStaticContent` 桩成 `() => null`,而契约要求返回 `[首节点, 末节点]`)。 给台子按契约补上(整段当一个不透明节点;每次都新建,不回用传进来的 start —— 真 DOM 在这里是克隆)。 两向变异:把钩子改回 `() => null` ⇒ 复现原来那条崩溃(一条断言都没跑到); 还原 ⇒ 逐字节相同并回到 4/0。 - `pnpm ts:check` —— 289 条,与改动前**同数**(该命令恒 exit 2,基线非零), `views/checkout/index.vue` 0 条。 - `pnpm build` 通过;在真产物上预览(`_verify-checkout/preview-server.mjs` 同时吐 `dist/` 与开屏接口 —— 生产构建里 `VITE_APP_BASE_API=/api/v1` 是同源相对路径),Playwright 真点键,390×844: | 动作 | 读数 | |---|---| | 点 `1` `2` `3` | `123` | | 点退格 | `12` | | 连点 `0` 八次 | `1200000`(整数位 7 位上限挡住第 8 位) | | 空金额点 `0`、再点 `0` | `0` → `0`(前导零不累积) | | 点 `.` | **`0.`**(旧版是一个光秃秃的 `.`) | | 再点 `4`、`1` | `0.4` → `0.41`(旧版是 `.4`) | | 再点 `.`、再点 `5` | `0.41` → `0.41`(第二个点与第三位小数都被吃掉) | | 退格三次 | `0.41` → `0` | | 点 `.` `0` `0` | `0.00` | | 按「付款」(0.41 元) | 提示「收款金额最低 1 元」,金额与提示同时转 `#e34d59`,**POST 计数 0** | | 再点 `4` | `0.04`,提示回到「最低 1 元」、颜色复原 | | 空金额按「付款」 | 提示「请输入正确的金额,最多两位小数」,**POST 计数 0** | | 点 `3` `8` `.` `5` `0` 后按「付款」 | `POST /payment/checkout/<token>/orders` 发出;遮罩「正在前往支付页面,应付 **¥38.50**」+ 订单号回显 | 布局(390×844):`scrollWidth 390`(= 视口,无横向溢出)、`scrollHeight 844`(不滚动)、 `header.panel` 0,0,390,147、`.pad` 0,602,390,242。 宽屏 1440×900:柱 480 居中(panel x=480 w=480),键 112px,`scrollWidth 1440` 无溢出; **390 下一个像素都没变** —— 先往浏览器注入候选 CSS 量过(panel/pad/键位逐格相同), 再改源码,改完复量一致。 遮罩在 1440 下 `position:fixed` 铺满 1440×900(`> *:not(.redirect)` 的排除成立)。 另:加载态骨架与无效态文案这两段串化内容在浏览器里都按正确的类名与尺寸渲染出来了。 不含后端改动 ⇒ **无需重打 jar**,只是前端产物。但部署前仍须先执行 `java/src/main/resources/db/lsy-checkout-link.sql`(建表 + 菜单/权限串,无此表顾客页直接 起不来),执行后需重登。
  • 7a82dbcb72 fix(乐收赢测试): 实体↔DDL 分叉防线放行 `@TableField(exist = false)` 字段,并把这个前提钉住 用户跑全量 `mvn test` 报红:`LsyEntityMappingTest.everyEntityFieldHasAColumnInTheMigration` —— `pay_lsy_order 缺列 refunding`。根因不是实体写错,是**防线自己少了一条语义**: 那条防线按**反射**枚举实体字段(含父类),把每个字段都当作 MP 会写进 SQL 的列。 而 `41b0ed89` 新加的 `LsyOrderEntity.refunding` 是**派生量**(`@TableField(exist = false)`, 不落库),MyBatis-Plus 本来就会把它从列清单里剔掉 ⇒ 它不可能引发 42703, 这条防线的前提(「MP 按实体生成列清单」)在它身上不成立。 **放行它不等于放宽防线**,两处证据都是量出来的,不是推的: - 新增 `nonPersistentFieldsAreExcludedFromMybatisPlusColumnList`:不引注解作证据, 直接问 MP 要 `TableInfo`(不碰库、不起 Spring),逐字段核对非持久字段**不在**列清单里, 并配正对照(同一实体里至少一个真列在清单里 —— 否则空清单能让否定读数也通过) 与防空转断言(四张实体里一个这类字段都没有时直接红)。 - 两向变异(都还原并用 sha256 核过,`05668d7b…`): · 摘掉 `refunding` 上的注解 ⇒ **2 红**(原防线 `:124` 与 新用例的空转分支 `:189`); · 给**真列** `finishTime` 挂上 `exist = false`(也就是「有人用这个注解来消音」那条路) ⇒ **1 红,来自方向二**(`多出实体未映射的列 finish_time`)⇒ 放行的口子没有绕过反向防线。 全量读数:`mvn test` **1026 条 / 0 失败**(用户那一跑是 1025 条 / 1 失败,多的这条就是新用例)。 只动测试,生产码与 `target/classes` 未变 ⇒ 15:32 那个 jar 仍是有效产物,不必重打。
  • ca27929e32 fix(顾客页): 标签页标题不被后台系统标题覆盖,离开时还原 顾客自助收银台(免登录 /#/checkout/<token>)面对的是正要付钱的人, 标签页上挂着管理端的名字(sys_web_title,「公司管理」)不合适。 两处一起改才成立: - checkout/index.vue:onMounted 写自己的标题(Payment Platform), onUnmounted 把**进来之前那一份**写回去 —— 这是 SPA,浏览器后退键会把人 送回后台那一屏,不还原的话后台标签页会一直挂着顾客页的名字。 存的是自己读到的原值,不是写死一个常量回填。 - main.ts:setTitleAndFavicon() 是异步的(先 await 一次配置请求), 不拦的话配置晚到一步就会把顾客页标题改回 sys_web_title, 症状是「标题偶尔又变回公司管理」。判据用 hash 前缀而不是路由名 —— 本函数在 router 就绪之前就会跑,那时读不到 currentRoute。 验证(frontend/_verify-checkout/title.mjs,4 条断言全绿): 本仓没有 jsdom,改用 createRenderer + 自写内存 nodeOps,真 vue 运行时、 onMounted/onUnmounted 真的会触发。T1 挂载后标题变、T2 卸载后还原; T3 顾客页 hash 下 sys_web_title 不生效;T4 是正对照 —— 后台 hash 下它照常生效 (没有这条,「把整个功能删掉」也能让 T3 通过)。 变异:去掉 main.ts 那道守卫 ⇒ 只有 T3 红;去掉 onMounted 里的标题写入 ⇒ 只有 T1 红。
  • Zobrazit porovnání pro tyto 46 revize »

před 2 týdny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 7a2ec244fd fix(转账): 失败通知把 error_code / fail_reason 丢掉了 —— 两列一直空着 用户 2026-09-29 报:转账通知报文里有失败信息,但 pay_transfer 上没赋值。 根因(读代码定位,非推测): `AccountHandler.handleTransOrderChanged`(本方法由 f5b92b61 于 2026-07-16 补上) 从 params 里只取了 5 个键 —— out_biz_no / order_id / status / pay_fund_order_id / trans_amount,然后回写 6 列。**error_code 与 fail_reason 从头到尾没被读过一次**, 所以两列永远是空的。status=FAIL 却写进去了,这就是用户看到的形态。 同一文件里那条老腿 `handleTransChange`(:261-262 / :289-290)是读这两个字段的, 新腿补的时候漏了。而字段名还**换了**:本接口叫 `fail_reason`, 老腿那一族(alipay.commerce.ec.*)叫 `error_msg`。照老腿抄名字同样会落空。 字段确实到手了的证据(不是「也许上游没发」): `NotificationService.flattenBizContent`(:197-215)把 biz_content 的每个键展开进 params(只跳过顶层已有的键)。本条报文的顶层是 charset/biz_content/utc_timestamp/ sign/app_id/version/sign_type/notify_id/msg_method,与这两个键不撞。 同一报文里的 order_id 已经落到 pay_transfer.order_no 上了 ⇒ 展开这一跳是通的。 修法:多读两个键,多写两列,都带 StrUtil.isNotBlank 守卫(成功报文里没有这两个键, 守卫保证一个字符都不写)。 新增 AccountHandlerTest(本类此前**没有任何测试**,所以这个缺陷只能靠生产数据发现): - transOrderChanged_fail_writesErrorCodeAndReason —— 键名与取值逐字取自 2026-09-29 生产库 alipay_notify_log.message,不是照文档或邻类推的 - transOrderChanged_success_leavesErrorColumnsUntouched —— 正对照, 证明守卫不是「无论如何都写」 两向读数: - 修复前跑 ⇒ 红:expected: <Ali_Err_111036> but was: <null>(正对照那条绿) - 变异(把 params.get("fail_reason") 改成 params.get("error_msg"))⇒ 红 这一变异**正对着缺陷本身**(错的就是字段名),证明用例钉的是名字不是行为 - 还原后 sha256 逐字节相同(43c96b6f…),grep MUTANT 无残留 - 全量 923 绿 / 0 失败 / 0 错误(surefire-reports 跑前已清空,避免跨运行累积虚高) 未改动、但同一条腿上还有一处**既有**缺陷,本提交不碰(无线上影响,且是别的批次的地盘): `transfer.setNextRetryAt(null)` 被 MyBatis-Plus 的 NOT_NULL 更新策略静默吞掉, 所以 next_retry_at 清不掉(用户给的这行读数里它仍在)。今天全仓没有任何 @Scheduled 或 SQL 消费这个列,所以没有实际后果。 ★ 已发生的这笔失败不会自动补上:状态已是 FAIL,而本方法开头就 `if (!"DEALING".equals(...)) return;` ⇒ 重复通知也会被挡在门外。要补历史得手写 UPDATE。
  • c1afbd1595 chore(乐收赢): 补交租户角色页面授权 SQL(此前一直只在工作区) `lsy-tenant-page-grants.sql` 是 2026-09-28 那批交付里唯一**没有被跟踪**的文件 —— 本仓其余 13 份 `db/lsy-*.sql` 全部入库,只有它挂在 `?? ` 下。交付件不进版本库 就会静默消失(本仓已有前例),所以在用户执行它之前先把它落进 git。**只落文件, 没有对任何库跑过。** 内容:把「银联收单」目录(`route_name='Collection'`)连同其下三个只读页面 (`CollectionOrder` / `CollectionRefund` / `CollectionMerchant`)补授给 租户角色(`role_id = 2`)。 为什么必须连目录一起授:`UserService.buildMenuTree` 对「父不在集合里」的子节点 是**静默丢弃**(无 else 分支、不报错)⇒ 只授页面等于没授。 已知的行为取舍(脚本头部第 38-48 行已写明,此处不重复论证):三个页面里 `CollectionMerchant` 是唯一带写权限的(page/save/delete 共用同一个权限串), 执行前可以只删掉 `pages` 的 VALUES 里那一行。 幂等:末条 INSERT 逐行 `NOT EXISTS`,重跑不插重复行也不报错。
  • f2002b6a46 test(乐收赢): 退款回调修复的两支探针(直连库读数 + HTTP 端到端) 修复有没有生效,之前只有单测说得了话。补两支探针,一支量库、一支量接口。 **两支探针的量具不同,读数不能互相替代**(这是它俩分开写的理由): 1. `logs/_lsy-order-refunds-probe.py` —— psycopg2 直连库,跑与新端点**同一条** WHERE / ORDER BY。它证明的是「按 out_trade_no 能查到这几行、字段长这样」, **证明不了端点**:不过 Spring Security、不过 @PreAuthorize、不过 Jackson、 不过租户拦截器。它自己的 docstring 里写着这句话。 ★ 这一版顺带订正了它上一版的**越权表述**:原来逐行打印 「前端要读的键 X 在**响应**里存在」,可它量的是**表里的列** —— 两回事。 现在改成量 information_schema 并按实话说,最后一跳(实体 → JSON 键名) 标注为「未由本脚本验证」。(键名那一跳另由读 `LsyRefundEntity` 与父类 `PaymentBaseEntity` 源码确认:`status` 是**继承**来的,子类里根本没有这一行。) 2. `logs/_lsy-refunds-e2e.py` —— 走 HTTP 拿真令牌打两个端点, 收到的是**页面真正会收到的那份 JSON**。这是「抽屉里有没有东西」的证据。 2026-09-29 在订单 BC1790649777 上的读数(该单已全额退款,totals 1/1): - `/refunds` → HTTP 200,**1 行**,五列全有真值: `out_refund_no=RF1104113b4da0`、`refund_fee=1`、`status=SUCCESS`、 `refund_no=200001070X4026092911041110001`、 `finish_time=2026-09-29T11:04:13.007374+08:00`(微秒 6 位 ⇒ 前端那列宽度据此定的)。 - `/notify-logs` → HTTP 200,**6 行**(改前只按订单 trace 查是 4 行), 多出的两行正是退款回调:`11:04:12.903 RECEIVED` 与 `11:04:12.937 ORDER_NOT_FOUND`,两条 `trade_type=0`、trace 都是 `4e8ceec2b83b44a78efaacd73499b5a9`(= 我方的 refund_trace_no)。 这两行就是用户问「回调轨迹还是支付那四条」看到的缺的那两行; `ORDER_NOT_FOUND` 是**修复前**那次误判留下的历史行,重部署后不会再产生。 顺带修 `logs/_lsy-refund-live.py` 第 4 步的**列名错**:它 SELECT 了 `pay_lsy_refund.refund_reason`,而那一列本表**没有**(它在汇安融那张表上), 一跑就 UndefinedColumn。已换成 `refund_trace_no` 并注明列名逐个取自实体。 ★ 该脚本**没有**重新端到端跑过 —— 它一跑就发第二笔真退款,不做。 两处都只读(SELECT / GET),不发写请求。
  • 436df3b52d feat(乐收赢): 订单抽屉显示已退金额与退款单 用户 2026-09-29 报:「订单的状态是已全额退款,但是详情中没有显示退款之类的东西」。 已退金额这一行**不需要后端改动** —— `refunded_amount` 本来就在详情响应里 (`LsyOrderEntity.refundedAmount`),缺的只是没人渲染它。补一行。 「退款单」一节用新端点 `getLsyOrderRefunds`,五列:商户退款单号 / 金额 / 状态 / 退款单号 / 完成时间,整节 `v-if="refunds.length"` —— 空表会让每一笔正常支付都拖 一节「没有退款」的空架子,而这一节存在的意义恰恰是回答「这笔到底退没退」。 几处刻意选择,都写进代码注释了: - **不拿 `getLsyRefundPage({keyword})` 代替**:那条腿是 LIKE,给的是「订单号包含 本单号」的退款单,抽屉要的是「属于本单的」。订单号互为前缀时两者不同。 - 状态中文映射逐个取自 `LsyRefundStatus` 枚举源码,措辞与退款列表页一致 —— 同一份状态在两个页面叫两个名字,排查时会对不上。 - 完成时间**不引入 dayjs 格式化**(本仓有 `formatToDateTime`):同一个抽屉里 「回调轨迹」那节显示的是 Jackson 原文,两节时间长得不一样比原样显示更难读。 宽度 180 与该节、以及订单列表的「创建时间」取齐。 - 完成时间挂 `show-overflow-tooltip`:实测值是**微秒** 6 位 (`2026-09-29T11:04:13.007374+08:00`,32 字符),比按毫秒估的多 3 个字符; 截断至少能悬停看全,不是无声丢字。 - `openDetail` 三条 ref **先清空再赋值**:抽屉实例是复用的,任何一条请求抛异常, 上一次的内容会原样留着,而它看起来完全像「这一笔的数据」。 验证(本仓 `frontend/` 无测试框架,`npm run ts:check` 有 289 条历史遗留基线, 命令恒 exit 2 ⇒ 只能同机前后差分): - 改后 289 条,与基线**逐字节相同**;两个被改文件里 0 条。 - 前后端 URL 契约静态对照:后端 `@RequestMapping("/payment/collection/order")` + `@GetMapping("/{outTradeNo}/refunds")` ↔ 前端 `/payment/collection/order/${outTradeNo}/refunds`。 - ★ 模板**未被渲染验证** —— vue-tsc 只查表达式类型,不证明浏览器里画得出来。 这一步留给最终验收。
  • 851d622ada feat(乐收赢): 订单详情补退款单端点,回调轨迹纳入退款行的 trace 用户 2026-09-29 报的两个症状,这是后端的另一半: 订单状态写着「已全额退款」,可详情里看不到任何退款;回调轨迹只有支付那四条。 第二个症状**是结构性的**,不是没渲染:`notifyLogs` 只按订单自己的 pay_trace_no 过滤,而退款回调行的 trace 是**退款单的** trace ⇒ 那些行在这一版查询下**永远** 取不出来,不管前端怎么画。改成 trace 集合(订单 trace ∪ 每笔退款的 refund_trace_no)。 新增 GET /payment/collection/order/{outTradeNo}/refunds: - 权限串**复用** `module_payment:leshouying:order:list`,与详情/轨迹两个端点同一个 ⇒ 菜单 SQL 不用改、不用重发授权(加上它是第 4 个复用同一串的端点, `grep -c` 该串在本控制器内 = 4)。 - 按 out_trade_no **等值**过滤并按 created_time 倒序。刻意**不用**退款列表那条 `LIKE keyword` 腿代替:LIKE 给的是「单号包含本单号」的退款单,订单号互为前缀时 两者会给出不同的行,且客户端收敛不回去。 - 订单不存在时走 requireOrder 的 404,与详情端点同一行为。 两个实现细节值得一提,都会**静默**出错: - traces 为空时先返回空列表再查。`IN ()` 是非法 SQL —— 空集合直接发下去是数据库 报错,不是空结果。(真到这一步的订单就是「没有退款也没 trace」的建单失败单。) - `in(...)` 在 MyBatis-Plus 里渲染成**多个独立占位符**(IN (#{...1},#{...2})), 不是把一个集合塞成一个参数。测试里按集合去找绑定值会找不到 —— 本类第一版 5 条挂了 3 条,就是这个。辅助方法先调 getSqlSegment()(该调用才填充 paramNameValuePairs)再解析占位符名。 两向变异(还原后 grep -rn MUTANT 无残留): - notifyLogs 里 in(...) 改 eq(...) ⇒ 两条轨迹用例红 - refundsOf 里去掉 out_trade_no 等值条件 ⇒ 作用域用例红 LeshouyingOrderControllerTest 5/5 绿(本控制器此前**没有任何**测试类); LeshouyingAdminWiringTest 6/6 绿;全量 916 绿。
  • Zobrazit porovnání pro tyto 22 revize »

před 2 týdny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 06c70a78b1 fix(系统): 角色授权保存 500 —— 批量插入的内层 foreach 少了逗号 RoleMapper 两条批量插入注解里,外层 foreach 有 separator=',',内层没有: VALUES <foreach roleIds separator=','><foreach menuIds>(...)</foreach></foreach> 于是一个角色配 N 个菜单生成的是 `VALUES (?, ?) (?, ?) ...` —— 元组之间没有逗号。 MyBatis-Plus 的租户行拦截器拿 JSqlParser 解这条 SQL 时抛 `MybatisPlusException: Failed to process, Error SQL: INSERT INTO sys_role_menus ...`, /system/role/permission/setting 整个 500。线上 2026-09-28 11:48 实测(一个角色 28 个菜单)。 只配**一个**菜单时 SQL 恰好合法(单元素组不需要逗号),所以这条缺陷从 00e98dc(2026-05-30 Python→Java 平移)原样躺到今天;而权限抽屉此前读不到已授权的菜单(1bdaa59 才修好), 也没人能真的点到保存。两条腿今天才凑齐 —— 缺陷何时进来的由 `git log -S "<foreach collection='menuIds' item='menuId'>"` 定死:只有 00e98dc 加、本提交删。 RoleMapperSqlTest 用 MyBatis 自己的 XMLLanguageDriver 求值注解里的 script,再把结果交给 CCJSqlParserUtil —— 与 MyBatis-Plus 租户拦截器同一支解析器。RoleServiceTest 把 RoleMapper 整个 mock 掉,「注解里的 SQL 长什么样」这类缺陷在它眼里完全不可见。 两向读数: - 修前 3/3 红,报文就是线上那条 `VALUES (?, ?) (?, ?) (?, ?)`;两角色那条臂显示块之间的 逗号**在**,缺的只有内层 ⇒ 根因定位到内层 foreach,不是外层 - 单量探针:JSqlParser 对缺逗号那条抛 `Encountered unexpected token: "("`,对有逗号那条 PARSED-OK(正对照,证明这支尺不是在乱拒) - 修后全量 893 绿(此前 890) insertRoleDepts 是同一处缺陷(data_scope=5 且多部门时同样 500),一并修。

před 2 týdny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 3a9a655eac chore(系统): module/system 授权事后核对 SQL 交付件(只读,待用户执行) 用户贴回 §0 两个读数后,出现了无法从读数本身判定的分叉:回复里那句「第三个无数据」 有两种读法而结论相反 —— 整个授权文件已从头跑到尾(§3 返回 0 行 = 通过), 或只跑了 §0(那就不存在第三个结果集)。§3 返回 0 行既可能是「已执行且正确」, 也可能是「这条查询根本没跑到」,两者读数一样。 所以用只读读数定死:① 角色 1 现在持不持有 id 2 与两个新权限点 ② §1 那两个权限点 在不在 sys_menu ③ §2a 的快照表在不在。三条一起看,执行过没执行过不会混。 另加 ④ 祖先目录检查,这条与 §3 是两回事:§3 只核权限串,不核菜单渲不渲染。 依据 ⓪-B 实测,角色 1 持有 242/243 而**不持有它们的父节点 id 2**(系统管理目录), 按 UserService#buildMenuTree(662 行)「父节点不在授权集合里的行静默丢弃」的写法, 今天只要有人给非超管账号分配角色 1,那个人的侧边栏里整个系统管理都不会出现。 admin 是超管走 getAllMenusTree(58-59 行)所以从未暴露。④ 顺带核 id 2 的 status: 若为 '1'(软删除)则授了也不渲染,得单独处理。 只读性配了正对照:同一把尺(行首写操作关键字)在本文件 0 命中, 在 sys-authz-grant-module-system.sql 命中 3;放宽到全文任意位置分别是 0 与 5。 本文件只读,执行由用户做。
  • b8b1733983 chore(系统): module/system 授权配套 SQL 交付件(写数据,待用户执行) 2a92d65 给 84 个端点挂上守卫后,权限串必须有人持有,否则守卫 = 锁人。 清点读数(sys-authz-inventory-readonly.sql ①②)显示 73 条核心串一条都没授给任何角色, 角色 1「管理员角色」只持有 2 个 module_system 菜单。所以补齐守卫与补齐授权必须成套, 且**授权先跑、后端后部署** —— 反过来的话,新后端上线到授权跑完之间,除超管外的账号 全被挡在系统管理之外,包括分配权限那个端点本身,自己把自己锁在门外。 §0 只读核对。其中 ⓪-A 会列出持有角色 1 的账号与全部超管:若角色 1 被租户账号持有, 把整个 module_system 授给它等于把平台管理权交给那个租户,得换一个角色授。 ⓪-B 打印角色 1、2 当前完整的已授权菜单行(含目录),留一份供回滚比对 —— ② 那张表只看了 permission LIKE 'module_system:%' 的行,看不到祖先目录授没授。 §1 补 module_system:invitation:create / :delete 两个权限点。前端 module_system/invitation/index.vue 用这两个串(第 15/16 行的 perm-create/perm-delete、 第 120 行 v-hasPerm),而库里只有 :list(id 243)。不补行的话这两个端点对非超管永久 403,按钮也永远不出现。列形照 har-menu.sql 的权限点段。 §2 把 module_system 全量菜单 + 全部祖先目录授给角色 1,递归取闭包。祖先必须一起授: UserService#buildMenuTree 对父节点不在授权集合里的行是静默丢弃,只授子节点菜单看不见。 幂等(NOT EXISTS 挡重复)。角色 2「租户角色」本次不动:它现有 5 个菜单对应的端点在 payment 模块的 /payment/api-key,不在本批 84 个守卫里。 §3 回读核对,应返回 0 行。清单是 76 个权限串,从 SystemEndpointAuthzTest.java **机械提取**(不手抄),生成脚本 logs/_gen-grant-sql.py 写完再反读比对, SQL 里 76 条 == 测试文件 76 条逐条相同。判据按「串」不按「行」:同一个串可能挂在 多行 sys_menu 上(如 dept:query 同时在 id 12 和 id 47),持有任意一行即满足。 §4 回滚:§2a 先建一次性快照表 _bak_role_menus_role1,回滚即删掉不在快照里的角色 1 行。 本文件是写数据的,与 2a92d65 一样未经执行 —— 执行由用户做。
  • 2a92d65dd9 feat(系统): module/system 84 个端点补方法级授权,附端点→权限串钉子测试 这批端点从 Java 平移起一个 @PreAuthorize 都没有:SecurityConfig 的授权规则只有 .anyRequest().authenticated()(仅认证),全 main 树没有授权用的 HandlerInterceptor 也没有 @Aspect,而 Python 版每条路由都挂 AuthPermission([...])。后果不是泛泛的 「权限不足」,是一条能走通的路径:sys_role / sys_menu / sys_role_menus 在 TenantInnerInterceptor 的 IGNORE_TABLES 里(不加租户谓词),所以任意已登录账号 (含普通租户账号)可以直接 PATCH /system/role/permission/setting 改写任意角色的授权 DELETE /system/role/delete 删掉平台自己的角色 84 个端点挂 @perm.hasAny('<权限串>'),18 个端点有意豁免并显式列出: 登录入口 10 个(SecurityConfig WHITE_LIST)、/system/notice/available、 /system/param/info、/system/user/current/info(三者同样在白名单)、以及 /system/user/register、/forget/password(白名单)与三个自助接口 (current/password/change、current/info/update、current/avatar/upload)—— 让用户改自己的密码还要 user:update 是错的。 钉子测试 SystemEndpointAuthzTest 用反射把整张「端点→权限串」表钉死,102 行。 它挡三件事:漏挂、串写错、以及**新端点**(表里没有就红 —— 新端点默认是开放的, 必须有人显式决定它要哪个串)。串写错是静默的:PermissionCheck 对 ROLE_SUPERUSER 直通,所以拿超管跑的用例一条都测不出写错的串,这处独立核对不能省。表里同时钉住 整个 SpEL 形式(@perm.hasAny('...')),hasAll 或 bean 名写错一样红。 两向变异(sha256 指纹 + 还原复核 5/5 逐字节相同): - 删掉 role:permission 的守卫 ⇒ 红,失败信息点名 PATCH /system/role/permission/setting - 把 user:query 打成 user:quer ⇒ 红,点名 GET /system/user/list - hasAny 改成 hasAll ⇒ 红,点名 GET /system/menu/tree - 新增一个没有守卫的 /system/log/probe ⇒ 红,keySet 断言点名该端点 - 只加注释 + 一个非映射私有方法 ⇒ 仍绿(证明它没把非端点方法误当端点) 全量 890 绿(此前 888,+2 即本测试)。 ## 部署顺序(重要) 配套授权 SQL java/src/main/resources/db/sys-authz-grant-module-system.sql 必须在**部署本提交之前**执行。反过来的话,新后端上线到授权跑完之间,除超管外的 账号全被挡在系统管理之外,包括分配权限那个端点本身 —— 自己把自己锁在门外。 该文件 §0 是只读核对,先跑它,其中一个读数是「角色 1 是否被租户账号持有」, 那决定授权该授给谁。
  • a5f2c9e7b8 chore(系统): module/system 方法级授权清点 SQL 交付件(只读,待用户执行) 起因:查「角色权限抽屉全不勾」时量到 module/system/** 12 个 controller、 102 个端点,@PreAuthorize 出现 0 次;SecurityConfig 的授权规则只有 .anyRequest().authenticated(),全 main 树也没有授权用的 HandlerInterceptor 或 @Aspect。Python 版每条路由都挂 AuthPermission([...])(role 8/8、dict 16/16、 params 10/10……只有登录那批是 0)⇒ 平移时掉的,不是设计选择。 后果是具体路径不是泛泛的「权限不足」:sys_role / sys_menu / sys_role_menus 等表在 TenantInnerInterceptor 的 IGNORE_TABLES 里(不加租户谓词),所以普通 租户账号登录后可直接 PATCH /system/role/permission/setting 改写任意角色的 授权、或 DELETE /system/role/delete 删掉平台自己的角色。 为什么先清点而不是直接补注解:权限串若在 sys_menu 里不存在、或只存在于 status='1' 的行(@TableLogic 判成已删、进不了菜单树也就没法被勾选),就没有 任何角色能持有它 ⇒ 补上注解只会把非超管管理员锁在角色/用户管理之外。 超管不受影响(PermissionCheck 对 ROLE_SUPERUSER 直通),所以用超管跑的用例 一条都测不出这个缺陷。 三个结果集:① 现存的 module_system:% 权限串(含 status)② 各自授给了哪些角色 ③ 期望清单里库中没有活行的串。期望清单 73 条由 `366311e^` 的 controller 机械 提取(不手抄),与 Python 版逐路由的 AuthPermission 一一对应。 只读性做了两向量:本文件行首写操作关键字 0 命中;同一把尺量 har-menu.sql 命中 5 ⇒ 是有对照的负读数,不是检索式造的。
  • 1bdaa59d5c fix(系统): 角色详情带上已授权菜单/部门,权限抽屉此前全不勾且保存会清空授权 RoleService#toVO 自 Java 平移起就把 menus / depts 写死成空数组,而前端权限 分配抽屉一直读 detail.menus[].id —— 那个形状不是随便定的,Python 版 RoleOutSchema.menus 就是 list[MenuOutSchema](lazy="selectin")。 后果不止是「显示全未勾选」:抽屉保存走的是先删光再插入 (RoleMapper.deleteRoleMenusByRoleIds → insertRoleMenus),所以 「打开抽屉、什么都没改、点确定」会把该角色的授权**清空**。 改法: - RoleMapper 新增 selectMenusByRoleId / selectDeptsByRoleId,条件里带 status='0'。抽屉那棵树来自 MenuService#getMenuTree(MenuEntity.status 挂 @TableLogic,树里只有活着的菜单),这里不跟着过滤就会回勾一批树里没有的 id(Element 静默忽略),保存时又被删掉:读到的与写回去的对不上。 - RoleVO.menus 由 id 列表改成 List<MenuVO> —— 前端取的是 .id,纯 id 数组上 取 .id 全是 undefined,树同样全不勾。depts 用 List<Map>,键名由 SQL 别名 钉成 id / name / parent_id(Map 的键不会被 Jackson 的 SNAKE_CASE 改写)。 - 两个字段只填在 getDetail。列表/分页不查:那是每行两次查询的 N+1, 而抽屉只用得到 detail,由 RoleServiceTest#getList_doesNotLoadMenusPerRole 钉住。 测试 RoleServiceTest 两条,断言走**线上那支 ObjectMapper**(JacksonConfig, SNAKE_CASE)序列化后的 JSON —— 前端读的是键名(parent_id / data_scope), 不是 Java 字段名。 两向读数:getDetail 里去掉那两行赋值 ⇒ 红(menus=[] ,expected 1 but was 0); 按 sha256 3eca6f83… 还原 ⇒ 全量 888 绿(原 886 + 本类 2 条)。 管理端角色页无需重新登录:JwtAuthFilter 每请求重载权限,本次也没改权限串。
  • Zobrazit porovnání pro tyto 152 revize »

před 2 týdny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • b5d6a60edf fix: 修复全库时间列漂移 - pgjdbc 读 timestamp 挂 UTC 导致每次 updateById 漂 +8h 根因:timestamp without time zone 与 OffsetDateTime 的映射不对称。 读侧把库里的墙钟当成 UTC 瞬间读出来,写回时又按会话时区归化, 于是每「读出 → updateById 写回」一次,时间列就 +8 小时。 - ShanghaiOffsetDateTimeTypeHandler:读侧按 getColumnTypeName() 分流。 裸 timestamp 走 LocalDateTime 再挂 +08;timestamptz 交回驱动原生映射 (getObject 向 timestamptz 列要 LocalDateTime 会直接抛 PSQLException)。 ★ 分辨只能靠类型名:getColumnType() 对两者都返回 93, 写成 == Types.TIMESTAMP_WITH_TIMEZONE 的分支编译得过、永不成立、也不报错。 写侧发裸 LocalDateTime 字面量,实测对两类列都正确。 - MyBatisPlusConfig:走 ConfigurationCustomizer 显式注册。不能用 type-handlers-package 扫包 —— 同包 JsonbTypeHandler 标着 @MappedTypes(String.class),会被扫成全局 String 处理器,波及全库。 - application.yml:druid connection-init-sqls 把会话时区钉成 Asia/Shanghai, 写侧的正确性押在它上面。 - db/timezone-drift-report.sql:只读受损清单。含 timestamptz 正对照 —— 同一查询在会话 +08 与 UTC 下读数会平移,用于排除会话造成的假阳性。 - db/timezone-drift-repair.sql:一次性数据修复。含备份表、行数守卫、回滚, 每个写操作后跟 COMMIT(缺 COMMIT 会让整笔改动随连接消失,而当场自查读到 0)。 ★ 执行顺序是先发版、后修数据。 实测受损 16 表 / 328 行,漂移量恒为 8 小时的整数倍(n 跨 1..277)。 测试 119 绿。

před 3 týdny

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • dbe364ceb7 feat: 对卡转账统一固定T0到账时效 - 后端强制T0不再由调用端指定(含前端单笔/批量、openapi),前端单笔移除时效选择,文档同步

před 1 měsícem

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • 8c6e6f487d feat: 批量付款LOGON_ID主体扫码授权制单 - user.info.share取2088 userId(应用未开通open_id实证改用user_id)+ 授权回调复用白名单aplipay/auth按参数分流
  • d5da56d84f Revert "feat: 授权查询暴露 authUserInfo + openId" This reverts commit c556b25a6d5e3f15949e21ae8d2439a021742944.
  • c556b25a6d feat: 授权查询暴露 authUserInfo + openId - BatchSubjectService.query 返回 Map<String,Object>, 扩展 open_id + auth_user_info(BankAuthUserInfo 5 字段判空, Map.of 不支持 null); 未授权分支返回结构一致(open_id 空串 + 空对象), 前端无需分支防御 - 授权列表 AUTHED/NORMAL 行新增「授权详情」按钮, dialog 重点渲染 OpenID 高亮块 + 授权用户信息(支付宝账号/昵称/姓名/身份证脱敏/外标卡号) - queryAuthorize 前端类型 AuthorizeQueryResult 对齐后端契约 - 新增 query_returnsAuthUserInfoAndOpenId 用例, 3 个存量 query 用例改签名
  • a98227bcca fix: 批量付款三个 tab 联动问题 - 主体下拉切 tab 重载: BatchPayCreate 暴露 active prop + watch, 切到批量制单 tab 时重新请求 authorize/list, 授权完成后立即可选 - 详情刷新联动列表: BatchPayDetail 刷新/关闭后 emit refresh, index.vue batchListKey++ 重挂载列表 - 切 tab 关闭详情: watch activeTab 清空 detailBatchNo, 详情不再残留 - AuthorizeList 表单新增主体类型下拉(ALIPAY_USER_ID/LOGON_ID/OPEN_ID), 列表主体账号列显示类型小字, 主体名称提示须与实名一致
  • 95ca793a6a feat: 授权申请主体类型可选 + 雪花ID序列化修复 - participant_id_type 可选: ALIPAY_USER_ID(默认)/ALIPAY_LOGON_ID/ALIPAY_OPEN_ID, 空值回落默认, 非法值 400 拦截; rebind 复用原申请类型 - BatchAuthorizeEntity.getId 覆写 @JsonSerialize(ToStringSerializer): 19位雪花ID超 JS 安全整数致前端精度丢失(后两位变0), rebind 回传错误 id 404 - batch-account-level.sql 补 DROP NOT NULL: 去企业化后实体无 enterprise_id, INSERT 不含该列, 原 NOT NULL 无默认值致授权/批次落库失败
  • Zobrazit porovnání pro tyto 15 revize »

před 1 měsícem

alphah odeslal do větve java v repositáři gatsby/payment-platform

  • c5431230e3 fix: WAIT_PAY 批次可二次支付/可关闭 + 关闭失败回写真实状态 用户实测: ①本地被定时同步成 WAIT_PAY 后支付/关闭按钮消失(v-if 仅 INIT) ②关闭批次报 BATCH_ORDER_STATUS_INVALID(支付宝侧 INVALID=明细全部无效,SDK 注释实证) - renderPay/batchClose 守卫放行 INIT+WAIT_PAY(未支付即可重新生成链接/可关闭) - batchClose 失败时 detail.query 回写真实状态(避免本地残留 INIT 反复点关闭) - 前端按钮 v-if 放行 INIT+WAIT_PAY,状态文案增 WAIT_PAY=等待支付 / INVALID=已失效 测试: 59/59 Service 全绿(+3 用例,M3 断言文案同步更新)

před 1 měsícem