From 78fabd15a5943d2db7b75baa81ab61e03cade610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Wed, 23 Sep 2026 15:06:20 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E9=87=91=E8=9D=B6?= =?UTF-8?q?=E7=A5=A8=E6=B1=A0=E9=80=89=E7=A5=A8=E4=B8=9A=E5=8A=A1=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=EF=BC=88=E5=8F=AF=E7=94=A8=E8=BF=87=E6=BB=A4=E3=80=81?= =?UTF-8?q?=E6=B8=B8=E6=A0=87=E8=A1=A5=E9=A1=B5=E4=B8=8E=E5=9B=BA=E5=AE=9A?= =?UTF-8?q?=E6=9F=A5=E6=9C=AA=E7=94=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 同步2026-09-23选票方案演进:可用过滤口径、有界循环补页与nextCursor游标分页机制、 单据使用状态收敛为后端固定查未用、保存链路新增可用性复核步骤、前端弹窗条件与分页交互调整。 --- ...像(收票登记+结算单认领+按票号补查+统一组件)业务流程.md | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/doc/docs/业务流程/2026-09-22-金蝶票池选票统一查镜像(收票登记+结算单认领+按票号补查+统一组件)业务流程.md b/doc/docs/业务流程/2026-09-22-金蝶票池选票统一查镜像(收票登记+结算单认领+按票号补查+统一组件)业务流程.md index 8e093636..839e5107 100644 --- a/doc/docs/业务流程/2026-09-22-金蝶票池选票统一查镜像(收票登记+结算单认领+按票号补查+统一组件)业务流程.md +++ b/doc/docs/业务流程/2026-09-22-金蝶票池选票统一查镜像(收票登记+结算单认领+按票号补查+统一组件)业务流程.md @@ -4,7 +4,7 @@ > 对接系统:金蝶发票云旗舰版·票易通(piaozone)全票池 A801-1,经业财融合 BFI 网关(rim 契约)透传 > 关联文档:`2026-09-20-收票管理同步金蝶全票池发票(A801-1查询+批量同步+定时绑定关联)业务流程.md`(同步链已整体退役,仅存推送/回写侧仍有效的键与端点见该文档退役声明)、`2026-09-22-收票单推送金蝶全链路业务流程(A303-2保存-A302-6指定应付-A801-3预检回写).md`(图05 推送链路,镜像字段 invoice_type_code/items_json 由其取数消费) > 设计依据:`docs/superpowers/specs/2026-09-22-invoice-pool-remote-reversal-design.md` -> 最后更新:2026-09-22(本文档原为「金蝶票池选票统一查镜像」,同日二次决策改回实时查询后整篇重写;文件名保留以免断链) +> 最后更新:2026-09-23(选票可用过滤 + 有界循环补页游标分页 + 单据使用状态收敛为后端固定查未用;2026-09-22 本文原为「金蝶票池选票统一查镜像」,同日二次决策改回实时查询后整篇重写;文件名保留以免断链) --- @@ -14,7 +14,9 @@ **定稿口径**: -- 收票登记新增、正式结算单认领两处选票改为**实时查 A801-1 全票池**;A801-1 无总数,采用"多取 1 条探测 hasMore"的**游标式下一页翻页**(无跳页能力)。 +- 收票登记新增、正式结算单认领两处选票改为**实时查 A801-1 全票池**;A801-1 无总数,翻页采用"多取 1 条探测 hasMore + 有界循环补页 + 扫描游标"的**游标式下一页翻页**(无跳页能力,机制详见 §2)。 +- (2026-09-23)选票**只出可用票**:金蝶返回行须同时满足 剩余可用金额 `availableAmount > 0` 且 金额使用状态 `amountUseStatus = "0"`(未使用,2-全部使用)。A801-1 无该维度的服务端过滤条件,只能拉取后客户端过滤,由此引入补页与游标(§2)。 +- (2026-09-23)**单据使用状态**不再作为前端筛选条件,后端固定下推"未用"(`expenseStatus = "1"`)。 - `blade_kingdee_invoice_pool` 语义降级为**登记存根**:只存被业务选中的发票,写入时机=**收票登记保存时服务端重查金蝶落库**(否决"选中即写")。 - 全量同步体系**退役删除**:定时任务、增量窗口、镜像批量 upsert、定时绑定(receiptMap)、漏绑补偿、白名单过滤(票种 + 客商税号 AND,客商建档不再是选票前置)、手动全量同步端点与"立即同步"按钮、按票号补查(语义并入主查询)。 - 金蝶不可用时查询与保存重查均**直接报错**(镜像不再提供读服务,无降级路径——接受"每次开弹窗/每次保存打一次金蝶"的一次性成本,换取同步体系归零)。 @@ -30,11 +32,13 @@ - 已删除端点:`POST /*/invoice-pool/fetch`(按票号补查,语义并入主查询的发票号码条件)、`POST /invoice-receipt/sync-invoice-pool`(手动全量同步)。 - **保留端点**:`POST /invoice-receipt/sync-kingdee`(列表"批量同步",职责收敛为刷新**已登记票**的镜像行与状态,镜像行此时必已存在)。 -**查询条件**(`KingdeeInvoicePoolQueryDTO`):`current/size`(size 钳位 1~100,透传为 A801-1 pageSize=size+1 探测)、`invoiceNo`、`invoiceDateStart/End`(服务端强校验 yyyy-MM-dd)、`salerTaxNo/buyerTaxNo`、`invoiceType`(票易通编码,如 26/27,选项取 `BfiConstant.INVOICE_TYPE_NAMES`)、`expenseStatus`(1 未用/30 在用/60 已用/65 已入账)。**删除**了镜像时代的 `salerName/buyerName/claimed/registered` 条件(远程不支持名称模糊;本地状态不可下推)。 +**查询条件**(`KingdeeInvoicePoolQueryDTO`):`current/size`(size 钳位 1~100)、`invoiceNo`、`invoiceDateStart/End`(服务端强校验 yyyy-MM-dd)、`salerTaxNo/buyerTaxNo`、`invoiceType`(票易通编码,如 26/27,选项取 `BfiConstant.INVOICE_TYPE_NAMES`)、`cursor`(2026-09-23 新增,下一页续扫游标)。`expenseStatus` 已从前端条件删除,后端固定下推"未用"(`KingdeeBfiConstant.POOL_EXPENSE_STATUS_UNUSED`)。**删除**了镜像时代的 `salerName/buyerName/claimed/registered` 条件(远程不支持名称模糊;本地状态不可下推)。 -**A801-1 统一查询口径**:所有出站查询一律携带 `invoiceStatus = "0"`(正常票);返回行按 `invoiceNo` trim 全等防御过滤后再截断 size 条(防接口模糊匹配语义假命中)。 +**A801-1 统一查询口径**:所有出站查询一律携带 `invoiceStatus = "0"`(正常票)与 `expenseStatus = "1"`(单据使用状态未用,后端固定;按票号单查 `queryPoolByInvoiceNo` 不下推 expenseStatus);返回行先做**选票可用过滤**(`isUsablePoolRow`:票号非空 + 剩余可用金额 `availableAmount > 0` + 金额使用状态 `amountUseStatus = "0"`,可用金额空白/解析失败按不可用兜底;口径常量集中在 `KingdeeBfiConstant`),再按 `invoiceNo` trim 全等防御过滤(防接口模糊匹配语义假命中),最后截断 size 条。 -**返回体**(`KingdeeInvoicePoolPageVO`):`{ records: List, current, size, hasMore }`——**不再合成 total**。行映射:票种编码→中文名、税率小数转百分数(顶层优先、回退明细首行)、`invoiceAmount = availableInvoiceAmount = matchedAmount = totalAmount`、`attachmentJson = {source:'piaozone', path:downloadUrl, name:票号.pdf}`;`id` 置空(未入镜像)。 +**有界循环补页分页(2026-09-23)**:可用过滤发生在拉取之后,单页远程返回可能大部分被过滤,因此 `invoicePoolPage` 在单请求内循环补页凑满一页——首拉 `size+1` 轻量探测,不足再按契约上限 500 行/次补拉,单请求最多 3 次远程调用(`POOL_MAX_REMOTE_CALLS`,过滤率过低时以此封顶防请求无限拉长),仍不足则返回稀疏页。远程短页(返回行数 < 请求数)即耗尽信号;`hasMore = 未耗尽 或 窗口内还有剩余过滤行`。**翻下一页必须回传上页 `nextCursor`**(格式"远程页码:已消费过滤行数",游标页码按 500 行窗口定位),否则按页码锚定会漏行/重行;游标解析失败自动降级为页码锚定,不阻断查询。 + +**返回体**(`KingdeeInvoicePoolPageVO`):`{ records: List, current, size, hasMore, nextCursor }`——**不再合成 total**;`nextCursor` 为下一页续扫游标,null 表示没有更多。行映射:票种编码→中文名、税率小数转百分数(顶层优先、回退明细首行)、`invoiceAmount = availableInvoiceAmount = matchedAmount = totalAmount`、`availableTaxAmount = availableAmount`(2026-09-23 新增"剩余可用金额"展示列,金蝶可用税额口径,**不改** `availableInvoiceAmount` 的价税合计口径——认领金额校验依赖该口径)、`attachmentJson = {source:'piaozone', path:downloadUrl, name:票号.pdf}`;`id` 置空(未入镜像)。`BfiInvoicePoolVO` 同步新增 `amountUseStatus` 字段承接金蝶返回。 **claimed/registered 纯展示装饰**:对本页票号批量查 `blade_formal_settlement_invoice`(关联主单未删且非 voided)与 `blade_invoice_receipt`(未删且非 voided),内存打标;只读展示,**不落库、不进查询条件**。 @@ -44,10 +48,13 @@ ``` ① 按 invoiceNo 调 queryPoolByInvoiceNo 远程 A801-1 精确单查(含票号全等防御) -② 未命中或 delete="1" → ServiceException("金蝶票池未查询到发票 X 或已作废,不能保存"),保存中止 +② 未命中或 delete="1" → ServiceException("金蝶票池未查询到发票 X,不能保存"),保存中止 (选中→保存竞态:票被删/红冲在此拦截;不主动作废既有登记,与列表同步口径一致) -③ 命中 → fillInvoicePool 构建/刷新镜像行(保留图05 的 invoiceTypeCode/itemsJson 映射) -④ 保存/更新镜像行 → receipt.kingdeeInvoicePoolId = mirror.id, +③ 命中后按选票同口径 isUsablePoolRow 复核可用性(2026-09-23 新增): + 剩余可用金额为 0 或金额使用状态已变更 → ServiceException("发票 X 剩余可用金额为0或金额使用状态已变更,不能保存") + (防选票到保存之间票被他人认领/使用造成超用;明细补查 fetchMirrorItemsFromPool 不做该过滤) +④ fillInvoicePool 构建/刷新镜像行(保留图05 的 invoiceTypeCode/itemsJson 映射) +⑤ 保存/更新镜像行 → receipt.kingdeeInvoicePoolId = mirror.id, 回填 kingdeeBillNo = serialNo、kingdeeStatus ``` @@ -56,9 +63,9 @@ ## 4. 前端统一组件(`src/components/kingdee-invoice-pool-dialog/main.vue`) - Props:`modelValue / title / confirmText / multiple / initialInvoiceNo / queryApi`(`fetchApi/syncApi` 已删除);事件 `confirm(rows)`(单选 `[row]`、多选选中数组)。打开时重置查询、按 `initialInvoiceNo` 预填、清勾选、回第 1 页加载。 -- 查询表单六条件:发票号码、开票日期区间、销方税号、购方税号、发票类型(编码下拉)、单据使用状态;查询/重置按钮在加载期间禁用。 -- 表格:票面列 + **认领状态/登记状态**两个 tag(仅展示);"金蝶同步"列已删除(远程票恒在池中无意义);行主键用发票号码兜底(`rowKey = invoiceNo || id`,远程票 id 恒空);单选 radio/双击直选,多选 `reserve-selection` 跨页保留。 -- 分页区:**游标式**——"上一页/下一页" + "第 N 页" + 每页条数(10/20/50/100);`hasMore=false` 禁用下一页,第 1 页禁用上一页,加载中全部禁用;查询条件变更或换页大小即回第 1 页;翻页请求失败时页码回滚到发起前值(不停错页)。 +- 查询表单五条件(2026-09-23 删除"单据使用状态",后端固定查未用):发票号码、开票日期区间、销方税号、购方税号、发票类型(编码下拉);查询/重置按钮在加载期间禁用。 +- 表格:票面列 + **剩余可用金额**列(2026-09-23 新增,`availableTaxAmount`)+ **认领状态/登记状态**两个 tag(仅展示);"金蝶同步"列已删除(远程票恒在池中无意义);行主键用发票号码兜底(`rowKey = invoiceNo || id`,远程票 id 恒空);单选 radio/双击直选,多选 `reserve-selection` 跨页保留。 +- 分页区:**游标式**——"上一页/下一页" + "第 N 页" + 每页条数(10/20/50/100);**下一页请求回传上页 `nextCursor` 续扫**(新查询/换页大小/重新打开自动重置游标与页缓存);**上一页直接回显已浏览页缓存(`pageCache`),不再发请求**;`hasMore=false` 禁用下一页,第 1 页禁用上一页,加载中全部禁用;查询条件变更或换页大小即回第 1 页;翻页请求失败时页码回滚到发起前值(不停错页)。 - 接线:收票登记表单(`multiple=false`,回填不再取 `id`,金蝶单据号取 `serialNo`、状态固定显示"未同步",保存落库后由后端刷新);结算单编辑器(`multiple=true`、`confirm-text="确认认领"`,`handleInvoiceClaimConfirm` 把复数 `attachmentsJson` 归一为认领契约单数 `attachmentJson`)。 - 翻页期间票池数据变动:游标页可能重复/漏行(组件按发票号码做行主键与选择去重,结算认领确认前还有票号集合查重);最终一致性以保存/提交时服务端重查为准,无资金风险。 @@ -68,8 +75,9 @@ ## 6. 已知限制与验证 -- 每次打开弹窗、每次保存都是一次金蝶远程调用(预期成本);金蝶不可用期间选票/收票保存均不可用。 -- `expenseStatus` 仅作查询条件下推 A801-1,响应回传与否不影响 claimed/registered 本地装饰。 +- 每次打开弹窗(最多 3 次补页调用)、每次保存都是金蝶远程调用(预期成本);金蝶不可用期间选票/收票保存均不可用。 +- 单据使用状态固定后端下推"未用"(前端无此条件);选票可用过滤(`availableAmount > 0` 且 `amountUseStatus = "0"`)在拉取后进行,过滤率过低时单请求最多补页 3 次即返回稀疏页,靠 `hasMore + nextCursor` 继续翻页。 +- 下一页依赖前端回传 `nextCursor`;上一页走前端缓存回显(0 请求),缓存页为浏览时快照,票池实时变动最终以保存/提交时服务端重查为准。 - 无表结构、无菜单 SQL 变更;Nacos 三份模板中 `thirdParty.bfi.invoiceReceipt.*` 同步相关键已随任务删除(图05 `sync-kingdee`/推送链使用的键保留)。 - 后端验证:`mvn clean compile -DskipTests -pl blade-service/blade-transport -am` + `mvn test-compile` 通过;前端 `pnpm run build` 通过;退役符号两仓库 grep 归零。 - 手工回归清单与过程记录:`.superpowers/sdd/2026-09-22-invoice-pool-remote-reversal/`(各任务简报/报告/评审包)。