# 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 |
|-----------|------------|-------------------------------------|
| — | — | — |
