# Data Model: 用户发票抬头管理

**Date**: 2026-07-24

## 新增表：info_invoice（用户发票抬头，用户级，镜像 info_address）

> DDL 写入 `updatesql/sql.md`，由开发者手动执行（项目规范）。用户级数据用 `info_` 前缀（与 `info_address` / `info_user` 一致），区别于订单级 `pos_order_invoice`。

| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT PK AUTO | 主键 |
| user_id | BIGINT NOT NULL | 用户id（JWT 隔离；写库前强制以 token 解析值覆盖） |
| title_name | VARCHAR(50) NOT NULL | 抬头备注名（如「公司-美食達」「个人-手机条码」），客户端列表展示用 |
| category | VARCHAR(8) NOT NULL | 类型：`B2C`=个人 / `B2B`=公司 |
| buyer_name | VARCHAR(100) NOT NULL | 买方名称：B2C=个人姓名 / B2B=公司全名 |
| buyer_ubn | VARCHAR(16) | 统一编号（统编，8 位数字，含前导零须字符串）；**仅 B2B** |
| buyer_email | VARCHAR(200) | 接收邮箱；B2B 必填 / B2C 仅 ezPay 会员载具(2) 时必填 |
| carrier_type | VARCHAR(8) | 载具类型 `0`手机条码 / `1`自然人凭证 / `2`ezPay 会员；**仅 B2C** |
| carrier_num | VARCHAR(64) | 载具号码（随 carrier_type） |
| create_time | DATETIME DEFAULT CURRENT_TIMESTAMP | 创建时间（DB 自动） |
| update_time | DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间（DB 自动） |

> 不设 `create_by` / `update_by`：App 端走 JWT（非 ruoyi 登录用户名），无意义字段，从简。`create_time` / `update_time` 由 DB `DEFAULT` / `ON UPDATE` 自动维护，代码不手动赋值。

**索引**：`KEY idx_user (user_id)`（用户隔离查询）。

**约束约定**（应用层校验，非 DB 约束）：

- 无默认抬头列、无数量上限、硬删除（与 `info_address` 一致）。
- B2B ↔ 载具字段互斥：B2B 时 `carrier_type` / `carrier_num` 必空；B2C 时 `buyer_ubn` 必空。
- 完整保存校验矩阵见 `contracts/api.md`。

## 复用实体（不改）

- **InfoUser**（`info_user`）：提供 userId（JWT claim `id` → `JwtUtil.getusid`）。
- **010 `ApplyInvoiceDto` / `PosOrderInvoice`**：不改。开票时客户端从选中抬头取字段填入 `ApplyInvoiceDto`，开票链路完全不动。
