// 资金账户与流水 6 表 model(t_user_expense / _school / _log / t_user_balance_request / // t_order_pay / t_payment_log)——与 unified/02-data-model/academic-model-ddl.sql「第一部分」逐列对齐。 // // 字段注释五维口径(本项目 discipline,逐字段必填): // // 语义与单位 | 枚举全量取值 | 写入时机 | 主外键/关联语义 | 安全红线 // // GORM 类型映射纪律(backend-model-field-spec.md §1.4,T2 实证漏 size → longtext 漂移): // - varchar(N) → string/*string + **必带 size:N**(N 严格等于 DDL); // - decimal(12,2) → **string + type:decimal(12,2)**(本批不直接落 money.Amount: // model 层保结构稳定,金额运算在 service 层用 money.Amount 完成,AC-02 共享内核); // - datetime(3) → time.Time/*time.Time(GORM MySQL 驱动默认 datetime(3)); // - date → *time.Time + type:date(对齐 DDL 的 DATE 列); // - tinyint(1) → bool;tinyint → int8;smallint → *int16;int → int32;bigint → int64; // - LONGTEXT(JSON/HTML)→ *string + type:longtext(string 直存,规避自定义类型序列化坑)。 // // 作用域说明(设计 §1.7 两条例外): // - t_user_expense / t_user_divide_into(_log) **无** institution_id/campus_id: // 全局钱包 / 全局推广体系,**不继承 BaseRepo**,越权断言在 Service 层经 MembershipChecker; // - t_user_expense_school 等带作用域列的表 → Repository 继承 BaseRepo 强制 Scoped。 package model import "time" // t_user_expense_log.type 编码(设计 §10.2 权威口径,方向以 SP dump 实现为准)。 // // 方向标注:+ 表示入账方向(balance 增加),− 表示出账方向;**106/206 为 ★dump 口径漂移项** // (与 P0-04 规格相反,见设计 §11-6 #2),后续 service 实现不得"按规格顺手修正"。 const ( ExpenseLogTypeRecharge int32 = 100 // 充值(+) ExpenseLogTypeCourseCampus int32 = 101 // 课程消费·校区(−) ExpenseLogTypeCourseSchoolA int32 = 104 // 课程消费·学校(−) ExpenseLogTypeCourseSchoolB int32 = 105 // 课程消费·学校(−) ExpenseLogTypeTransferOut int32 = 106 // 转结出(★dump 口径:+money) ExpenseLogTypeRecommend int32 = 107 // 老带新推荐(+) ExpenseLogTypeBadDebt int32 = 108 // 坏账处理(+) ExpenseLogTypeHistoryDeduct int32 = 200 // 历史扣费(−) ExpenseLogTypeCourseCampusB int32 = 201 // 课程消费·校区(−) ExpenseLogTypeCourseSchoolC int32 = 204 // 课程消费·学校(−) ExpenseLogTypeCourseSchoolD int32 = 205 // 课程消费·学校(−) ExpenseLogTypeTransferIn int32 = 206 // 转结入(★dump 口径:−money) ExpenseLogTypeRefund int32 = 207 // 余额退费(−,全额含坏账部分) ExpenseLogTypeClearBalance int32 = 208 // 余额清零(−) ExpenseLogTypeCancelRechargeAct int32 = 209 // 取消充值活动(−) ) // t_user_balance_request.type / approved 编码(设计 §2「4. t_user_balance_request」)。 const ( BalanceRequestTypeRecharge int32 = 100 // 普通充值 BalanceRequestTypeBadDebt int32 = 101 // 坏账 BalanceRequestTypeRefund int32 = 202 // 退费 BalanceRequestTypeCancelRecharge int32 = 203 // 取消充值活动 BalanceRequestTypeTransfer int32 = 300 // 转结 BalanceRequestApprovedCancelled int8 = -2 // 取消 BalanceRequestApprovedRejected int8 = -1 // 不通过 BalanceRequestApprovedPending int8 = 0 // 未处理 BalanceRequestApprovedPassed int8 = 1 // 已通过(资金变动触发点) ) // t_user_balance_request.transfer_accounts_type 收款方式编码。 const ( TransferAccountsNone int8 = 0 // 无 TransferAccountsCash int8 = 1 // 现金 TransferAccountsWechat int8 = 2 // 微信 TransferAccountsAlipay int8 = 3 // 支付宝 ) // t_order_pay.status 状态编码。 const ( OrderPayStatusFailed int8 = -2 // 失败 OrderPayStatusCancelled int8 = -1 // 取消 OrderPayStatusUnpaid int8 = 0 // 未支付 OrderPayStatusSucceeded int8 = 1 // 成功 ) // UserExpense 用户费用表 t_user_expense(**全局维度**账户·自然键 user_id·无作用域列)。 // // 主键:user_id(canonical_user_id ULID,D1)。 // 作用域:**无** institution_id/campus_id——跨机构共享的钱包视图,作用域断言由 Service 层完成 // (设计 §1.7 例外一,代码评审须逐处确认"未绕过 Scoped 就代表了它不需要 scoped")。 type UserExpense struct { UserID string `gorm:"column:user_id;primaryKey;size:26;not null" json:"userId"` // 用户(ULID 26 位,D1;原 userid int 经 t_legacy_mapping 重映射)|枚举:无|写入:首次资金操作 UPSERT|FK:→ t_user.canonical_user_id(无物理 FK)|红线:旧 int userid 只进映射表,禁止在此落 int Balance string `gorm:"column:balance;type:decimal(12,2);not null;default:0.00" json:"balance"` // 余额(元,±;负数=欠费)|枚举:无|写入:充值/消费/退费/清零事务内更新|FK:无|红线:只允许 `balance = balance + ?` 原子表达式,禁止读-改-写(防丢更新) Spend string `gorm:"column:spend;type:decimal(12,2);not null;default:0.00" json:"spend"` // 上课消费课费单价之和(元;只增)|枚举:无|写入:spend SP(FundService.AccumulateSpend)|FK:无|红线:本列不动 balance,勿混用为扣费 TotalIncome string `gorm:"column:total_income;type:decimal(12,2);not null;default:0.00" json:"totalIncome"` // 总充值(元)|枚举:无|写入:充值+/退费-/清零不动/转结双向|FK:无|红线:报表口径列,禁止业务分支改写 TotalExpenditure string `gorm:"column:total_expenditure;type:decimal(12,2);not null;default:0.00" json:"totalExpenditure"` // 总消费(元;报课学费之和+历史欠费)|枚举:无|写入:消费+/退费-/转结双向调整|FK:无|红线:与 t_user_expense_school 同名列须同事务同步 TotalRecommendedAmount string `gorm:"column:total_recommended_amount;type:decimal(12,2);not null;default:0.00" json:"totalRecommendedAmount"` // 累计推荐金额(元)|枚举:无|写入:老带新双边入账累加|FK:无|红线:双边各加一次,勿重复累加(双跑重点比对项) SpecialExpenses string `gorm:"column:special_expenses;type:decimal(12,2);not null;default:0.00" json:"specialExpenses"` // 特殊费用(元;充值活动提前退费/坏账/清零累加)|枚举:无|写入:BadDebt/ClearBalance/退费差额|FK:无|红线:只增不减(对账口径) } // TableName 指定表名(GORM 默认复数化,与 DDL 的 t_ 前缀不符)。 func (UserExpense) TableName() string { return "t_user_expense" } // UserExpenseSchool 用户费用表 t_user_expense_school(**校区维度**账户·多租户)。 // // UK(user_id, institution_id, campus_id):旧库仅 PK id 无唯一键,目标态补 UK 以支撑 // "COUNT>0 则 UPDATE 否则 INSERT"(旧 SP)→ `clause.OnConflict{DoNothing}` 的 UPSERT 语义(设计 §1.5)。 // campus_id=0 表示机构级账户(跨校区汇总)。 type UserExpenseSchool struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 账户记录 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:被流水/工单逻辑引用(无物理 FK)|红线:不可复用/手工指定 UserID string `gorm:"uniqueIndex:uk_user_inst_campus,priority:1;column:user_id;size:26;not null" json:"userId"` // 用户(ULID;原 userid)|枚举:无|写入:账户创建时|FK:→ t_user(逻辑)|红线:不得跨界改写 InstitutionID int64 `gorm:"uniqueIndex:uk_user_inst_campus,priority:2;index:idx_inst_campus,priority:1;column:institution_id;not null" json:"institutionId"` // 机构 ID(原 schoolid;作用域列)|枚举:无|写入:账户创建时锁定,永不更新|FK:→ t_institution|红线:scoping 强依赖,禁止 0 值写入 CampusID int64 `gorm:"uniqueIndex:uk_user_inst_campus,priority:3;index:idx_inst_campus,priority:2;index:idx_campus_balance,priority:1;column:campus_id;not null;default:0" json:"campusId"` // 校区 ID(原 campusid;0=机构级)|枚举:无|写入:账户创建时锁定|FK:→ t_campus|红线:UK 第 3 列,改动即账户错位 Balance string `gorm:"index:idx_campus_balance,priority:2;column:balance;type:decimal(12,2);not null;default:0.00" json:"balance"` // 校区余额(元,±;负数=欠费)|枚举:无|写入:资金事务内原子增减|FK:无|红线:与全局 balance 同事务更新(固定顺序:全局→校区,防死锁) Spend string `gorm:"column:spend;type:decimal(12,2);not null;default:0.00" json:"spend"` // 校区上课消费课费单价之和(元)|枚举:无|写入:spend SP|FK:无|红线:只增 TotalIncome string `gorm:"column:total_income;type:decimal(12,2);not null;default:0.00" json:"totalIncome"` // 校区总充值(元)|枚举:无|写入:充值/退费/转结|FK:无|红线:与全局表同事务 TotalExpenditure string `gorm:"column:total_expenditure;type:decimal(12,2);not null;default:0.00" json:"totalExpenditure"` // 校区总消费(元)|枚举:无|写入:消费/退费/转结|FK:无|红线:与全局表同事务 TotalRecommendedAmount string `gorm:"column:total_recommended_amount;type:decimal(12,2);not null;default:0.00" json:"totalRecommendedAmount"` // 校区累计推荐金额(元)|枚举:无|写入:老带新结算|FK:无|红线:双边累计勿重复 SpecialExpenses string `gorm:"column:special_expenses;type:decimal(12,2);not null;default:0.00" json:"specialExpenses"` // 校区特殊费用(元;坏账/清零/提前退费)|枚举:无|写入:BadDebt/ClearBalance|FK:无|红线:只增不减 } // TableName 指定表名。 func (UserExpenseSchool) TableName() string { return "t_user_expense_school" } // UserExpenseLog 用户费用变更流水表 t_user_expense_log(**只增不改**·幂等键 request_id+type)。 // // 主键:id(D3 自增);幂等:UNIQUE(request_id, type)——Go 侧写库撞键即视为重复请求, // 返回首次结果(Repeated=true,code 仍 200,设计 §1.5)。历史行 request_id 为 NULL(MySQL 唯一键对 NULL 放行)。 type UserExpenseLog struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 流水 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:只增不改不删(审计底线) UserID string `gorm:"index:idx_user_time,priority:1;index:idx_user_type,priority:1;column:user_id;size:26;not null" json:"userId"` // 用户(ULID;原 userid)|枚举:无|写入:资金方法内|FK:→ t_user(逻辑)|红线:与账户行 user_id 必须一致 Type int32 `gorm:"uniqueIndex:uk_request_type,priority:2;index:idx_user_type,priority:2;column:type;not null;default:0" json:"type"` // 类型编码(见 ExpenseLogType*:100/101/104/105/106/107/108/200/201/204/205/206/207/208/209)|枚举:全量见常量块|写入:资金方法内按动作固定传值|FK:无|红线:type 参与幂等键,禁止复用不同语义 LogID *string `gorm:"index:idx_log_id;column:log_id;size:64" json:"logId,omitempty"` // 业务单号(原 logid:报课 id/缴费申请 id/结课申请 id/充值单号;无则 NULL)|枚举:无|写入:资金方法内|FK:弱关联 t_user_balance_request.biz_no 等|红线:非唯一,勿当幂等键 RequestID *string `gorm:"uniqueIndex:uk_request_type,priority:1;column:request_id;size:64" json:"requestId,omitempty"` // 幂等请求号(原 SP inputRequestid;NULL=历史数据)|枚举:无|写入:调用方传入(工单 biz_no/支付单 biz_no)|FK:无|红线:与 type 组唯一键,重复提交必须命中而非重复入账 Money string `gorm:"column:money;type:decimal(12,2);not null;default:0.00" json:"money"` // 变更金额(元;正负号表方向,见 ExpenseLogType* 注释)|枚举:无|写入:资金方法内|FK:无|红线:符号方向须与 type 口径一致(106/206 ★反向) BalanceAfter *string `gorm:"column:balance_after;type:decimal(12,2)" json:"balanceAfter,omitempty"` // 变更后校区余额快照(元;新增列,便于对账/双跑)|枚举:无|写入:同事务内 UPDATE 后回读|FK:无|红线:历史行为 NULL,不得回填猜测值 CreateUserID *string `gorm:"column:create_user_id;size:26" json:"createUserId,omitempty"` // 创建人(ULID;原 create_userid int,0/系统 → NULL)|枚举:无|写入:Service 从 scope/user 上下文取|FK:→ t_user(逻辑)|红线:不得写入旧 int id CreateTime time.Time `gorm:"index:idx_user_time,priority:2;column:create_time;not null" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认 CURRENT_TIMESTAMP(3)|FK:无|红线:只增不改(勿 UPDATE) Remark *string `gorm:"column:remark;size:1000" json:"remark,omitempty"` // 备注(原 text → varchar(1000),超长截断见迁移报告)|枚举:无|写入:资金方法内|FK:无|红线:不得写入敏感信息(手机号/证件号) } // TableName 指定表名。 func (UserExpenseLog) TableName() string { return "t_user_expense_log" } // UserBalanceRequest 余额变更申请表 t_user_balance_request(充值/退费/坏账/取消活动/转结 工单·审批驱动)。 // // 旧 UUID 主键处置(设计 §2.2 / §11-10 拍板):id int64 自增 + biz_no varchar(32) UNIQUE 保留旧 UUID。 // 资金变动触发点:approved 由 0 → 1 的审批动作(Service 同事务内先调资金方法、后回写工单并加幂等 request_id)。 type UserBalanceRequest struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 工单 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:被流水 log_id/优惠池 balance_request_id 引用|红线:对外暴露的业务单号是 biz_no,不是本列 BizNo string `gorm:"uniqueIndex:uk_biz_no;column:biz_no;size:32;not null" json:"bizNo"` // 业务单号(旧 UUID 主键原样保留;UNIQUE)|枚举:无|写入:创建工单时生成/迁移搬入|FK:唯一键锚点(迁移幂等/回滚/双跑反查)|红线:不可变(割接回滚依赖) Type int32 `gorm:"index:idx_type_approved,priority:1;column:type;not null;default:100" json:"type"` // 类型(100 普通充值/101 坏账/202 退费/203 取消充值活动/300 转结)|枚举:见 BalanceRequestType*|写入:创建工单时|FK:无|红线:决定调用哪个资金方法,禁止运行期变更 UserID string `gorm:"index:idx_user;column:user_id;size:26;not null;default:''" json:"userId"` // 用户(ULID;原 userid)|枚举:无|写入:创建工单时|FK:→ t_user(逻辑)|红线:与资金方法入参必须一致 InstitutionID int64 `gorm:"index:idx_inst_campus_approved,priority:1;column:institution_id;not null;default:0" json:"institutionId"` // 机构 ID(原 schoolid;作用域列)|枚举:无|写入:创建工单时|FK:→ t_institution|红线:scoping 依赖,禁止 0 值落库(除平台级) CampusID int64 `gorm:"index:idx_inst_campus_approved,priority:2;column:campus_id;not null;default:0" json:"campusId"` // 校区 ID(原 campusid;作用域列)|枚举:无|写入:创建工单时|FK:→ t_campus|红线:同上 ToInstitutionID *int64 `gorm:"column:to_institution_id" json:"toInstitutionId,omitempty"` // 转入机构 ID(仅 type=300 转结;新增列)|枚举:无|写入:转结申请时|FK:→ t_institution|红线:转出≠转入时须校验两侧均可见 ToCampusID *int64 `gorm:"column:to_campus_id" json:"toCampusId,omitempty"` // 转入校区 ID(仅 type=300)|枚举:无|写入:转结申请时|FK:→ t_campus|红线:同上 Money string `gorm:"column:money;type:decimal(12,2);not null;default:0.00" json:"money"` // 金额(元;旧 decimal(10,3) 收敛)|枚举:无|写入:创建工单时|FK:无|红线:审批通过后不可改(改须新建工单) TransferAccountsType int8 `gorm:"column:transfer_accounts_type;not null;default:0" json:"transferAccountsType"` // 收款方式(0 无/1 现金/2 微信/3 支付宝)|枚举:TransferAccounts*|写入:创建工单时|FK:无|红线:仅记账口径,不代表已对账 TransferAccountsTime *time.Time `gorm:"column:transfer_accounts_time" json:"transferAccountsTime,omitempty"` // 收款时间(datetime(3))|枚举:无|写入:财务确认收款时|FK:无|红线:不得早于 request_time OrderNo *string `gorm:"column:order_no;size:50" json:"orderNo,omitempty"` // 交易单号(原 orderid;第三方流水号/报课单号)|枚举:无|写入:支付回调/财务录入|FK:弱关联 t_order_pay.pay_order|红线:非唯一 RequestUserID string `gorm:"column:request_user_id;size:26;not null;default:''" json:"requestUserId"` // 申请人(ULID;原 request_userid)|枚举:无|写入:创建工单时|FK:→ t_user(逻辑)|红线:禁止旧 int id RequestTime *time.Time `gorm:"column:request_time" json:"requestTime,omitempty"` // 申请时间(datetime(3))|枚举:无|写入:提交时|FK:无|红线:只写一次 ResponseUserID *string `gorm:"column:response_user_id;size:26" json:"responseUserId,omitempty"` // 审批人(ULID;原 response_userid;0→NULL)|枚举:无|写入:审批动作|FK:→ t_user(逻辑)|红线:申请人不得自审(业务规则在 Service 校验) ResponseTime *time.Time `gorm:"column:response_time" json:"responseTime,omitempty"` // 审批时间(datetime(3))|枚举:无|写入:审批动作|FK:无|红线:与 approved 同事务写 ResponseMsg *string `gorm:"column:response_msg;size:1000" json:"responseMsg,omitempty"` // 审批意见|枚举:无|写入:审批动作|FK:无|红线:用户输入,禁止拼接进 SQL Approved int8 `gorm:"index:idx_inst_campus_approved,priority:3;index:idx_type_approved,priority:2;column:approved;not null;default:0" json:"approved"` // 审批状态(-2 取消/-1 不通过/0 未处理/1 已通过;原 aproved 错拼修正)|枚举:BalanceRequestApproved*|写入:审批动作|FK:无|红线:0→1 是资金变动唯一触发点,重复调用须幂等 Item *string `gorm:"column:item;size:1000" json:"item,omitempty"` // 项目(报课/充值课程说明)|枚举:无|写入:创建工单时|FK:无|红线:展示用,不参与计算 Imgs *string `gorm:"column:imgs;type:longtext" json:"-"` // 凭证图片(JSON 字符串直存)|枚举:无|写入:创建工单时|FK:无|红线:**API 不回显原始报文**(json:"-"),防凭证泄露 Remark *string `gorm:"column:remark;size:1000" json:"remark,omitempty"` // 备注|枚举:无|写入:创建/审批|FK:无|红线:脱敏 CreatedAt time.Time `gorm:"column:created_at;not null" json:"createdAt"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改 UpdatedAt time.Time `gorm:"column:updated_at;not null" json:"updatedAt"` // 更新时间(datetime(3),ON UPDATE)|枚举:无|写入:DB 自动|FK:无|红线:勿手改 } // TableName 指定表名。 func (UserBalanceRequest) TableName() string { return "t_user_balance_request" } // OrderPay 支付订单表 t_order_pay(旧 UUID 主键 → int64 + biz_no)。 // // 本期仅承接"创建/取消/回调"三动作;支付通道(JSAPI/W02 银联)适配层 P1(设计 §11-11)。 type OrderPay struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 订单 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:被 t_payment_log.order_id 引用|红线:对外单号是 biz_no BizNo string `gorm:"uniqueIndex:uk_biz_no;column:biz_no;size:32;not null" json:"bizNo"` // 业务单号(旧 UUID 原样保留;UNIQUE;对外暴露给前端/支付网关)|枚举:无|写入:下单时|FK:幂等/回滚锚点|红线:不可变(支付网关侧已登记) Status int8 `gorm:"index:idx_status;column:status;not null;default:0" json:"status"` // 状态(-2 失败/-1 取消/0 未支付/1 成功)|枚举:OrderPayStatus*|写入:下单/取消/回调|FK:无|红线:状态机单向(成功不可回退),回调重复须幂等 Type *string `gorm:"column:type;size:100" json:"type,omitempty"` // 订单类型(充值/报课…,旧字符串原样)|枚举:旧字符串枚举(不收敛)|写入:下单时|FK:无|红线:仅展示/分流,不参与金额计算 InstitutionID *int64 `gorm:"column:institution_id" json:"institutionId,omitempty"` // 机构 ID(原 schoolid;作用域列;可空)|枚举:无|写入:下单时|FK:→ t_institution|红线:NULL ≠ 0,勿混判 CampusID *int64 `gorm:"column:campus_id" json:"campusId,omitempty"` // 校区 ID(原 campusid;作用域列;可空)|枚举:无|写入:下单时|FK:→ t_campus|红线:NULL ≠ 0 UserID *string `gorm:"index:idx_user_create,priority:1;column:user_id;size:26" json:"userId,omitempty"` // 付款用户(ULID;原 userid)|枚举:无|写入:下单时|FK:→ t_user(逻辑)|红线:家长代付场景 ≠ create_user_id PayOrder *string `gorm:"index:idx_pay_order;column:pay_order;size:50" json:"payOrder,omitempty"` // 交易单号(第三方流水号)|枚举:无|写入:支付回调|FK:弱关联 t_payment_log|红线:非唯一,索引仅用于排障 PayType *string `gorm:"column:pay_type;size:10" json:"payType,omitempty"` // 支付方式(JSAPI 微信 / W02 银联 AllinPay)|枚举:JSAPI/W02|写入:下单时|FK:无|红线:新增通道须评审(对账口径) PayAccount *string `gorm:"column:pay_account;size:100" json:"-"` // 支付平台用户标识(原 pay_acct;openid/卡号)|枚举:无|写入:下单时|FK:无|红线:**json:"-" 不外泄**(openid/卡号属个人信息) PayMoney string `gorm:"column:pay_money;type:decimal(12,2);not null;default:0.00" json:"payMoney"` // 交易金额(元;旧 float(10,2) 收敛)|枚举:无|写入:下单时锁定|FK:无|红线:回调金额与订单金额不符 → 拒绝入账 PayTime *time.Time `gorm:"column:pay_time" json:"payTime,omitempty"` // 支付时间(datetime(3))|枚举:无|写入:回调成功|FK:无|红线:成功态必须非 NULL PayReturnData *string `gorm:"column:pay_return_data;type:longtext" json:"-"` // 支付返回数据(JSON 直存;含敏感信息)|枚举:无|写入:回调|FK:无|红线:**API 与日志均不得回显**(json:"-") CreateUserID *string `gorm:"column:create_user_id;size:26" json:"createUserId,omitempty"` // 下单人(ULID)|枚举:无|写入:下单时|FK:→ t_user(逻辑)|红线:家长代付场景与 user_id 不同 CreateTime time.Time `gorm:"index:idx_user_create,priority:2;column:create_time;not null" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改 Remark *string `gorm:"column:remark;size:500" json:"remark,omitempty"` // 用户备注|枚举:无|写入:下单时|FK:无|红线:用户输入,展示需转义 } // TableName 指定表名。 func (OrderPay) TableName() string { return "t_order_pay" } // PaymentLog 支付网关日志表 t_payment_log(**只读历史**;旧 UUID 主键 → int64 + biz_no)。 // // 本期纳管为只读:迁移保留、不新增写入(新支付回调日志写 t_audit_log + t_order_pay.pay_return_data, // 设计 §11-9)。是否重建新通道日志表列 P2。 type PaymentLog struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 日志 ID(D3 自增)|枚举:无|写入:仅迁移期|FK:无|红线:只读历史,禁止新写入 BizNo string `gorm:"uniqueIndex:uk_biz_no;column:biz_no;size:100;not null" json:"bizNo"` // 对外订单号(旧主键原样保留;UNIQUE)|枚举:无|写入:迁移搬入|FK:弱关联 t_order_pay.biz_no|红线:与 t_order_pay.biz_no 长度不同(100 vs 32),勿混用 UserID *string `gorm:"index:idx_user;column:user_id;size:26" json:"userId,omitempty"` // 用户(ULID)|枚举:无|写入:迁移搬入|FK:→ t_user(逻辑)|红线:历史可能缺失 OrderID *int64 `gorm:"index:idx_order;column:order_id" json:"orderId,omitempty"` // 订单 ID(原 order_id int → t_order_pay.id;迁移期 NULL 待回填)|枚举:无|写入:迁移回填|FK:→ t_order_pay.id|红线:NULL 期间不得据此做资金判断 PaymentType int32 `gorm:"column:payment_type;not null;default:0" json:"paymentType"` // 支付网关类型(旧 int 编码原样)|枚举:旧编码(不收敛,需对照 PHP 常量)|写入:迁移搬入|FK:无|红线:语义未定前不做分支 ReceiveTime *time.Time `gorm:"column:receive_time" json:"receiveTime,omitempty"` // 收到支付消息时间(datetime(3))|枚举:无|写入:迁移搬入|FK:无|红线:只读 ReturnData *string `gorm:"column:return_data;type:longtext" json:"-"` // 网关原始数据(JSON 直存)|枚举:无|写入:迁移搬入|FK:无|红线:**敏感,API/日志不得回显** CreateTime time.Time `gorm:"column:create_time;not null" json:"createTime"` // 生成时间(datetime(3))|枚举:无|写入:DB 默认/迁移|FK:无|红线:只读 Result int32 `gorm:"index:idx_result;column:result;not null;default:0" json:"result"` // 处理结果(旧编码原样)|枚举:旧编码(不收敛)|写入:迁移搬入|FK:无|红线:只读 } // TableName 指定表名。 func (PaymentLog) TableName() string { return "t_payment_log" }