// 活动域 7 表 model(t_courses_activity / _signed / _user_discount / _user_discount_log / // _used_log / _request / _visit)——与 academic-model-ddl.sql「第二部分」逐列对齐。 // // 业务主线(设计 §3.2,覆盖 9 个 SP): // // 活动方案(type 2 充值赠送/3 代金券/4 老带新) → 报名(signed, 状态机 0~4) → 赠送金额池(user_discount) // → 使用/退回流水(user_discount_log) → 老带新结算(used_log 写负值) → 优惠申请(request 审批) → 回访(visit) // // 字段注释五维口径:语义与单位 | 枚举全量取值 | 写入时机 | 主外键/关联语义 | 安全红线。 package model import "time" // t_courses_activity.type 活动类型编码(1 试听为旧遗留,不参与 SP 逻辑)。 const ( ActivityTypeTrial int8 = 1 // 试听活动(旧遗留,不参与 SP 逻辑) ActivityTypeRecharge int8 = 2 // 充值赠送 ActivityTypeVoucher int8 = 3 // 代金券 ActivityTypeRecommend int8 = 4 // 老带新推荐 ) // t_courses_activity.status 活动状态编码(旧 COMMENT 自相矛盾,以 PHP 判断口径为准,设计 §11-5)。 const ( ActivityStatusDisabled int8 = 0 // 停用 ActivityStatusEnabled int8 = 1 // 开启 ActivityStatusEnded int8 = 2 // 已结束 ) // t_courses_activity_signed.status 报名状态机(0 取消报名 → 1 报名 → 2 参加活动 → 3 报课成交 / 4 过期)。 const ( ActivitySignedCancelled int32 = 0 // 取消报名(旧 DELETE+INSERT 幂等会清掉的行状态) ActivitySignedSigned int32 = 1 // 报名 ActivitySignedAttended int32 = 2 // 参加活动(老带新结算后置为此态) ActivitySignedDeal int32 = 3 // 报课(成交) ActivitySignedExpired int32 = 4 // 过期不能使用 ) // t_courses_activity_request.approved 审批状态编码。 const ( ActivityRequestApprovedRejected int8 = -1 // 不通过 ActivityRequestApprovedPending int8 = 0 // 未处理 ActivityRequestApprovedPassed int8 = 1 // 通过 ) // t_courses_activity.toped 置顶标记编码。 const ( ActivityTopedNo int8 = 0 // 不置顶 ActivityTopedYes int8 = 1 // 置顶(置顶有效至活动结束) ) // CoursesActivity 活动表 t_courses_activity(充值赠送/代金券/老带新)。 // // 作用域:institution_id + campus_id(campus_id=0 表示"机构所有**自营**校区", // 是 custom_refund/cleared_balance 的 `campus_id IN (x,0)` 判据来源,设计 §2「7.」)。 type CoursesActivity struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 活动 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:被 signed/discount/used_log/request 引用|红线:报名表已落库后不得物理删除 Type int8 `gorm:"index:idx_inst_campus_type_status,priority:3;column:type;not null;default:1" json:"type"` // 活动类型(1 试听/2 充值赠送/3 代金券/4 老带新推荐)|枚举:ActivityType*|写入:创建活动时|FK:无|红线:决定结算分支(type=3 一次全退、type=4 双边入账),运行期禁改 Toped int8 `gorm:"column:toped;not null;default:0" json:"toped"` // 置顶(0 否/1 是,置顶有效至活动结束)|枚举:ActivityToped*|写入:后台编辑|FK:无|红线:仅展示序,不影响资金 Thumbnail *string `gorm:"column:thumbnail;size:500" json:"thumbnail,omitempty"` // 缩略图 URL|枚举:无|写入:后台编辑|FK:无|红线:外部 URL,渲染需校验域名白名单 SubjectID int32 `gorm:"column:subject_id;not null;default:0" json:"subjectId"` // 科目 ID(0 混合)|枚举:科目字典(跨域)|写入:创建活动时|FK:→ 教学域科目(无物理 FK)|红线:不参与金额计算 GradeID int32 `gorm:"column:grade_id;not null;default:0" json:"gradeId"` // 年级 ID(0 混合)|枚举:年级字典(跨域)|写入:创建活动时|FK:→ 教学域年级|红线:同上 InstitutionID int64 `gorm:"index:idx_inst_campus_type_status,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_type_status,priority:2;column:campus_id;not null;default:0" json:"campusId"` // 校区 ID(原 campusid;0=机构所有自营校区;作用域列)|枚举:无|写入:创建活动时锁定|FK:→ t_campus|红线:0 的活动失效范围依赖自营判定(设计 §11-2) StartTime *time.Time `gorm:"index:idx_time,priority:1;column:start_time" json:"startTime,omitempty"` // 活动开始时间(datetime(3))|枚举:无|写入:创建/编辑|FK:无|红线:报名须校验时间窗(3110) EndTime *time.Time `gorm:"index:idx_time,priority:2;column:end_time" json:"endTime,omitempty"` // 活动结束时间(datetime(3))|枚举:无|写入:创建/编辑|FK:无|红线:须 >= start_time NumberMax int32 `gorm:"column:number_max;not null;default:0" json:"numberMax"` // 可报名人数(0=不限)|枚举:无|写入:创建/编辑|FK:无|红线:与 number_sign 比较须在事务内(防超卖,3141) NumberSign int32 `gorm:"column:number_sign;not null;default:0" json:"numberSign"` // 当前报名人数(报名+1/取消-1)|枚举:无|写入:signed_add/cancel(原子增减)|FK:无|红线:取消时下限保护(不得为负) Title *string `gorm:"column:title;size:500" json:"title,omitempty"` // 标题|枚举:无|写入:创建/编辑|FK:无|红线:用户输入,展示需转义 Content *string `gorm:"column:content;type:longtext" json:"content,omitempty"` // 活动内容(HTML)|枚举:无|写入:创建/编辑|FK:无|红线:**富文本 XSS 风险**,输出须 sanitize Money string `gorm:"column:money;type:decimal(12,2);not null;default:0.00" json:"money"` // 金额/额度(元;充值赠送额/代金券面额/老带新奖励额)|枚举:无|写入:创建/编辑|FK:无|红线:报名时快照进 signed.activity_money,事后改活动不影响存量 Status int8 `gorm:"index:idx_inst_campus_type_status,priority:4;column:status;not null;default:0" json:"status"` // 状态(0 停用/1 开启/2 已结束)|枚举:ActivityStatus*|写入:后台编辑/到期任务|FK:无|红线:与报名表 status 同名不同义(设计 §11-5),勿混用 View int32 `gorm:"column:view;not null;default:0" json:"view"` // 浏览量(后台可见)|枚举:无|写入:详情页访问自增|FK:无|红线:非资金字段,可异步 Memo *string `gorm:"column:memo;size:500" json:"memo,omitempty"` // 备注|枚举:无|写入:创建/编辑|FK:无|红线:脱敏 CreateUserID *string `gorm:"column:create_user_id;size:26" json:"createUserId,omitempty"` // 创建人(ULID;原 create_userid)|枚举:无|写入:创建时|FK:→ t_user(逻辑)|红线:禁止旧 int id CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改 UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updatedAt"` // 更新时间(datetime(3),ON UPDATE;新增列)|枚举:无|写入:DB 自动|FK:无|红线:勿手改 } // TableName 指定表名。 func (CoursesActivity) TableName() string { return "t_courses_activity" } // CoursesActivitySigned 活动报名/结算表 t_courses_activity_signed(UK(user_id, activity_id))。 // // 幂等语义演进(设计 §1.5):旧 SP `signed_add` 用 DELETE+INSERT 保证"同一 user+activity 只留一条", // 目标态改为 `clause.OnConflict{DoUpdates: 覆盖 status/activity_money/recommend_user_id}` + UK 兜底。 // institution_id/campus_id 为**冗余作用域列**(取活动上的值),便于 scoping 查询(迁移后一次性回填)。 type CoursesActivitySigned struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 报名记录 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:注销不等于删除(status=0 保留审计) ActivityID int64 `gorm:"uniqueIndex:uk_user_activity,priority:2;index:idx_activity_status,priority:1;column:activity_id;not null" json:"activityId"` // 活动 ID(原 activityid)|枚举:无|写入:报名时|FK:→ t_courses_activity.id|红线:UK 第 2 列,禁止改绑 UserID string `gorm:"uniqueIndex:uk_user_activity,priority:1;index:idx_user_status,priority:1;column:user_id;size:26;not null" json:"userId"` // 报名用户(ULID;原 userid)|枚举:无|写入:报名时|FK:→ t_user(逻辑)|红线:UK 第 1 列,一活动一人一行 InstitutionID int64 `gorm:"index:idx_inst_campus,priority:1;column:institution_id;not null;default:0" json:"institutionId"` // 机构 ID(冗余作用域列,取活动值;新增)|枚举:无|写入:报名时冗余|FK:→ t_institution|红线:不得与活动表值冲突(迁移回填后一次性冻结) CampusID int64 `gorm:"index:idx_inst_campus,priority:2;column:campus_id;not null;default:0" json:"campusId"` // 校区 ID(冗余作用域列,取活动 campus_id;新增)|枚举:无|写入:报名时冗余|FK:→ t_campus|红线:同上 Status int32 `gorm:"index:idx_activity_status,priority:2;index:idx_user_status,priority:2;column:status;not null;default:0" json:"status"` // 报名状态(0 取消报名/1 报名/2 参加活动/3 报课成交/4 过期不能使用)|枚举:ActivitySigned*|写入:报名/结算/取消|FK:无|红线:状态机单向推进(2→1 仅取消路径);type=4 限一次判定按 dump 用**活动** status(§11-5) RechargeAmount string `gorm:"column:recharge_amount;type:decimal(12,2);not null;default:0.00" json:"rechargeAmount"` // 充值金额(元;copy_activity_discount_to_signed 回填)|枚举:无|写入:充值赠送结算/迁移重放|FK:无|红线:只读回填值,勿参与实时计算 ActivityMoney string `gorm:"column:activity_money;type:decimal(12,2);not null;default:0.00" json:"activityMoney"` // 活动金额(元;报名时取 t_courses_activity.money 快照)|枚举:无|写入:报名时|FK:无|红线:取消报名时归零(status=0 同行清 0) ActivityMoneyUsed string `gorm:"column:activity_money_used;type:decimal(12,2);not null;default:0.00" json:"activityMoneyUsed"` // 已使用活动金额(元;老带新=已充值到账户)|枚举:无|写入:结算|FK:无|红线:取消报名时归零;与 used_log 累计须一致 RecommendUserID *string `gorm:"column:recommend_user_id;size:26" json:"recommendUserId,omitempty"` // 推荐人(ULID;原 recommend_userid;0→NULL)|枚举:无|写入:报名时|FK:→ t_user(逻辑)|红线:双边入账的对手方,禁止自荐(业务校验) CreateUserID *string `gorm:"column:create_user_id;size:26" json:"createUserId,omitempty"` // 创建人(原 create_userid)|枚举:无|写入:报名时|FK:→ t_user(逻辑)|红线:禁止旧 int id CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"createTime"` // 报名时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改(对账依据) Imgs *string `gorm:"column:imgs;size:255" json:"imgs,omitempty"` // 图片备注(JSON 字符串)|枚举:无|写入:报名时|FK:无|红线:含个人信息,输出按需裁剪 Memo *string `gorm:"column:memo;size:500" json:"memo,omitempty"` // 备注|枚举:无|写入:报名/审核|FK:无|红线:脱敏 RequestNo *string `gorm:"index:idx_request_no;column:request_no;size:100" json:"requestNo,omitempty"` // 申请单号(原 requestid)|枚举:无|写入:报名时|FK:弱关联 t_user_balance_request.biz_no|红线:字符串保留(非 int64 FK),勿 JOIN id ResponseUserID *string `gorm:"column:response_user_id;size:26" json:"responseUserId,omitempty"` // 审核人(ULID;原 response_userid;非空表示已审核通过→不可取消)|枚举:无|写入:审核动作|FK:→ t_user(逻辑)|红线:**取消报名的核心判据**(3107) ResponseTime *time.Time `gorm:"column:response_time" json:"responseTime,omitempty"` // 审核时间(datetime(3))|枚举:无|写入:审核动作|FK:无|红线:与 response_user_id 同事务写 ResponseMsg *string `gorm:"column:response_msg;size:500" json:"responseMsg,omitempty"` // 审核意见|枚举:无|写入:审核动作|FK:无|红线:用户输入,展示转义 } // TableName 指定表名。 func (CoursesActivitySigned) TableName() string { return "t_courses_activity_signed" } // CoursesActivityUserDiscount 活动学生优惠表 t_courses_activity_user_discount(赠送金额池)。 // // 语义:discount_amount = 赠送总额(**负数是减、正数是赠送**),discount_amount_used = 已使用; // 可用 = discount_amount − discount_amount_used(user_discount_usable 仅累加 >0 的行,设计 §3.2 #14)。 type CoursesActivityUserDiscount struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 优惠账户 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:被 discount_log 引用|红线:资金池行,禁止物理删除(审计) ActivityID int64 `gorm:"index:idx_user_activity,priority:2;column:activity_id;not null" json:"activityId"` // 活动 ID(原 activityid)|枚举:无|写入:充值赠送入池时|FK:→ t_courses_activity.id|红线:决定赠送口径 InstitutionID int64 `gorm:"index:idx_inst_campus_user,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_user,priority:2;column:campus_id;not null;default:0" json:"campusId"` // 校区 ID(原 campusid;0=机构所有自营校区)|枚举:无|写入:入池时锁定|FK:→ t_campus|红线:usable 汇总按此过滤 UserID string `gorm:"index:idx_user_activity,priority:1;index:idx_inst_campus_user,priority:3;column:user_id;size:26;not null" json:"userId"` // 学生(ULID;原 userid)|枚举:无|写入:入池时|FK:→ t_user(逻辑)|红线:池归属人,禁止跨人使用 BalanceRequestID *int64 `gorm:"column:balance_request_id" json:"balanceRequestId,omitempty"` // 来源充值/退费工单 ID(原 balance_requestid varchar(32) → 迁移 JOIN biz_no 重映射)|枚举:无|写入:入池时|FK:→ t_user_balance_request.id|红线:NULL=历史/手工入池,不得据此反推金额 Income string `gorm:"column:income;type:decimal(12,2);not null;default:0.00" json:"income"` // 充值金额(元;触发赠送的充值额)|枚举:无|写入:入池时|FK:无|红线:审计口径列,只写不改 DiscountAmount string `gorm:"column:discount_amount;type:decimal(12,2);not null;default:0.00" json:"discountAmount"` // 赠送金额(元;负数=扣减/正数=赠送)|枚举:无|写入:入池时|FK:无|红线:可用额=本列−已用,禁止直接改本列做消费 DiscountAmountUsed string `gorm:"column:discount_amount_used;type:decimal(12,2);not null;default:0.00" json:"discountAmountUsed"` // 已使用赠送金额(元;<= discount_amount)|枚举:无|写入:使用/退回(原子增减)|FK:无|红线:须与 discount_log 累计一致(对账恒等式) CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改 } // TableName 指定表名。 func (CoursesActivityUserDiscount) TableName() string { return "t_courses_activity_user_discount" } // CoursesActivityUserDiscountLog 活动学生优惠使用记录表 t_courses_activity_user_discount_log(只增)。 // // 迁移说明:`courses_studentid`(旧 varchar(32) UUID)→ courses_student_id(int64 FK), // 同时保留 courses_student_no 旧 UUID 供迁移/对账(回填完成后可停用,设计 §2「10.」)。 type CoursesActivityUserDiscountLog struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 流水 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:只增不改(审计底线) ActivityUserDiscountID int64 `gorm:"index:idx_discount;column:activity_user_discount_id;not null" json:"activityUserDiscountId"` // 优惠账户 ID(原 activity_user_discountid)|枚举:无|写入:使用/退回时|FK:→ t_courses_activity_user_discount.id|红线:汇总须按本列分组核对 CoursesStudentID *int64 `gorm:"index:idx_student;column:courses_student_id" json:"coursesStudentId,omitempty"` // 报课记录 ID(原 courses_studentid UUID → t_courses_student.id;未回填前 NULL)|枚举:无|写入:使用/退回时|FK:→ t_courses_student.id|红线:NULL 期间用 courses_student_no 回溯 CoursesStudentNo *string `gorm:"index:idx_student_no;column:courses_student_no;size:32" json:"coursesStudentNo,omitempty"` // 报课记录旧单号(迁移期保留旧 UUID)|枚举:无|写入:迁移搬入|FK:弱关联 t_courses_student.biz_no|红线:回填完成后停用,禁止新写入 DiscountAmountUsed string `gorm:"column:discount_amount_used;type:decimal(12,2);not null;default:0.00" json:"discountAmountUsed"` // 变动金额(元;负=消费/正=退回)|枚举:无|写入:使用/退回时|FK:无|红线:符号方向固定,退回按 ABS 等额回冲 CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"createTime"` // 使用时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:同秒多行用 id 排序(退回游标 DESC) Memo *string `gorm:"column:memo;size:500" json:"memo,omitempty"` // 备注(默认文案"使用充值赠送金额")|枚举:无|写入:使用/退回时|FK:无|红线:脱敏 } // TableName 指定表名。 func (CoursesActivityUserDiscountLog) TableName() string { return "t_courses_activity_user_discount_log" } // CoursesActivityUsedLog 老带新活动获得/使用记录表 t_courses_activity_used_log(只增)。 // // 语义:老带新结算写**负值**(activity_money_used = −金额,表示"从活动池取出并充值到账户"); // `copy_activity_discountLog_to_usedLog` 全量重放按 discount_log 原值写入(可能为正)。 // 撤销前置校验:老带新有本表记录 → 不可取消报名(3108,设计 §3.2 #20)。 type CoursesActivityUsedLog struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 记录 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:只增不改 ActivityID int64 `gorm:"index:idx_activity_user,priority:1;column:activity_id;not null" json:"activityId"` // 活动 ID(原 activityid)|枚举:无|写入:结算/重放|FK:→ t_courses_activity.id|红线:撤销校验按 (activity,user) 判存在 UserID string `gorm:"index:idx_activity_user,priority:2;index:idx_user;column:user_id;size:26;not null" json:"userId"` // 参与用户(ULID;原 userid)|枚举:无|写入:结算/重放|FK:→ t_user(逻辑)|红线:与 signed.user_id 一致 RecommendUserID *string `gorm:"column:recommend_user_id;size:26" json:"recommendUserId,omitempty"` // 推荐人(ULID;原 recommend_userid;0→NULL)|枚举:无|写入:结算|FK:→ t_user(逻辑)|红线:双边入账对手方 OrderNo *string `gorm:"index:idx_order_no;column:order_no;size:100" json:"orderNo,omitempty"` // 订单号(原 orderid:老带新=报课信息 id;copy 重放=courses_studentid)|枚举:无|写入:结算/重放|FK:弱关联 t_courses_student.biz_no|红线:字符串保留,勿当 int64 FK ActivityMoneyUsed string `gorm:"column:activity_money_used;type:decimal(12,2);not null;default:0.00" json:"activityMoneyUsed"` // 活动金额变动(元;老带新结算=负值)|枚举:无|写入:结算/重放|FK:无|红线:符号方向 ★ 结算负值,重放可能正值(双跑重点比对) CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改 Memo *string `gorm:"column:memo;size:255" json:"memo,omitempty"` // 备注|枚举:无|写入:结算/重放|FK:无|红线:脱敏 } // TableName 指定表名。 func (CoursesActivityUsedLog) TableName() string { return "t_courses_activity_used_log" } // CoursesActivityRequest 活动优惠申请表 t_courses_activity_request(旧 UUID 主键 → int64 + biz_no)。 // // 审批:approved −1 不通过 / 0 未处理 / 1 通过;`copy_activity_discount_to_signed` 只取 // `approved=1 AND type=100`(设计 §3.6 #31)。 type CoursesActivityRequest struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 申请 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:对外单号是 biz_no BizNo string `gorm:"uniqueIndex:uk_biz_no;column:biz_no;size:32;not null" json:"bizNo"` // 业务单号(旧 UUID 原样保留;UNIQUE)|枚举:无|写入:申请时|FK:幂等/回滚锚点|红线:不可变 ActivityID int64 `gorm:"index:idx_activity_user,priority:1;column:activity_id;not null" json:"activityId"` // 活动 ID(原 activityid)|枚举:无|写入:申请时|FK:→ t_courses_activity.id|红线:审批通过后不可改绑 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 依赖 CampusID int64 `gorm:"index:idx_inst_campus_approved,priority:2;column:campus_id;not null;default:0" json:"campusId"` // 适用校区 ID(原 campusid;作用域列)|枚举:无|写入:申请时|FK:→ t_campus|红线:同上 UserID string `gorm:"index:idx_activity_user,priority:2;column:user_id;size:26;not null;default:''" json:"userId"` // 用户(ULID)|枚举:无|写入:申请时|FK:→ t_user(逻辑)|红线:与请求人可不同(代申请场景需审计) GiveAmount string `gorm:"column:give_amount;type:decimal(12,2);not null;default:0.00" json:"giveAmount"` // 优惠金额(元;旧 float(10,2))|枚举:无|写入:申请时|FK:无|红线:审批通过才入池,未通过不得预占 Imgs *string `gorm:"column:imgs;type:longtext" json:"-"` // 图片备注(JSON 直存)|枚举:无|写入:申请时|FK:无|红线:**不外泄**(json:"-") Memo *string `gorm:"column:memo;type:longtext" json:"memo,omitempty"` // 备注|枚举:无|写入:申请/审批|FK:无|红线:用户输入,展示转义 RequestUserID string `gorm:"column:request_user_id;size:26;not null;default:''" json:"requestUserId"` // 申请人(ULID)|枚举:无|写入:申请时|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;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;type:longtext" json:"responseMsg,omitempty"` // 审批意见|枚举:无|写入:审批动作|FK:无|红线:用户输入,转义 Approved int8 `gorm:"index:idx_inst_campus_approved,priority:3;column:approved;not null;default:0" json:"approved"` // 审批状态(-1 不通过/0 未处理/1 通过;原 aproved 错拼修正)|枚举:ActivityRequestApproved*|写入:审批动作|FK:无|红线:0→1 才可入池,重复审批幂等 CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"createdAt"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改 UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updatedAt"` // 更新时间(datetime(3),ON UPDATE)|枚举:无|写入:DB 自动|FK:无|红线:勿手改 } // TableName 指定表名。 func (CoursesActivityRequest) TableName() string { return "t_courses_activity_request" } // CoursesActivityVisit 活动回访记录表 t_courses_activity_visit。 // // 来源:旧库基本空表(AUTO_INCREMENT=1),字符集由 latin1 收敛 utf8mb4。 type CoursesActivityVisit struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 回访 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:只增不改 ActivitySignedID *int64 `gorm:"index:idx_signed;column:activity_signed_id" json:"activitySignedId,omitempty"` // 报名记录 ID(原 activity_signedid)|枚举:无|写入:回访登记时|FK:→ t_courses_activity_signed.id|红线:NULL=历史脏数据,勿据此做业务判断 CreateUserID *string `gorm:"column:create_user_id;size:26" json:"createUserId,omitempty"` // 回访人(ULID;原 create_userid)|枚举:无|写入:回访登记时|FK:→ t_user(逻辑)|红线:禁止旧 int id CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"createTime"` // 回访时间(datetime(3))|枚举:无|写入:DB 默认/录入|FK:无|红线:勿手改 Memo *string `gorm:"column:memo;size:500" json:"memo,omitempty"` // 回访内容|枚举:无|写入:回访登记时|FK:无|红线:含学员沟通内容,按权限裁剪输出 } // TableName 指定表名。 func (CoursesActivityVisit) TableName() string { return "t_courses_activity_visit" }