---

description: "Task list for 用户发票抬头管理"

---

# Tasks: 用户发票抬头管理

**Input**: Design documents from `/specs/014-invoice-profile/`

**Prerequisites**: spec.md ✅, data-model.md ✅, contracts/api.md ✅, plan.md ✅

**范围**: 仅后端 App 端 CRUD（实体 / Mapper / Service / Controller + SQL）。无前端、无 i18n、无后台端。

**测试**: 轻量手测（curl/Postman），按 contracts/api.md。

## Format: `[ID] [P?] Description`

- **[P]**: 可并行（不同文件、无未完成依赖）

## Path Conventions

- 实体 / Mapper / Service：`ruoyi-system/src/main/java/com/ruoyi/system/...`
- Controller：`ruoyi-admin/src/main/java/com/ruoyi/app/order/InfoInvoiceController.java`
- SQL：`updatesql/sql.md`

---

## Phase 1: Setup

- [x] T001 在 `updatesql/sql.md` 追加 2026-07-24 段：`CREATE TABLE info_invoice`（字段 / 类型 / 注释见 data-model.md，含 `KEY idx_user(user_id)`，引擎 InnoDB / utf8mb4）。无 sys_menu（本期无后台端）。

---

## Phase 2: Foundational

- [x] T002 [P] 新建 `InfoInvoice` 实体（`ruoyi-system/.../domain/InfoInvoice.java`）：`@Data @TableName("info_invoice") @EqualsAndHashCode(callSuper=false)`，`@TableId(type=IdType.AUTO)`；字段 id / userId / titleName / category / buyerName / buyerUbn / buyerEmail / carrierType / carrierNum / createTime / updateTime（注释对齐 data-model；create_time/update_time 由 DB DEFAULT/ON UPDATE 自动维护，无 createBy/updateBy）。参考 `InfoAddress.java` 风格。
- [x] T003 [P] 新建 `InfoInvoiceMapper` 接口（`ruoyi-system/.../mapper/InfoInvoiceMapper.java`）继承 `BaseMapper<InfoInvoice>`，**空体无自定义方法、无 XML**——App 端仅用 MyBatis-Plus `IService` CRUD（list/saveOrUpdate/getById/removeById，字段驼峰自动映射 info_invoice 列）；本期无后台端，故无需自定义查询。
- [x] T004 新建 `IInfoInvoiceService`（`ruoyi-system/.../service/IInfoInvoiceService.java`，继承 `IService<InfoInvoice>`，方法 listByUserId / saveOrUpdateMine / getMine / deleteMine）+ `InfoInvoiceServiceImpl`（`.../service/impl/InfoInvoiceServiceImpl.java`，`extends ServiceImpl<InfoInvoiceMapper, InfoInvoice>`）。实现私有 `validateInvoiceProfile(InfoInvoice)`（按 contracts 校验矩阵：titleName / buyerName 非空、category ∈ {B2C,B2B}、B2B 统编 `^\d{8}$` + 邮箱必填 + 载具空、B2C 载具按类型正则 + 统编空 + 邮箱仅会员载具必填），非法抛 `ServiceException`。`saveOrUpdateMine` 内强制 setUserId + 校验 + 更新时归属校验；`getMine`/`deleteMine` 越权返回 null/false。依赖 T002、T003。

**Checkpoint**: 表 + 实体 + mapper + service（含校验）就绪。

---

## Phase 3: Controller（App 端）

- [x] T005 新建 `InfoInvoiceController`（`ruoyi-admin/.../app/order/InfoInvoiceController.java`，`@RequestMapping("/system/invoice")`，extends `BaseController`，注入 `IInfoInvoiceService`）：① `GET /getinvoice`（`@Anonymous @Auth` + `@RequestHeader token` → `getusid` → `QueryWrapper.eq("user_id")` → list）；② `POST /invoice`（token + `@RequestBody InfoInvoice` → `setUserId` 强制覆盖 → `validateInvoiceProfile` → 更新时校验归属 → `saveOrUpdate`）；③ `GET /getinvoicexq?id=`（getById + 归属校验）；④ `GET /deleinvoice?id=`（归属校验 → removeById）。注解 / 工具类照搬 `InfoAddressController`。依赖 T004。

**Checkpoint**: 4 接口可用，curl 跑通。

---

## Phase 4: Polish

- [ ] T006 [P] 按 contracts/api.md 手测：B2C（三种载具）/ B2B 保存 → 列表 → 详情 → 改 → 删；校验矩阵各非法分支（统编非 8 位、手机条码不以 `/` 开头、自然人凭证格式错、B2B 带载具、B2C 带统编、必填空）均拦截；越权（A 操作 B 的 id）拒绝。
- [x] T007 [P] 更新记忆索引 `MEMORY.md`：加一行指向 `specs/014-invoice-profile/spec.md`（参考 010 写法）。

---

## Dependencies & Execution Order

- Phase 1 Setup → Phase 2 Foundational（T002 / T003 可并行，T004 依赖二者）→ Phase 3 Controller（T005 依赖 T004）→ Phase 4 Polish（T006 依赖 T005，T007 随时）。
