All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 11m42s
- 新增成对迁移 000222:tb_exchange_order 增加非空 migration_status 与 migration_failure_reason,按既有 migrate_data/migration_completed 回填历史, 并加四值 CHECK 约束,不新增索引 - 模型与常量定义四种迁移状态及中文名称,保留既有布尔字段兼容语义 - 物流换货创建恒 not_migrated,发货按请求落 pending/not_migrated, 完成成功写 migrated/not_migrated 并清空失败原因、同步兼容字段 - 直接换货创建即完成,任一步失败整体回滚,不持久化换货单、不产生 failed - 迁移失败回滚全部业务修改后,在独立短事务内条件更新 failed 与安全失败原因 并写失败审计,RowsAffected 为 0 时跳过状态写入但仍写审计 - failed 物流单重试仅限超级管理员或平台用户,授权以锁内 FOR UPDATE 判定为准, 重试从钱包余额起整表重跑;非 failed 单沿用既有完成门禁 - 列表与详情返回迁移状态与中文名称,仅 failed 返回失败原因;既有三字段保持兼容 - 换货导出在「状态」列后新增中文「迁移状态」列,不导出失败原因 - 同步 order-refund-exchange 主 spec 与验证证据,归档本 Change - 登记 KNOWN-ISSUE-001:既有标签复制 OnConflict 未声明部分索引谓词(42P10), 旧资产带标签时迁移最后一步失败,待另立变更修复
79 lines
5.1 KiB
Go
79 lines
5.1 KiB
Go
package model
|
||
|
||
import (
|
||
"fmt"
|
||
"math/rand"
|
||
"time"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// ExchangeOrder 换货单模型
|
||
// 承载物流换货与直接换货的完整生命周期:后台发起 → 可选客户填写收货信息 → 可选后台发货 → 确认完成(含可选全量迁移) → 旧资产可转新
|
||
// 状态机:shipping 为 1-待填写信息 → 2-待发货 → 3-已发货待确认 → 4-已完成,1/2 时可取消 → 5-已取消;direct 创建即完成为 4
|
||
// 迁移状态机:物流换货创建为 not_migrated;发货按 migrate_data 写 pending 或 not_migrated;确认完成成功写 migrated、失败写 failed(保持 3 可再次确认完成)
|
||
// 迁移结果以 migration_status 为准,migrate_data、migration_completed、migration_balance 仅保留兼容语义
|
||
type ExchangeOrder struct {
|
||
gorm.Model
|
||
BaseModel `gorm:"embedded"`
|
||
|
||
// 单号
|
||
ExchangeNo string `gorm:"column:exchange_no;type:varchar(50);not null;uniqueIndex:idx_exchange_order_no,where:deleted_at IS NULL;comment:换货单号(EXC+日期+随机数)" json:"exchange_no"`
|
||
|
||
// 流程类型
|
||
FlowType string `gorm:"column:flow_type;type:varchar(20);not null;default:'shipping';index:idx_exchange_order_flow_status;comment:换货流程类型 shipping-物流换货 direct-直接换货" json:"flow_type"`
|
||
|
||
// 旧资产快照
|
||
OldAssetType string `gorm:"column:old_asset_type;type:varchar(20);not null;comment:旧资产类型(iot_card/device)" json:"old_asset_type"`
|
||
OldAssetID uint `gorm:"column:old_asset_id;not null;index:idx_exchange_order_old_asset;comment:旧资产ID" json:"old_asset_id"`
|
||
OldAssetIdentifier string `gorm:"column:old_asset_identifier;type:varchar(100);not null;comment:旧资产标识符(ICCID/虚拟号)" json:"old_asset_identifier"`
|
||
|
||
// 新资产快照(发货时填写)
|
||
NewAssetType string `gorm:"column:new_asset_type;type:varchar(20);comment:新资产类型(iot_card/device)" json:"new_asset_type"`
|
||
NewAssetID *uint `gorm:"column:new_asset_id;comment:新资产ID" json:"new_asset_id,omitempty"`
|
||
NewAssetIdentifier string `gorm:"column:new_asset_identifier;type:varchar(100);comment:新资产标识符(ICCID/虚拟号)" json:"new_asset_identifier"`
|
||
|
||
// 收货信息(客户端填写)
|
||
RecipientName string `gorm:"column:recipient_name;type:varchar(50);comment:收件人姓名" json:"recipient_name"`
|
||
RecipientPhone string `gorm:"column:recipient_phone;type:varchar(20);comment:收件人电话" json:"recipient_phone"`
|
||
RecipientAddress string `gorm:"column:recipient_address;type:text;comment:收货地址" json:"recipient_address"`
|
||
|
||
// 物流信息(后台发货时填写)
|
||
ExpressCompany string `gorm:"column:express_company;type:varchar(100);comment:快递公司" json:"express_company"`
|
||
ExpressNo string `gorm:"column:express_no;type:varchar(100);comment:快递单号" json:"express_no"`
|
||
|
||
// 迁移相关
|
||
MigrateData bool `gorm:"column:migrate_data;type:boolean;default:false;comment:是否执行全量迁移" json:"migrate_data"`
|
||
MigrationCompleted bool `gorm:"column:migration_completed;type:boolean;default:false;comment:迁移是否已完成" json:"migration_completed"`
|
||
MigrationBalance int64 `gorm:"column:migration_balance;type:bigint;default:0;comment:迁移转移金额(分)" json:"migration_balance"`
|
||
|
||
// 迁移状态(保留上面的布尔字段供兼容客户端与既有业务读取,迁移结果以本字段为准)
|
||
MigrationStatus string `gorm:"column:migration_status;type:varchar(20);not null;default:'not_migrated';comment:业务数据迁移状态 not_migrated-不迁移 pending-待迁移 migrated-已迁移 failed-迁移失败" json:"migration_status"`
|
||
MigrationFailureReason string `gorm:"column:migration_failure_reason;type:varchar(500);not null;default:'';comment:最近一次业务数据迁移失败的安全化原因,仅迁移状态为 failed 时非空" json:"migration_failure_reason"`
|
||
|
||
// 业务时间
|
||
ShippedAt *time.Time `gorm:"column:shipped_at;comment:发货时间,仅物流换货发货成功后写入" json:"shipped_at,omitempty"`
|
||
CompletedAt *time.Time `gorm:"column:completed_at;comment:换货完成时间,物流换货和直接换货完成后写入" json:"completed_at,omitempty"`
|
||
|
||
// 业务信息
|
||
ExchangeReason string `gorm:"column:exchange_reason;type:varchar(100);not null;comment:换货原因" json:"exchange_reason"`
|
||
Remark *string `gorm:"column:remark;type:text;comment:备注" json:"remark,omitempty"`
|
||
Status int `gorm:"column:status;type:int;not null;default:1;index:idx_exchange_order_status;comment:换货状态 1-待填写信息 2-待发货 3-已发货待确认 4-已完成 5-已取消" json:"status"`
|
||
|
||
// 多租户
|
||
ShopID *uint `gorm:"column:shop_id;index;comment:所属店铺ID" json:"shop_id,omitempty"`
|
||
}
|
||
|
||
// TableName 指定表名
|
||
func (ExchangeOrder) TableName() string {
|
||
return "tb_exchange_order"
|
||
}
|
||
|
||
// GenerateExchangeNo 生成换货单号
|
||
// 格式:EXC + 年月日时分秒 + 6位随机数,如 EXC20260319143052123456
|
||
func GenerateExchangeNo() string {
|
||
now := time.Now()
|
||
randomNum := rand.Intn(1000000)
|
||
return fmt.Sprintf("EXC%s%06d", now.Format("20060102150405"), randomNum)
|
||
}
|