models_activity.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // 活动域 7 表 model(t_courses_activity / _signed / _user_discount / _user_discount_log /
  2. // _used_log / _request / _visit)——与 academic-model-ddl.sql「第二部分」逐列对齐。
  3. //
  4. // 业务主线(设计 §3.2,覆盖 9 个 SP):
  5. //
  6. // 活动方案(type 2 充值赠送/3 代金券/4 老带新) → 报名(signed, 状态机 0~4) → 赠送金额池(user_discount)
  7. // → 使用/退回流水(user_discount_log) → 老带新结算(used_log 写负值) → 优惠申请(request 审批) → 回访(visit)
  8. //
  9. // 字段注释五维口径:语义与单位 | 枚举全量取值 | 写入时机 | 主外键/关联语义 | 安全红线。
  10. package model
  11. import "time"
  12. // t_courses_activity.type 活动类型编码(1 试听为旧遗留,不参与 SP 逻辑)。
  13. const (
  14. ActivityTypeTrial int8 = 1 // 试听活动(旧遗留,不参与 SP 逻辑)
  15. ActivityTypeRecharge int8 = 2 // 充值赠送
  16. ActivityTypeVoucher int8 = 3 // 代金券
  17. ActivityTypeRecommend int8 = 4 // 老带新推荐
  18. )
  19. // t_courses_activity.status 活动状态编码(旧 COMMENT 自相矛盾,以 PHP 判断口径为准,设计 §11-5)。
  20. const (
  21. ActivityStatusDisabled int8 = 0 // 停用
  22. ActivityStatusEnabled int8 = 1 // 开启
  23. ActivityStatusEnded int8 = 2 // 已结束
  24. )
  25. // t_courses_activity_signed.status 报名状态机(0 取消报名 → 1 报名 → 2 参加活动 → 3 报课成交 / 4 过期)。
  26. const (
  27. ActivitySignedCancelled int32 = 0 // 取消报名(旧 DELETE+INSERT 幂等会清掉的行状态)
  28. ActivitySignedSigned int32 = 1 // 报名
  29. ActivitySignedAttended int32 = 2 // 参加活动(老带新结算后置为此态)
  30. ActivitySignedDeal int32 = 3 // 报课(成交)
  31. ActivitySignedExpired int32 = 4 // 过期不能使用
  32. )
  33. // t_courses_activity_request.approved 审批状态编码。
  34. const (
  35. ActivityRequestApprovedRejected int8 = -1 // 不通过
  36. ActivityRequestApprovedPending int8 = 0 // 未处理
  37. ActivityRequestApprovedPassed int8 = 1 // 通过
  38. )
  39. // t_courses_activity.toped 置顶标记编码。
  40. const (
  41. ActivityTopedNo int8 = 0 // 不置顶
  42. ActivityTopedYes int8 = 1 // 置顶(置顶有效至活动结束)
  43. )
  44. // CoursesActivity 活动表 t_courses_activity(充值赠送/代金券/老带新)。
  45. //
  46. // 作用域:institution_id + campus_id(campus_id=0 表示"机构所有**自营**校区",
  47. // 是 custom_refund/cleared_balance 的 `campus_id IN (x,0)` 判据来源,设计 §2「7.」)。
  48. type CoursesActivity struct {
  49. ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 活动 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:被 signed/discount/used_log/request 引用|红线:报名表已落库后不得物理删除
  50. 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 双边入账),运行期禁改
  51. Toped int8 `gorm:"column:toped;not null;default:0" json:"toped"` // 置顶(0 否/1 是,置顶有效至活动结束)|枚举:ActivityToped*|写入:后台编辑|FK:无|红线:仅展示序,不影响资金
  52. Thumbnail *string `gorm:"column:thumbnail;size:500" json:"thumbnail,omitempty"` // 缩略图 URL|枚举:无|写入:后台编辑|FK:无|红线:外部 URL,渲染需校验域名白名单
  53. SubjectID int32 `gorm:"column:subject_id;not null;default:0" json:"subjectId"` // 科目 ID(0 混合)|枚举:科目字典(跨域)|写入:创建活动时|FK:→ 教学域科目(无物理 FK)|红线:不参与金额计算
  54. GradeID int32 `gorm:"column:grade_id;not null;default:0" json:"gradeId"` // 年级 ID(0 混合)|枚举:年级字典(跨域)|写入:创建活动时|FK:→ 教学域年级|红线:同上
  55. 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 值
  56. 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)
  57. StartTime *time.Time `gorm:"index:idx_time,priority:1;column:start_time" json:"startTime,omitempty"` // 活动开始时间(datetime(3))|枚举:无|写入:创建/编辑|FK:无|红线:报名须校验时间窗(3110)
  58. EndTime *time.Time `gorm:"index:idx_time,priority:2;column:end_time" json:"endTime,omitempty"` // 活动结束时间(datetime(3))|枚举:无|写入:创建/编辑|FK:无|红线:须 >= start_time
  59. NumberMax int32 `gorm:"column:number_max;not null;default:0" json:"numberMax"` // 可报名人数(0=不限)|枚举:无|写入:创建/编辑|FK:无|红线:与 number_sign 比较须在事务内(防超卖,3141)
  60. NumberSign int32 `gorm:"column:number_sign;not null;default:0" json:"numberSign"` // 当前报名人数(报名+1/取消-1)|枚举:无|写入:signed_add/cancel(原子增减)|FK:无|红线:取消时下限保护(不得为负)
  61. Title *string `gorm:"column:title;size:500" json:"title,omitempty"` // 标题|枚举:无|写入:创建/编辑|FK:无|红线:用户输入,展示需转义
  62. Content *string `gorm:"column:content;type:longtext" json:"content,omitempty"` // 活动内容(HTML)|枚举:无|写入:创建/编辑|FK:无|红线:**富文本 XSS 风险**,输出须 sanitize
  63. Money string `gorm:"column:money;type:decimal(12,2);not null;default:0.00" json:"money"` // 金额/额度(元;充值赠送额/代金券面额/老带新奖励额)|枚举:无|写入:创建/编辑|FK:无|红线:报名时快照进 signed.activity_money,事后改活动不影响存量
  64. 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),勿混用
  65. View int32 `gorm:"column:view;not null;default:0" json:"view"` // 浏览量(后台可见)|枚举:无|写入:详情页访问自增|FK:无|红线:非资金字段,可异步
  66. Memo *string `gorm:"column:memo;size:500" json:"memo,omitempty"` // 备注|枚举:无|写入:创建/编辑|FK:无|红线:脱敏
  67. CreateUserID *string `gorm:"column:create_user_id;size:26" json:"createUserId,omitempty"` // 创建人(ULID;原 create_userid)|枚举:无|写入:创建时|FK:→ t_user(逻辑)|红线:禁止旧 int id
  68. CreateTime time.Time `gorm:"column:create_time;not null" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改
  69. UpdatedAt time.Time `gorm:"column:updated_at;not null" json:"updatedAt"` // 更新时间(datetime(3),ON UPDATE;新增列)|枚举:无|写入:DB 自动|FK:无|红线:勿手改
  70. }
  71. // TableName 指定表名。
  72. func (CoursesActivity) TableName() string { return "t_courses_activity" }
  73. // CoursesActivitySigned 活动报名/结算表 t_courses_activity_signed(UK(user_id, activity_id))。
  74. //
  75. // 幂等语义演进(设计 §1.5):旧 SP `signed_add` 用 DELETE+INSERT 保证"同一 user+activity 只留一条",
  76. // 目标态改为 `clause.OnConflict{DoUpdates: 覆盖 status/activity_money/recommend_user_id}` + UK 兜底。
  77. // institution_id/campus_id 为**冗余作用域列**(取活动上的值),便于 scoping 查询(迁移后一次性回填)。
  78. type CoursesActivitySigned struct {
  79. ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 报名记录 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:注销不等于删除(status=0 保留审计)
  80. 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 列,禁止改绑
  81. 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 列,一活动一人一行
  82. InstitutionID int64 `gorm:"index:idx_inst_campus,priority:1;column:institution_id;not null;default:0" json:"institutionId"` // 机构 ID(冗余作用域列,取活动值;新增)|枚举:无|写入:报名时冗余|FK:→ t_institution|红线:不得与活动表值冲突(迁移回填后一次性冻结)
  83. CampusID int64 `gorm:"index:idx_inst_campus,priority:2;column:campus_id;not null;default:0" json:"campusId"` // 校区 ID(冗余作用域列,取活动 campus_id;新增)|枚举:无|写入:报名时冗余|FK:→ t_campus|红线:同上
  84. 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)
  85. RechargeAmount string `gorm:"column:recharge_amount;type:decimal(12,2);not null;default:0.00" json:"rechargeAmount"` // 充值金额(元;copy_activity_discount_to_signed 回填)|枚举:无|写入:充值赠送结算/迁移重放|FK:无|红线:只读回填值,勿参与实时计算
  86. 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)
  87. ActivityMoneyUsed string `gorm:"column:activity_money_used;type:decimal(12,2);not null;default:0.00" json:"activityMoneyUsed"` // 已使用活动金额(元;老带新=已充值到账户)|枚举:无|写入:结算|FK:无|红线:取消报名时归零;与 used_log 累计须一致
  88. RecommendUserID *string `gorm:"column:recommend_user_id;size:26" json:"recommendUserId,omitempty"` // 推荐人(ULID;原 recommend_userid;0→NULL)|枚举:无|写入:报名时|FK:→ t_user(逻辑)|红线:双边入账的对手方,禁止自荐(业务校验)
  89. CreateUserID *string `gorm:"column:create_user_id;size:26" json:"createUserId,omitempty"` // 创建人(原 create_userid)|枚举:无|写入:报名时|FK:→ t_user(逻辑)|红线:禁止旧 int id
  90. CreateTime time.Time `gorm:"column:create_time;not null" json:"createTime"` // 报名时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改(对账依据)
  91. Imgs *string `gorm:"column:imgs;size:255" json:"imgs,omitempty"` // 图片备注(JSON 字符串)|枚举:无|写入:报名时|FK:无|红线:含个人信息,输出按需裁剪
  92. Memo *string `gorm:"column:memo;size:500" json:"memo,omitempty"` // 备注|枚举:无|写入:报名/审核|FK:无|红线:脱敏
  93. 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
  94. ResponseUserID *string `gorm:"column:response_user_id;size:26" json:"responseUserId,omitempty"` // 审核人(ULID;原 response_userid;非空表示已审核通过→不可取消)|枚举:无|写入:审核动作|FK:→ t_user(逻辑)|红线:**取消报名的核心判据**(3107)
  95. ResponseTime *time.Time `gorm:"column:response_time" json:"responseTime,omitempty"` // 审核时间(datetime(3))|枚举:无|写入:审核动作|FK:无|红线:与 response_user_id 同事务写
  96. ResponseMsg *string `gorm:"column:response_msg;size:500" json:"responseMsg,omitempty"` // 审核意见|枚举:无|写入:审核动作|FK:无|红线:用户输入,展示转义
  97. }
  98. // TableName 指定表名。
  99. func (CoursesActivitySigned) TableName() string { return "t_courses_activity_signed" }
  100. // CoursesActivityUserDiscount 活动学生优惠表 t_courses_activity_user_discount(赠送金额池)。
  101. //
  102. // 语义:discount_amount = 赠送总额(**负数是减、正数是赠送**),discount_amount_used = 已使用;
  103. // 可用 = discount_amount − discount_amount_used(user_discount_usable 仅累加 >0 的行,设计 §3.2 #14)。
  104. type CoursesActivityUserDiscount struct {
  105. ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 优惠账户 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:被 discount_log 引用|红线:资金池行,禁止物理删除(审计)
  106. ActivityID int64 `gorm:"index:idx_user_activity,priority:2;column:activity_id;not null" json:"activityId"` // 活动 ID(原 activityid)|枚举:无|写入:充值赠送入池时|FK:→ t_courses_activity.id|红线:决定赠送口径
  107. 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 值
  108. 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 汇总按此过滤
  109. 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(逻辑)|红线:池归属人,禁止跨人使用
  110. 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=历史/手工入池,不得据此反推金额
  111. Income string `gorm:"column:income;type:decimal(12,2);not null;default:0.00" json:"income"` // 充值金额(元;触发赠送的充值额)|枚举:无|写入:入池时|FK:无|红线:审计口径列,只写不改
  112. DiscountAmount string `gorm:"column:discount_amount;type:decimal(12,2);not null;default:0.00" json:"discountAmount"` // 赠送金额(元;负数=扣减/正数=赠送)|枚举:无|写入:入池时|FK:无|红线:可用额=本列−已用,禁止直接改本列做消费
  113. DiscountAmountUsed string `gorm:"column:discount_amount_used;type:decimal(12,2);not null;default:0.00" json:"discountAmountUsed"` // 已使用赠送金额(元;<= discount_amount)|枚举:无|写入:使用/退回(原子增减)|FK:无|红线:须与 discount_log 累计一致(对账恒等式)
  114. CreateTime time.Time `gorm:"column:create_time;not null" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改
  115. }
  116. // TableName 指定表名。
  117. func (CoursesActivityUserDiscount) TableName() string { return "t_courses_activity_user_discount" }
  118. // CoursesActivityUserDiscountLog 活动学生优惠使用记录表 t_courses_activity_user_discount_log(只增)。
  119. //
  120. // 迁移说明:`courses_studentid`(旧 varchar(32) UUID)→ courses_student_id(int64 FK),
  121. // 同时保留 courses_student_no 旧 UUID 供迁移/对账(回填完成后可停用,设计 §2「10.」)。
  122. type CoursesActivityUserDiscountLog struct {
  123. ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 流水 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:只增不改(审计底线)
  124. 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|红线:汇总须按本列分组核对
  125. 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 回溯
  126. CoursesStudentNo *string `gorm:"index:idx_student_no;column:courses_student_no;size:32" json:"coursesStudentNo,omitempty"` // 报课记录旧单号(迁移期保留旧 UUID)|枚举:无|写入:迁移搬入|FK:弱关联 t_courses_student.biz_no|红线:回填完成后停用,禁止新写入
  127. DiscountAmountUsed string `gorm:"column:discount_amount_used;type:decimal(12,2);not null;default:0.00" json:"discountAmountUsed"` // 变动金额(元;负=消费/正=退回)|枚举:无|写入:使用/退回时|FK:无|红线:符号方向固定,退回按 ABS 等额回冲
  128. CreateTime time.Time `gorm:"column:create_time;not null" json:"createTime"` // 使用时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:同秒多行用 id 排序(退回游标 DESC)
  129. Memo *string `gorm:"column:memo;size:500" json:"memo,omitempty"` // 备注(默认文案"使用充值赠送金额")|枚举:无|写入:使用/退回时|FK:无|红线:脱敏
  130. }
  131. // TableName 指定表名。
  132. func (CoursesActivityUserDiscountLog) TableName() string {
  133. return "t_courses_activity_user_discount_log"
  134. }
  135. // CoursesActivityUsedLog 老带新活动获得/使用记录表 t_courses_activity_used_log(只增)。
  136. //
  137. // 语义:老带新结算写**负值**(activity_money_used = −金额,表示"从活动池取出并充值到账户");
  138. // `copy_activity_discountLog_to_usedLog` 全量重放按 discount_log 原值写入(可能为正)。
  139. // 撤销前置校验:老带新有本表记录 → 不可取消报名(3108,设计 §3.2 #20)。
  140. type CoursesActivityUsedLog struct {
  141. ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 记录 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:只增不改
  142. 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) 判存在
  143. 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 一致
  144. RecommendUserID *string `gorm:"column:recommend_user_id;size:26" json:"recommendUserId,omitempty"` // 推荐人(ULID;原 recommend_userid;0→NULL)|枚举:无|写入:结算|FK:→ t_user(逻辑)|红线:双边入账对手方
  145. 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
  146. ActivityMoneyUsed string `gorm:"column:activity_money_used;type:decimal(12,2);not null;default:0.00" json:"activityMoneyUsed"` // 活动金额变动(元;老带新结算=负值)|枚举:无|写入:结算/重放|FK:无|红线:符号方向 ★ 结算负值,重放可能正值(双跑重点比对)
  147. CreateTime time.Time `gorm:"column:create_time;not null" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改
  148. Memo *string `gorm:"column:memo;size:255" json:"memo,omitempty"` // 备注|枚举:无|写入:结算/重放|FK:无|红线:脱敏
  149. }
  150. // TableName 指定表名。
  151. func (CoursesActivityUsedLog) TableName() string { return "t_courses_activity_used_log" }
  152. // CoursesActivityRequest 活动优惠申请表 t_courses_activity_request(旧 UUID 主键 → int64 + biz_no)。
  153. //
  154. // 审批:approved −1 不通过 / 0 未处理 / 1 通过;`copy_activity_discount_to_signed` 只取
  155. // `approved=1 AND type=100`(设计 §3.6 #31)。
  156. type CoursesActivityRequest struct {
  157. ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 申请 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:对外单号是 biz_no
  158. BizNo string `gorm:"uniqueIndex:uk_biz_no;column:biz_no;size:32;not null" json:"bizNo"` // 业务单号(旧 UUID 原样保留;UNIQUE)|枚举:无|写入:申请时|FK:幂等/回滚锚点|红线:不可变
  159. ActivityID int64 `gorm:"index:idx_activity_user,priority:1;column:activity_id;not null" json:"activityId"` // 活动 ID(原 activityid)|枚举:无|写入:申请时|FK:→ t_courses_activity.id|红线:审批通过后不可改绑
  160. 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 依赖
  161. CampusID int64 `gorm:"index:idx_inst_campus_approved,priority:2;column:campus_id;not null;default:0" json:"campusId"` // 适用校区 ID(原 campusid;作用域列)|枚举:无|写入:申请时|FK:→ t_campus|红线:同上
  162. UserID string `gorm:"index:idx_activity_user,priority:2;column:user_id;size:26;not null;default:''" json:"userId"` // 用户(ULID)|枚举:无|写入:申请时|FK:→ t_user(逻辑)|红线:与请求人可不同(代申请场景需审计)
  163. GiveAmount string `gorm:"column:give_amount;type:decimal(12,2);not null;default:0.00" json:"giveAmount"` // 优惠金额(元;旧 float(10,2))|枚举:无|写入:申请时|FK:无|红线:审批通过才入池,未通过不得预占
  164. Imgs *string `gorm:"column:imgs;type:longtext" json:"-"` // 图片备注(JSON 直存)|枚举:无|写入:申请时|FK:无|红线:**不外泄**(json:"-")
  165. Memo *string `gorm:"column:memo;type:longtext" json:"memo,omitempty"` // 备注|枚举:无|写入:申请/审批|FK:无|红线:用户输入,展示转义
  166. RequestUserID string `gorm:"column:request_user_id;size:26;not null;default:''" json:"requestUserId"` // 申请人(ULID)|枚举:无|写入:申请时|FK:→ t_user(逻辑)|红线:禁止旧 int id
  167. RequestTime *time.Time `gorm:"column:request_time" json:"requestTime,omitempty"` // 申请时间(datetime(3))|枚举:无|写入:提交时|FK:无|红线:只写一次
  168. ResponseUserID *string `gorm:"column:response_user_id;size:26" json:"responseUserId,omitempty"` // 审批人(ULID;0→NULL)|枚举:无|写入:审批动作|FK:→ t_user(逻辑)|红线:申请人不得自审(Service 校验)
  169. ResponseTime *time.Time `gorm:"column:response_time" json:"responseTime,omitempty"` // 审批时间(datetime(3))|枚举:无|写入:审批动作|FK:无|红线:与 approved 同事务
  170. ResponseMsg *string `gorm:"column:response_msg;type:longtext" json:"responseMsg,omitempty"` // 审批意见|枚举:无|写入:审批动作|FK:无|红线:用户输入,转义
  171. 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 才可入池,重复审批幂等
  172. CreatedAt time.Time `gorm:"column:created_at;not null" json:"createdAt"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改
  173. UpdatedAt time.Time `gorm:"column:updated_at;not null" json:"updatedAt"` // 更新时间(datetime(3),ON UPDATE)|枚举:无|写入:DB 自动|FK:无|红线:勿手改
  174. }
  175. // TableName 指定表名。
  176. func (CoursesActivityRequest) TableName() string { return "t_courses_activity_request" }
  177. // CoursesActivityVisit 活动回访记录表 t_courses_activity_visit。
  178. //
  179. // 来源:旧库基本空表(AUTO_INCREMENT=1),字符集由 latin1 收敛 utf8mb4。
  180. type CoursesActivityVisit struct {
  181. ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 回访 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:只增不改
  182. ActivitySignedID *int64 `gorm:"index:idx_signed;column:activity_signed_id" json:"activitySignedId,omitempty"` // 报名记录 ID(原 activity_signedid)|枚举:无|写入:回访登记时|FK:→ t_courses_activity_signed.id|红线:NULL=历史脏数据,勿据此做业务判断
  183. CreateUserID *string `gorm:"column:create_user_id;size:26" json:"createUserId,omitempty"` // 回访人(ULID;原 create_userid)|枚举:无|写入:回访登记时|FK:→ t_user(逻辑)|红线:禁止旧 int id
  184. CreateTime time.Time `gorm:"column:create_time;not null" json:"createTime"` // 回访时间(datetime(3))|枚举:无|写入:DB 默认/录入|FK:无|红线:勿手改
  185. Memo *string `gorm:"column:memo;size:500" json:"memo,omitempty"` // 回访内容|枚举:无|写入:回访登记时|FK:无|红线:含学员沟通内容,按权限裁剪输出
  186. }
  187. // TableName 指定表名。
  188. func (CoursesActivityVisit) TableName() string { return "t_courses_activity_visit" }