Date: 2026-06-16
DDL 写入
updatesql/sql.md,由开发者手动执行(项目规范)。当前态表:一笔订单至多一行;作废后状态置「作废」、允许重开(同行覆盖,MVP 不留历史明细)。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT PK AUTO | 主键 |
| order_id | BIGINT NOT NULL | 关联 pos_order.id(UNIQUE) |
| order_no | VARCHAR(64) | 冗余 pos_order.ddId,便于排查 |
| store_id | BIGINT NOT NULL | 冗余 pos_order 所属门店 id(关联 pos_store.id) |
| invoice_category | VARCHAR(8) | 发票类型:B2C / B2B |
| buyer_name | VARCHAR(100) | 买方名称(B2C=客户名/邮箱名,B2B=公司名) |
| buyer_ubn | VARCHAR(16) | 买方统一编号(统编 8 码,B2B 必填) |
| buyer_email | VARCHAR(200) | 接收邮箱(B2C 无载具时用) |
| carrier_type | VARCHAR(8) | 载具类型:0手机条码/1自然人凭证/2ezPay会员载具(可空) |
| carrier_num | VARCHAR(64) | 载具号码(carrier_type 非空时必填) |
| invoice_number | VARCHAR(16) | ezPay 发票号(开立成功后填,字轨号段分配) |
| random_num | VARCHAR(8) | 防伪随机码(ezPay 返回) |
| invoice_status | TINYINT NOT NULL DEFAULT 0 | 0未开 / 1已开 / 2失败 / 3作废 |
| total_amt | INT | 含税总额(= 订单实付 amount) |
| sales_amt | INT | 销售额(未税,= round(total/1.05)) |
| tax_amt | INT | 税额(= total - sales) |
| fail_reason | VARCHAR(500) | 开票/作废失败原因 |
| invoice_url | VARCHAR(500) | 发票查看凭证 / 链接 |
| apply_time | DATETIME | 客户申请开票时间 |
| issue_time | DATETIME | 开立成功时间 |
| invalid_time | DATETIME | 作废时间 |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
| create_by | VARCHAR(64) | 创建人 |
| update_by | VARCHAR(64) | 更新人 |
索引:
UNIQUE KEY uk_order_id (order_id) —— 防重复开票(D5)KEY idx_status (invoice_status)KEY idx_store (store_id) 开票成功 作废(ezPay invalid 成功)
┌─────────────────┐ ┌──────────────────┐
▼ │ ▼ │
[0 未开] ──成功──> [1 已开] ──作废──> [3 作废]
│ │
│ 失败 │ 失败
▼ ▼
[2 失败] <──重试─── (失败/作废/未开 均可重试开票)
└──重试成功──> [1 已开]
invoice_status ∈ {0 未开, 2 失败, 3 作废} 才允许发起开票;1 已开 拒绝(FR-005)。pos_order):开票来源。取 id/ddId/amount/food/state(=3 完成)/payStatus(=1)/门店归属(shId/mdId)。门店 id 判定:依据 CLAUDE.md 商家类型口径(普通/夜市商家用 shId,摊位用 mdId)对齐 pos_store。pos_store_ezpay):开票凭证来源。仅 ezpayStatus=2 且 isEnabled=1 的门店可开票;取 merchantId/hashKey/hashIv 构造 EzPayConfig。pos_store):invoice_exempt=1 的门店不提供开票(009)。按 research D1(不含运费,订单构成 amount = foodAmount − 优惠 + freight):
invoice_total = amount - freight // 商品实付(已扣优惠、不含运费)
total_amt = invoice_total // 含税总额
sales_amt = round(invoice_total / 1.05) // 销售额未税
tax_amt = total_amt - sales_amt // 税额
逐商品明细由 food JSON 组装(D2),优惠按比例分摊到各商品使 ΣItemAmt = total_amt(D3);运费不进发票。