# Implementation Plan: 商家 ezPay 发票开通管理 **Branch**: `test`(不新建分支,按用户要求在当前分支开发) | **Date**: 2026-06-15 | **Spec**: [spec.md](./spec.md) **Input**: Feature specification from `specs/009-ezpay-invoice-onboarding/spec.md` ## Summary 平台代商家到 ezPay(线下人工)注册申请发票,拿到凭证后由运营在平台后台录入并验证,验证通过标记"已开通"才具备开票前提。本期交付:①平台后台"ezPay 发票开通管理"列表(状态/筛选/快捷过滤)②状态推进(未申请→申请中→已开通)③凭证录入+ezPay 接口验证④启用开关⑤免用发票标记(新字段挂在 pos_store)⑥商家端上传统编。技术方案:新增独立表 `pos_store_ezpay`(1:1 门店)+ `pos_store` 加 `invoice_exempt` 列;复用已有 `EzPay`/`EzPayConfig`/`EzPayEncryptUtil` 工具类,用 `invoice_search` 只读调用验证凭证(避开 CheckValue 不确定性)。 ## Technical Context **Language/Version**: Java 17+(Spring Boot,若依 RuoYi 框架;代码用 `jakarta.servlet`、`HexFormat` 表明 JDK17+) **Primary Dependencies**: - 后端:Spring Boot、MyBatis-Plus(`@TableName`/`@TableId`/`LambdaQueryWrapper`)+ XML mapper、Apache HttpClient、fastjson2、hutool、若依通用(`AjaxResult`/`TableDataInfo`/`BaseController`/`@PreAuthorize("@ss.hasPermi(...)")`) - 复用:`com.ruoyi.app.utils.ezPay.EzPay`(issue/search/doPost)、`EzPayConfig`、`ezPayCrypto.EzPayEncryptUtil` - 前端:Vue.js + Element UI(平台后台 `foodie-admin-vue`、商家端 `foodie-store`,均 vue-i18n 四语言) **Storage**: MySQL(新增表 `pos_store_ezpay`;`pos_store` 加列 `invoice_exempt`) **Testing**: JUnit 单测(mock ezPay 回应验证判读分支)+ 手测(ezPay 测试环境 `cinv.ezpay.com.tw` 真凭证跑通录入→验证→已开通) **Target Platform**: Windows 开发 / Linux 部署的 Spring Boot 服务 + Vue 后台/商家端 **Project Type**: web-service(Spring Boot 后端)+ 两个 Vue 前端 **Performance Goals**: 平台后台列表分页查询 p95 < 500ms;凭证验证调用(含 ezPay 网络往返)< 10s 超时 **Constraints**: ezPay 无注册 API(线下人工);凭证验证走只读接口不产生真实发票;本期不含订单自动开票 **Scale/Scope**: 1 新表 + 1 新列;后端 1 controller + 1 service + 1 mapper(含XML) + 1 domain;平台后台 1 新页面;商家端 1 表单字段;4 语言 i18n ×2 前端 ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* `.specify/memory/constitution.md` 为未填充模板(占位符未替换),项目未定义具体宪法原则与门槛。**无实质门槛需评估**,设计遵循项目既有约定(若依分层、MyBatis-Plus、全栈字段清单、四语言 i18n、SQL 写 `updatesql/sql.md`)。Phase 1 后复核:无违反。 ## Project Structure ### Documentation (this feature) ```text specs/009-ezpay-invoice-onboarding/ ├── spec.md # 需求规格 ├── plan.md # 本文件(技术方案) ├── research.md # Phase 0:关键技术决策与依据 ├── data-model.md # Phase 1:表结构、字段、状态机 ├── quickstart.md # Phase 1:跑通验证步骤 ├── contracts/ │ └── api.md # Phase 1:REST 接口契约 ├── checklists/ │ └── requirements.md # spec 质量检查 └── tasks.md # /speckit-tasks 生成(本期 plan 不创建) ``` ### Source Code (repository root) ```text # ===== 后端 foodie_server ===== ruoyi-system/src/main/java/com/ruoyi/system/ ├── domain/ │ ├── PosStore.java # 已有:新增 invoice_exempt 字段 │ └── PosStoreEzpay.java # 新增:门店 ezPay 配置实体 ├── mapper/ │ ├── PosStoreMapper.java # 已有:resultMap 加 invoice_exempt │ └── PosStoreEzpayMapper.java # 新增 └── service/ ├── IPosStoreEzpayService.java # 新增 └── impl/PosStoreEzpayServiceImpl.java # 新增 ruoyi-system/src/main/resources/mapper/chanting/ ├── PosStoreMapper.xml # 已有:resultMap + select 列加 invoice_exempt └── PosStoreEzpayMapper.xml # 新增 ruoyi-admin/src/main/java/com/ruoyi/app/ ├── mendian/ │ ├── PosStoreController.java # 已有:addmendian/edit 携带 invoice_exempt;UBN 上传接口 │ └── PosStoreEzpayController.java # 新增:平台后台 ezPay 开通管理 REST ├── user/dto/ (或 mendian/dto) │ └── StoreEzpayCredentialDto.java # 新增:录入凭证入参 └── utils/ezPay/ # 已有,不改内部:EzPay/EzPayConfig/EzPayEncryptUtil # ===== SQL ===== updatesql/sql.md # 追加建表 + 加列语句(带日期注释) # ===== 平台后台前端 foodie-admin-vue ===== src/api/.../storeEzpay.js # 新增:接口封装 src/views/.../storeEzpay/index.vue # 新增:ezPay 开通管理页面 src/lang/{zh,tw,en,vi}.js # 四语言加 key(新对象层级 storeEzpay:{}) # ===== 商家端前端 foodie-store ===== src/lang/{zh,tw,en,vi}.js # 四语言加统编相关 key 门店设置表单 # 新增"统一编号"字段 ``` **Structure Decision**: 后端遵循既有若依分层(domain→mapper→service→controller),新表对应独立一套 CRUD;平台后台凭证管理用独立 `PosStoreEzpayController`(`/system/storeEzpay`,权限键 `chanting:storeEzpay:*`),与门店基础维护解耦;商家端 UBN 上传挂在门店设置流程。前端两个仓库各新增最小页面/字段,严格走四语言 i18n。 ## Complexity Tracking > 无宪法违反项,无需填写。 | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|-------------------------------------| | — | — | — |