You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tauri-client/call-client/TaxerInfo-tauri-v1-migratio...

538 lines
18 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# TaxerInfo 模块改写为 Tauri v1单窗口 + Vue设计文档
## 1. 文档目标
`CallClient/WPF/TaxerInfo.xaml.cs` + `CallClient/WPF/TaxerInfo.xaml` 对应功能迁移到 **Tauri v1 + Vue单窗口** 架构,确保:
- 业务行为与现有 WPF 一致(取号、开始办理、办结、实名信息、企业列表、网页加载、复制、查询)。
- 桌面能力由 TauriRust提供界面由 Vue 负责。
- 后续可以逐步替换原 `WebBrowser` 内嵌业务页,最终统一为前端组件化实现。
---
## 2. 现有模块功能盘点WPF
## 2.1 核心职责
`TaxerInfo` 窗口当前承担:
1. 展示办税员实名信息(身份证图、头像、姓名、手机号、采集状态)。
2. 展示当前取号下的企业/票据列表,支持“开始”“办结”“选择企业”。
3. 根据当前票据拼接 URL 并加载纳税人页面(`WebBrowser`)。
4. 提供操作按钮:复制税号、复制号码、刷新、一户式查询。
5. 展示“代理人办理企业清单”(通过 HTTP 接口获取)。
## 2.2 主要数据模型
- `ticket`:票据/办理对象(状态、企业信息、号票信息等)。
- `Enterprise`:代理人办理企业清单项。
- `SerialNumber`
- `EnterpriseName`
- `TaxpayerId`
- `ApiResponse / DataItem``today-enterprises` 接口返回结构。
- 外部响应:
- `SmzJhxtGetBsyxxByqhhmResponse`
- `SmzJhxtGetSmzcjxxResponse`
- `SmzJhxtGetBdNsrxxResponse`
## 2.3 关键流程
1. `LoadInfo(comparecode)` 触发加载:
- 调实名接口、绑定纳税人接口、绑定企业接口;
- 查询当日关联票据列表;
- 填充 UI。
2. 票据切换(`SelectedSubTicket`
- 更新“当前办理文案”;
- 刷新纳税人页面 URL。
3. 开始办理(`StartCommand`
- 更新票据状态为办理中;
- 记录窗口/员工信息;
- 刷新右侧页面。
4. 办结(`EndCommand`
- 结束当前票据;
- 浏览器跳转 `about:blank`
---
## 3. 迁移目标架构Tauri v1
## 3.1 技术分层
- **前端Vue**
- 单窗口页面:`TaxerInfoView.vue`
- 状态管理Pinia推荐或 Vue reactive store
- UI 组件Element Plus / Ant Design Vue任选
- **Tauri 后端Rust**
- `#[tauri::command]` 提供桌面能力与系统访问
- 封装对旧服务接口调用(可直接 HTTP 调用)
- 封装票据数据库/SDK 调用(通过 Rust 侧 adapter
- **桥接**
- `invoke()`:前端调用命令
- `event`:推送异步状态(可选)
## 3.2 单窗口布局建议(与 WPF 对齐)
- 左侧(固定宽度)
- 办税员实名信息卡片(身份证图 + 头像 + 姓名 + 手机 + 采集状态)
- 企业/票据列表(开始、办结按钮)
- “选择企业”按钮
- 右侧(自适应)
- 顶部操作栏(当前办理文案 + 四个按钮)
- Tabs
- `纳税人信息``iframe/webview` 或前端重构页
- `代理人办理企业清单`:表格展示 `EnterpriseList`
---
## 4. 接口设计Tauri Command + 业务 HTTP
以下为建议接口(前端调用 Rust command
## 4.1 初始化与加载
### `taxer_load_info(compareCode: String, currentTicketId: String) -> TaxerLoadInfoDto`
聚合返回:
- 办税员实名信息(姓名、性别、身份证、地址、头像、身份证底图)
- 实名采集结果(手机号、备注、是否已采集)
- 绑定纳税人列表(用于税号映射)
- 当日票据列表(`TicketDto[]`
- 默认选中票据
- 代理人办理企业清单(`EnterpriseDto[]`
> 说明WPF 中由 `BackgroundWorker + 多次接口请求 + 本地票据查询` 完成,迁移后建议统一为一个“聚合命令”,减少前端编排复杂度。
## 4.2 票据动作
### `taxer_start_ticket(ticketId: String, windowUid: i64, employeeUid: i64) -> TicketDto`
- 更新开始时间、状态dealing、窗口/员工绑定。
- 返回更新后的票据数据。
### `taxer_end_ticket(ticketId: String) -> TicketDto`
- 标记办结(对应原 `t.Stop(DateTime.Now)`)。
- 返回更新后的票据数据。
### `taxer_batch_end_or_abandon(ticketIds: Vec<String>) -> ()`
- 对应原 `End()` 的兜底逻辑(办理中停止、待办废弃)。
## 4.3 URL 与外部查询
### `taxer_build_nsr_url(ticketId: String, phoneNo: String, compareCode: String) -> String`
根据模板参数替换生成 URL等价 `Analize_URL`
- `{djxh}` `ENTERPRISE_ID`
- `{sfzhm}` 身份证号
- `{sflb}` 企业/个人标识
- `{swjgDm}` 税务机关代码
- `{qhhm}` 取号号码
- `{sjhm}` 手机号
### `taxer_build_yhscx_url(ticketId: String) -> String`
生成“一户式查询”URL。
## 4.4 代理人企业清单
### `taxer_get_today_enterprises(ticketId: String) -> Vec<EnterpriseDto>`
调用:
- `GET {ZIYUN_SERVICE_URL}/agentInfo/today-enterprises/{ticketId}`
返回字段映射:
- `serialNumber -> SerialNumber`
- `serviceTarget -> EnterpriseName`
- `serviceTargetCode -> TaxpayerId`
## 4.5 系统能力
### `system_copy_text(text: String) -> ()`
- 复制号码、复制税号统一复用。
### `system_open_external(url: String) -> ()`
- 一户式查询可在系统默认浏览器打开(可选)。
---
## 5. 前端 Vue 数据结构建议
```ts
export interface TaxerState {
compareCode: string
currentTicketId: string
currentTicket?: TicketDto
selectedSubTicket?: TicketDto
ticketList: TicketDto[]
enterpriseList: EnterpriseDto[]
name: string
phoneNo: string
memo: string
idCardImageBase64?: string
headImageBase64?: string
caijiRegistered: boolean
isLoading: boolean
isStarted: boolean
currentCompanyText: string
nsrUrl: string
}
```
---
## 6. 功能映射表WPF -> Tauri + Vue
- `Caiji_Click`:打开实名采集弹窗 -> Vue Dialog + `taxer_submit_realname`(若后续迁移)。
- `Copy_Click`:复制 `TKT_ID` -> `system_copy_text(currentTicket.tktId)`
- `Copy_Tax_Click`:复制税号 -> 先映射税号,再 `system_copy_text`
- `Reflesh_Click`:浏览器刷新 -> `iframe` 重新赋值 `src` 或 key 强制重渲染。
- `YHSCX_Click`:一户式查询 -> `taxer_build_yhscx_url` + 新标签页/外部浏览器。
- `StartCommand`:开始办理 -> `taxer_start_ticket` 后更新列表和当前文案。
- `EndCommand`:办结 -> `taxer_end_ticket` 后更新状态并清空 `nsrUrl`
- `SelectedSubTicket` setter切换企业 -> 重算 `currentCompanyText``nsrUrl`
- `LoadInfo`:初始化 -> `taxer_load_info` 一次加载。
---
## 7. 页面交互细节Vue
1. 页面 `onMounted``taxer_load_info`
2. 左侧列表点击行 => 更新 `selectedSubTicket`,调用 `taxer_build_nsr_url`
3. 列表项按钮显示规则(与现有一致):
- 显示“开始”:`isStarted && (status == 0 || status == 2)`
- 显示“办结”:`isStarted && status == 4`
4. 采集状态图标:
- 已采集:绿色(对应 `Registerd.png`
- 未采集:灰/红(对应 `UnRegisterd.png`
---
## 8. 迁移实施步骤(建议)
## 第 1 阶段:壳迁移(低风险)
- 建立 Tauri v1 + Vue 单窗口工程。
- 先做 UI 结构 1:1 迁移。
- 右侧“纳税人信息”先保留 `iframe/webview` 加载原 URL不改业务页
## 第 2 阶段:命令层落地
- 实现 `taxer_load_info / start / end / build_url / get_today_enterprises`
- 前端改为只依赖 command不再直接拼接/请求。
## 第 3 阶段:体验与稳定性
- 增加错误提示、重试、超时处理。
- 增加日志链路(前端行为日志 + Rust 命令日志)。
- 补充 E2E 场景测试(见第 10 节)。
---
## 9. 关键风险与处理
- **旧 SDK 依赖迁移风险**`ticket/business` 等可能强依赖 .NET。
- 方案:先通过 HTTP/本地服务桥接,逐步替换为 Rust 实现。
- **WebBrowser 行为差异**WPF WebBrowser 与 Tauri webview 对 cookie/session 行为不同。
- 方案:先验证登录态与跨域;必要时改外部浏览器打开。
- **线程模型差异**WPF Dispatcher/BackgroundWorker 到 Vue 异步模型要重构。
- 方案:统一在 command 层串行/并行编排,前端只维护状态。
---
## 10. 测试清单(迁移验收)
- 初始化后实名信息完整展示(姓名、头像、身份证图、手机号)。
- 票据列表状态渲染正确(待办/办理中/已完成)。
- 开始办理后状态切换为办理中,当前办理文案正确。
- 办结后状态切换为已完成,页面清空/切换逻辑正确。
- 复制号码、复制税号可在系统剪贴板拿到正确值。
- 一户式查询 URL 参数替换正确(`djxh/sfzhm/sflb/swjgDm/qhhm/sjhm`)。
- 代理人办理企业清单可正确加载并展示。
- 网络异常时有可感知报错,不出现页面卡死。
---
## 11. 建议目录结构Tauri
```text
src-tauri/
src/
commands/
taxer.rs
system.rs
services/
baishui_service.rs
ziyun_service.rs
ticket_service.rs
models/
taxer.rs
ticket.rs
src/
views/
TaxerInfoView.vue
stores/
taxer.ts
services/
tauri-taxer.ts
components/
taxer/
TaxerProfileCard.vue
TicketList.vue
TaxerToolbar.vue
EnterpriseTable.vue
```
---
## 12. 结论
`TaxerInfo` 模块适合按“**UI 先迁移、命令聚合、再逐步替换内嵌网页**”的路径落地。
在 Tauri v1 中建议将现有 ViewModel 的业务入口收敛为 5~7 个 command由 Vue 负责渲染与交互,可在保持业务一致的前提下提升后续可维护性。
---
## 13. 深挖:`BaiShuiSDK` / `QueuingSystemSDK` 真实依赖行为
本节用于回答“改写后是否还能调用相同后端接口”的关键问题。
## 13.1 `TaxerInfo` 实际依赖清单(代码级)
- `BaiShuiSDK.Model.BaishuiModel`
- `SmzJhxtGetBsyxxByqhhm`(取号号 -> 办税员信息)
- `SmzGetSmzcjxx`(身份证号 -> 采集信息)
- `SmzJhxtGetBdNsrxx`(身份证号 -> 绑定纳税人)
- `QueuingSystemSDK.ticket`
- `Update()`、`Stop()`、`Abandon()`、`GetList(...)`
- `QueuingSystemSDK.business`
- `new business((int)t.BIZ_UID)` 用于补全业务名称
- `QueuingSystemSDK.SystemControl`
- `Get("REALNAME_CHECK_API")`、`Get("BS_NSR_URL")`、`Get("BS_YHSCX_URL")`、`Get("BS_TAX_AUTHORITY_NUM")`、`Get("ZIYUN_SERVICE_URL")`
- `QueuingSystemSDK.HttpHelper`
- `httpGet("{ZIYUN_SERVICE_URL}/agentInfo/today-enterprises/{tktId}")`
- `DbHelperSQL.GetServerTime()`(开始办理时落库时间)
## 13.2 百税接口协议(必须保持兼容)
`TaxerInfo` 间接通过 `BaishuiModel -> BsDefaultClient.execute` 调后端。真实请求格式不是纯 JSON而是
- `POST {REALNAME_CHECK_API}`
- `Content-Type: application/x-www-form-urlencoded`
- body 参数:
- `domain.ywId={bid}`
- `domain.parmJson={UrlEncode(Json(request))}`
其中 `UrlEncode` 使用 SDK 自定义逻辑:逐字符编码,并将 `%xx` 转为大写(`%2f -> %2F`)。
这在某些网关上会影响签名或解析结果,建议 Rust 侧保持一致实现。
## 13.3 `TaxerInfo` 使用的 3 个百税 `bid`
- `smz.jhxtGetBsyxxByqhhm`
- 请求字段:`qhhm`
- 来源:`compareCode`(取号号码)
- `smz.getSmzcjxx`
- 请求字段:`sfzhm`
- 来源:上一步返回 `result.sfzhm`
- `smz.jhxtGetBdNsrxx`
- 请求字段:`sfzhm`
- 来源:同上
返回字段依赖(前端展示必须保留):
- 办税员:`xm/xb/mz/sfzhm/zz/csrq/sfzzmPic/headPic`
- 采集信息:`sjhm/bz/code`
- 绑定纳税人:`dataList[].djxh/nsrsbh/nsrmc`
## 13.4 队列票据本地数据行为(非 HTTP
`ticket` 属于本地数据库实体SQL Server不是远端 REST
- `Update()`
- 更新 `WIN_ID/BIZ_UID/EMP_UID/START_TIME/END_TIME/RANK/STATUS`
- `Stop(DateTime)`
- 更新 `END_TIME=GETDATE()`、`STATUS=complete(5)`
- `Abandon()`
- 更新 `END_TIME=GETDATE()`、`STATUS=abandoned(6)`
- `GetList(where, ..., hideLinkTicket)`
- 直接拼 SQL 查 `[ticket]` 表并映射对象
这意味着 Tauri 改写时有两个实现路径:
1. **兼容优先**:继续复用现有 .NET 服务层本地中间服务操作数据库Tauri 仅调用中间层 API。
2. **重写优先**Rust 直接连库并重建 `ticket` SQL 逻辑(工作量和风险更高)。
## 13.5 配置来源(`SystemControl.Get`
`SystemControl` 在静态构造时会把 `system` 表配置加载进内存缓存,然后 `Get(key)` 直接读缓存。
`TaxerInfo` 迁移最关键的配置项如下:
- `REALNAME_CHECK_API`:百税 API 入口地址
- `BS_NSR_URL`:纳税人信息页模板(含 `{djxh}` 等占位符)
- `BS_YHSCX_URL`:一户式查询 URL 模板
- `BS_TAX_AUTHORITY_NUM``{swjgDm}` 参数
- `ZIYUN_SERVICE_URL`:紫云服务入口
默认值(由 `DBUpdateManager` 初始化)显示 `BS_NSR_URL/BS_YHSCX_URL` 为同一模板,迁移后可沿用同策略。
## 13.6 直接 HTTP 接口(紫云)
`GetDelegaterInfo()` 使用:
- `GET {ZIYUN_SERVICE_URL}/agentInfo/today-enterprises/{CurrentTicket.TKT_ID}`
解析规则:
- 响应 `code == 200 && data != null` 才展示
- 字段映射:
- `serialNumber -> SerialNumber`
- `serviceTarget -> EnterpriseName`
- `serviceTargetCode -> TaxpayerId`
- 否则清空列表
## 13.7 Tauri v1 落地时的“接口保持一致”建议
### A. 百税接口适配器Rust必须做到
- 保持相同 `bid` 与请求字段名
- 保持 form 参数名:`domain.ywId`、`domain.parmJson`
- 保持 UTF-8 + URL 编码行为(百分号大写)
- 保持错误语义:
- `SmzJhxtGetBsyxxByqhhm``code != "00"` 视为失败
- `SmzGetSmzcjxx`:允许 `"00"``"01"`
- `SmzJhxtGetBdNsrxx`:当前 SDK 对非 00 未强拦截(按原行为兼容)
### B. 队列票据能力建议
- 第一阶段不要在 Rust 侧直连数据库重写 `ticket` 全量逻辑;
- 建议先将 `Start/Stop/GetList` 封成可调用服务HTTP/IPCTauri 调该服务;
- 等功能回归稳定后再考虑 Rust 直连库替换。
### C. URL 模板能力
- `Analize_URL` 的 6 个占位符替换规则必须逐字兼容:
- `{djxh}`、`{sfzhm}`、`{sflb}`、`{swjgDm}`、`{qhhm}`、`{sjhm}`
## 13.8 额外注意点(代码库现状)
- `SmzJhxtGetBdNsrxxRequest` 类定义在 `SmzJhxtGetBsyGlqyxxRequest.cs` 文件中(文件名与类名不一致),迁移时建议统一命名避免维护歧义。
- `HttpHelper.httpGet` 未设置超时与异常细化Tauri 迁移时建议新增超时、重试与可观测日志。
---
## 14. 基于现有 Tauri 项目(`F:\workspace\zyclient_linux\TauriClient\call-client`)的落位分析
本节基于当前 Tauri v1 代码现状补充,目的是将 `TaxerInfo` 改写方案落到“现项目可实施”的路径,而非抽象设计。
## 14.1 当前 Tauri 主结构(已实现)
- 前端路由:
- `/main` -> `MainView.vue`
- `/ticketList` -> `TicketListView.vue`
- `/login` -> `LoginView.vue`
- `/setup` -> `ServerSetupView.vue`
- Rust 窗口命令(已实现):
- `open_ticket_window`
- `close_ticket_window`
- `focus_window`
- `open_main_window`
- `open_login_window`
- 当前窗口形态:
- `main` 窗口:`500x100`、`always_on_top`、无边框(条形呼叫台)
- `ticketList` 窗口:`1024x720`、无边框、独立路由页
## 14.2 “办税员窗口”当前行为(关键结论)
`MainView.vue` 的“更多菜单”里确有 `办税员窗口` 按钮,但目前逻辑是:
- `handleMoreCommand("main")` 仅提示“当前已在办税员窗口”
- 并不会打开新的 TaxerInfo 页面/窗口
这说明:**现项目尚未承载 C# `TaxerInfo` 的完整 UI 与业务**,仅有一个呼叫控制条主窗。
## 14.3 与 `TaxerInfo` 的差距(按能力分层)
### A. 窗口与页面承载差距
-`main` 尺寸为 `500x100`,不具备 `TaxerInfo`(约 `1200x600`)承载条件。
- 当前仅有一个次窗口 `ticketList`,尚无 `taxerInfo` 路由/窗口定义。
### B. 接口协议差距
- 现有 `src/api/index.ts` 全部走 `API_QUEUE_CALLER_PATH=/api/queue/caller`JSON REST
- `TaxerInfo` 依赖的百税接口协议是 form-url-encoded + `domain.ywId/domain.parmJson`,目前项目中尚未实现该适配器。
### C. 数据源差距
-`MainView/TicketListView` 主要消费 `call-terminal/*` 接口(呼叫、票池、评价)。
- `TaxerInfo` 需要额外数据源:
- 百税 9.2.2 / 9.2.12 / 9.2.3
- 紫云 `today-enterprises`
- 本地票据扩展信息(企业名、税号映射等)
## 14.4 在现项目中建议的落位方案(不改代码阶段的设计结论)
### 方案选型
推荐新增独立窗口 `taxerInfo`(与 `ticketList` 同级),而不是直接塞入当前 `500x100` 主窗:
1. 保留 `main` 作为呼叫条形控制台(最小侵入)。
2. 在“更多菜单 -> 办税员窗口”中打开/聚焦 `taxerInfo`
3. `taxerInfo` 使用新路由页面(建议 `/taxerInfo`)承载原 WPF 界面。
### 这样做的原因
- 与现有多窗口架构一致(已有 `ticketList` 模式可复用)。
- 不影响当前呼叫链路稳定性。
- 便于逐步迁移 `TaxerInfo` 而非一次性替换 `MainView`
## 14.5 与现项目文件的映射建议
- 前端:
- 新增 `src/views/TaxerInfoView.vue`
- `src/router/index.ts` 增加 `/taxerInfo`
- `src/host/window.ts` 增加 `openTaxerInfoWindow()`
- `src/views/MainView.vue``handleMoreCommand("main")` 改为打开/聚焦 `taxerInfo`
- Rust
- `src-tauri/src/commands/window.rs` 增加
- `open_taxer_info_window`
- `close_taxer_info_window`(可选)
- `src-tauri/src/lib.rs` 注册上述命令
## 14.6 `TaxerInfo` 接口在现项目中的接入位置建议
### 前端 API 层
`src/api` 下新增 `taxer.ts`,避免和 `call-terminal` 混杂:
- `loadTaxerInfo(compareCode, currentTicketId)`
- `startTaxerTicket(...)`
- `endTaxerTicket(...)`
- `getTodayEnterprises(ticketId)`
- `buildNsrUrl(...)` / `buildYhscxUrl(...)`(可前后端任选)
### HTTP 传输层
沿用 `src/utils/service.ts` 的 axios + Tauri HTTP adapter 机制,但需要区分两类 baseURL
1. 现有 `API_QUEUE_CALLER_PATH`
2. 百税 API 专用 baseURL`REALNAME_CHECK_API`+ form 编码适配
## 14.7 文档结论更新(针对你的目标)
结合当前 Tauri 项目,`TaxerInfo` 改写最可行路径是:
- **先新增 `taxerInfo` 独立窗口**(由 `MainView` 的“办税员窗口”按钮打开)
- **再迁移 C# `TaxerInfo` 的 UI 与接口编排**
- **最后再评估是否把条形主窗与办税员窗合并**
这样既满足“在 Tauri 主流程中打开办税员窗口”,又不会破坏现有呼叫台功能。