| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- // 课酬与收支 6 表 model(t_course_payment / _detail / t_teacher_information /
- // t_teacher_courses_percentage / t_expenditure / t_expenditure_log)
- // —— 与 academic-model-ddl.sql「第五部分」逐列对齐。
- //
- // 覆盖 SP:course_payment_detail_get_teacher_detail(课酬多级回退匹配),
- // 匹配顺序(设计 §3.4 #26):
- // 1. 方案两级回退:campus_id 命中 → 回退 campus_id=0(机构级);
- // 2. 规则三级回退:个人(teacher_id=uid)→ 持证通用(teacher_id 空 + teacher_cert=1)→ 无证通用(teacher_cert=0);
- // 3. grade_id / subject_id 支持 −1 通配;教龄区间用 BETWEEN(teacher_exp_min..max)。
- //
- // 无可用方案 → 3112(apperr.ErrPayrollPlanMissing)。
- //
- // 字段注释五维口径:语义与单位 | 枚举全量取值 | 写入时机 | 主外键/关联语义 | 安全红线。
- package model
- import "time"
- // t_teacher_courses_percentage.payment_method 结算方式编码。
- const (
- PayrollPaymentMethodPercentage int8 = 1 // 提成结算
- PayrollPaymentMethodCash int8 = 2 // 现金结算
- )
- // t_expenditure.type 收支类型编码。
- const (
- ExpenditureTypeExpense int32 = 1 // 支出
- ExpenditureTypeIncome int32 = 2 // 收入
- )
- // CoursePayment 课酬方案表 t_course_payment(机构/校区级,按生效日期取最新)。
- type CoursePayment struct {
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 方案 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:被 course_payment_detail / classify 引用|红线:被规则引用后禁删
- InstitutionID int64 `gorm:"index:idx_inst_campus_actived_effect,priority:1;column:institution_id;not null;default:0" json:"institutionId"` // 机构 ID(原 schoolid;0=所有机构;作用域列)|枚举:无|写入:创建方案时|FK:→ t_institution|红线:0 表示通用方案,scoping 时需显式放行(评审确认)
- CampusID int64 `gorm:"index:idx_inst_campus_actived_effect,priority:2;column:campus_id;not null;default:0" json:"campusId"` // 校区 ID(原 campusid;0=机构所有校区)|枚举:无|写入:创建方案时|FK:→ t_campus|红线:两级回退的第二级依据
- Name *string `gorm:"column:name;size:200" json:"name,omitempty"` // 方案名称|枚举:无|写入:创建/编辑|FK:无|红线:展示转义
- EffectDate *time.Time `gorm:"index:idx_inst_campus_actived_effect,priority:4;column:effect_date" json:"effectDate,omitempty"` // 生效日期(datetime(3);取 effect_date<=当前 最新一条)|枚举:无|写入:创建/编辑|FK:无|红线:同机构校区多方案按本列 DESC 取首条
- Actived bool `gorm:"index:idx_inst_campus_actived_effect,priority:3;column:actived;not null;default:0" json:"actived"` // 是否生效(bit(1)→tinyint(1)/bool)|枚举:false/true|写入:启用/停用|FK:无|红线:仅 actived=true 参与匹配
- 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" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改
- Memo *string `gorm:"column:memo;type:longtext" json:"memo,omitempty"` // 备注|枚举:无|写入:创建/编辑|FK:无|红线:脱敏
- }
- // TableName 指定表名。
- func (CoursePayment) TableName() string { return "t_course_payment" }
- // CoursePaymentDetail 课酬明细规则表 t_course_payment_detail(个人→持证通用→无证通用 三级回退)。
- //
- // grade_id / subject_id 支持 −1 通配(匹配逻辑保留旧口径,设计 §3.4 #26);
- // percentage 为 1 位小数的百分比数字部分(如 65.5 表示 65.5%)。
- type CoursePaymentDetail struct {
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 明细 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:试算按 id ASC 取首条命中(回退优先级靠查询顺序)
- CoursePaymentID int64 `gorm:"index:idx_payment;column:course_payment_id;not null" json:"coursePaymentId"` // 课酬方案 ID(原 course_paymentid)|枚举:无|写入:创建规则时|FK:→ t_course_payment.id|红线:方案停用即整组失效
- GradeID int32 `gorm:"column:grade_id;not null;default:-1" json:"gradeId"` // 年级 ID(-1 所有/0 混合)|枚举:−1 通配 + 年级字典|写入:创建规则|FK:→ 教学域年级|红线:−1 通配逻辑不得写成 `= -1` 强制条件
- SubjectID int32 `gorm:"column:subject_id;not null;default:-1" json:"subjectId"` // 科目 ID(-1 所有/0 混合)|枚举:−1 通配 + 科目字典|写入:创建规则|FK:→ 教学域科目|红线:同上
- ClassType int32 `gorm:"index:idx_teacher_classify,priority:2;column:class_type;not null;default:0" json:"classType"` // 班级类型(对应 t_courses_classify.class_type)|枚举:班型编码|写入:创建规则|FK:弱关联 t_courses_classify.class_type|红线:匹配维度
- ClassifyID int64 `gorm:"index:idx_teacher_classify,priority:3;column:classify_id;not null;default:0" json:"classifyId"` // 课程分类 ID(原 classifyid;个人配置维度)|枚举:无|写入:创建规则|FK:→ t_courses_classify.id|红线:0=不限定分类
- TeacherID *string `gorm:"index:idx_teacher_classify,priority:1;column:teacher_id;size:26" json:"teacherId,omitempty"` // 老师(ULID;NULL/空=通用规则)|枚举:无|写入:个人规则时|FK:→ t_user(逻辑)|红线:三级回退的第一级判据(个人优先)
- Name *string `gorm:"column:name;size:300" json:"name,omitempty"` // 计算方式名称|枚举:无|写入:创建规则|FK:无|红线:展示转义
- Percentage string `gorm:"column:percentage;type:decimal(5,1);not null;default:0.0" json:"percentage"` // 课酬比例(百分比数字部分,1 位小数;旧 float(10,1))|枚举:0~100 常见|写入:创建规则|FK:无|红线:精度例外列;参与计算须走整数运算(分×比例→四舍五入)
- MinPayment string `gorm:"column:min_payment;type:decimal(12,2);not null;default:0.00" json:"minPayment"` // 最低课酬标准(元)|枚举:无|写入:创建规则|FK:无|红线:<= max_payment(0 表示不限制)
- MaxPayment string `gorm:"column:max_payment;type:decimal(12,2);not null;default:0.00" json:"maxPayment"` // 最高课酬标准(元)|枚举:无|写入:创建规则|FK:无|红线:0 表示不限制
- Reward string `gorm:"column:reward;type:decimal(12,2);not null;default:0.00" json:"reward"` // 额外课酬(元)|枚举:无|写入:创建规则|FK:无|红线:在比例基础上追加
- TeacherCert bool `gorm:"index:idx_cert_exp,priority:1;column:teacher_cert;not null;default:0" json:"teacherCert"` // 是否持证(bit(1)→tinyint(1)/bool)|枚举:false 无证/true 持证|写入:创建规则|FK:弱关联 t_teacher_information.teacher_cert|红线:第二级回退判据(持证通用)
- TeacherExpMin string `gorm:"index:idx_cert_exp,priority:2;column:teacher_exp_min;type:decimal(6,2);not null;default:0.00" json:"teacherExpMin"` // 教龄下限(年;旧 float(10,3) → 2 位)|枚举:无|写入:创建规则|FK:无|红线:精度例外列;BETWEEN 匹配
- TeacherExpMax string `gorm:"index:idx_cert_exp,priority:3;column:teacher_exp_max;type:decimal(6,2);not null;default:0.00" json:"teacherExpMax"` // 教龄上限(年)|枚举:无|写入:创建规则|FK:无|红线:>= teacher_exp_min
- Actived bool `gorm:"column:actived;not null;default:0" json:"actived"` // 是否生效(bit(1)→tinyint(1)/bool)|枚举:false/true|写入:启用/停用|FK:无|红线:仅 true 参与匹配
- Memo *string `gorm:"column:memo;type:longtext" json:"memo,omitempty"` // 备注|枚举:无|写入:创建/编辑|FK:无|红线:脱敏
- }
- // TableName 指定表名。
- func (CoursePaymentDetail) TableName() string { return "t_course_payment_detail" }
- // TeacherInformation 教师扩展信息表 t_teacher_information(资格证/教龄·自然键 teacher_id)。
- //
- // 作用域:**无** institution_id/campus_id(教师是平台级人员属性,跨机构共享)——
- // Repository 不继承 BaseRepo,越权断言在 Service 层(设计 §1.7 例外一)。
- type TeacherInformation struct {
- TeacherID string `gorm:"column:teacher_id;primaryKey;size:26;not null" json:"teacherId"` // 老师(ULID;原 teacherid,主键)|枚举:无|写入:首次填资质时|FK:→ t_user(逻辑)|红线:一人一行
- TeacherCert bool `gorm:"column:teacher_cert;not null;default:0" json:"teacherCert"` // 教师资格证(bit(1)→tinyint(1)/bool;0 无/1 有)|枚举:false/true|写入:资质维护|FK:无|红线:课酬第二级回退判据
- TeacherExp *time.Time `gorm:"column:teacher_exp;type:date" json:"teacherExp,omitempty"` // 教龄起算日(date)|枚举:无|写入:资质维护|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 (TeacherInformation) TableName() string { return "t_teacher_information" }
- // TeacherCoursesPercentage 老师课酬提成表 t_teacher_courses_percentage(提成/固定两制·UK(type, teacher_id))。
- //
- // `*_extend` 列为按年级/科目分档的 JSON/逗号扩展串,目标态**原样保留字符串**不结构化
- // (保结构稳定;解析在 Service 层,设计 §11-3 待补盘点)。
- type TeacherCoursesPercentage struct {
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 记录 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:无|红线:UK(type,teacher_id) 保证一人一类型一行
- Type int32 `gorm:"uniqueIndex:uk_type_teacher,priority:1;column:type;not null;default:1000" json:"type"` // 课程类型(class_type)|枚举:班型编码(1000 集体班…)|写入:配置时|FK:弱关联 t_courses_classify.class_type|红线:UK 第 1 列
- PaymentMethod int8 `gorm:"column:payment_method;not null;default:1" json:"paymentMethod"` // 结算方式(1 提成结算/2 现金结算)|枚举:PayrollPaymentMethod*|写入:配置时|FK:无|红线:决定试算分支
- TeacherID string `gorm:"uniqueIndex:uk_type_teacher,priority:2;column:teacher_id;size:26;not null" json:"teacherId"` // 老师(ULID;原 teacherid)|枚举:无|写入:配置时|FK:→ t_user(逻辑)|红线:UK 第 2 列
- Percentage int32 `gorm:"column:percentage;not null;default:0" json:"percentage"` // 提成比例(%,整数)|枚举:0~100|写入:配置时|FK:无|红线:与 extend 分档冲突时以 extend 为准(旧口径)
- Cost string `gorm:"column:cost;type:decimal(12,2);not null;default:0.00" json:"cost"` // 一次课课酬(元)|枚举:无|写入:配置时|FK:无|红线:现金结算基数
- AddedCost string `gorm:"column:added_cost;type:decimal(12,2);not null;default:0.00" json:"addedCost"` // 额外费用(元;比例基础上追加)|枚举:无|写入:配置时|FK:无|红线:追加项须可追溯
- PercentageExtend *string `gorm:"column:percentage_extend;size:500" json:"percentageExtend,omitempty"` // 提成比例扩展(按年级/科目分档,原样保留)|枚举:旧扩展串格式|写入:配置时|FK:无|红线:**不结构化**(解析在 Service),勿在此做 JSON 校验
- CostExtend *string `gorm:"column:cost_extend;size:500" json:"costExtend,omitempty"` // 固定课酬扩展(原样保留)|枚举:旧扩展串格式|写入:配置时|FK:无|红线:同上
- MaxReward string `gorm:"column:max_reward;type:decimal(12,2);not null;default:0.00" json:"maxReward"` // 最大课酬(元/一次课)|枚举:无|写入:配置时|FK:无|红线:0=不限制
- MaxRewardExtend *string `gorm:"column:max_reward_extend;type:longtext" json:"maxRewardExtend,omitempty"` // 最大课酬扩展(按年级分档)|枚举:旧扩展串格式|写入:配置时|FK:无|红线:同上
- MinReward string `gorm:"column:min_reward;type:decimal(12,2);not null;default:0.00" json:"minReward"` // 最小课酬(元/一次课)|枚举:无|写入:配置时|FK:无|红线:0=不限制
- MinRewardExtend *string `gorm:"column:min_reward_extend;size:500" json:"minRewardExtend,omitempty"` // 最小课酬扩展(原样保留)|枚举:旧扩展串格式|写入:配置时|FK:无|红线:同上
- EffectiveDate *time.Time `gorm:"column:effective_date;type:date" json:"effectiveDate,omitempty"` // 课酬生效日期(date)|枚举:无|写入:配置时|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 (TeacherCoursesPercentage) TableName() string { return "t_teacher_courses_percentage" }
- // Expenditure 收支类型字典表 t_expenditure(自引用树·支出/收入)。
- type Expenditure struct {
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 类型 ID(D3 自增)|枚举:无|写入:INSERT 自动|FK:被 expenditure_log.expenditure_id 引用|红线:被引用后禁删(改 disabled 语义由 status 缺列限制,本期只禁删)
- Type int32 `gorm:"index:idx_inst_type,priority:2;column:type;not null;default:1" json:"type"` // 类型(1 支出/2 收入)|枚举:ExpenditureType*|写入:创建字典项|FK:无|红线:决定报表方向(正负)
- ParentID int64 `gorm:"index:idx_parent;column:parent_id;not null;default:0" json:"parentId"` // 父类型 ID(原 parentid;0=顶级)|枚举:无|写入:创建字典项|FK:→ 本表 id(自引用)|红线:禁止成环
- Name string `gorm:"column:name;size:500;not null" json:"name"` // 名称|枚举:无|写入:创建/编辑|FK:无|红线:展示转义
- Remark *string `gorm:"column:remark;type:longtext" json:"remark,omitempty"` // 备注|枚举:无|写入:创建/编辑|FK:无|红线:脱敏
- InstitutionID int64 `gorm:"index:idx_inst_type,priority:1;column:institution_id;not null;default:0" json:"institutionId"` // 机构 ID(新增作用域列;0=平台通用字典)|枚举:无|写入:创建时|FK:→ t_institution|红线:0 为平台字典(scoping 需放行 0 行,评审确认)
- }
- // TableName 指定表名。
- func (Expenditure) TableName() string { return "t_expenditure" }
- // ExpenditureLog 收支记录表 t_expenditure_log(旧 UUID 主键 → int64 + biz_no)。
- //
- // year/month 为报表分桶冗余列(原 year year 类型 / month tinyint),保留以避免月度报表改 SQL。
- type ExpenditureLog 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:迁移/回滚锚点|红线:不可变
- InstitutionID int64 `gorm:"index:idx_inst_campus_time,priority:1;column:institution_id;not null;default:0" json:"institutionId"` // 机构 ID(原 schoolid;作用域列)|枚举:无|写入:记账时|FK:→ t_institution|红线:scoping 强依赖
- CampusID int64 `gorm:"index:idx_inst_campus_time,priority:2;column:campus_id;not null;default:0" json:"campusId"` // 校区 ID(原 campusid;作用域列)|枚举:无|写入:记账时|FK:→ t_campus|红线:同上
- ExpenditureID int64 `gorm:"index:idx_expenditure;column:expenditure_id;not null;default:0" json:"expenditureId"` // 收支类型 ID(原 expenditureid)|枚举:无|写入:记账时|FK:→ t_expenditure.id|红线:决定支出/收入方向
- Cost string `gorm:"column:cost;type:decimal(12,2);not null;default:0.00" json:"cost"` // 费用(元;旧 float(10,3) 收敛)|枚举:无|写入:记账时|FK:无|红线:符号由 type 决定,本列存正数
- CreateUserID *string `gorm:"column:create_user_id;size:26" json:"createUserId,omitempty"` // 创建人(ULID;原 create_userid)|枚举:无|写入:记账时|FK:→ t_user(逻辑)|红线:禁止旧 int id
- CreateTime time.Time `gorm:"index:idx_inst_campus_time,priority:3;column:create_time;not null" json:"createTime"` // 创建时间(datetime(3))|枚举:无|写入:DB 默认|FK:无|红线:勿手改
- Year *int16 `gorm:"index:idx_year_month,priority:1;column:year" json:"year,omitempty"` // 收支年份(报表分桶冗余列,原 year 类型)|枚举:如 2026|写入:记账时冗余|FK:无|红线:与 create_time 年份须一致(一致性校验)
- Month *int8 `gorm:"index:idx_year_month,priority:2;column:month" json:"month,omitempty"` // 收支月份(报表分桶冗余列,原 month tinyint)|枚举:1~12|写入:记账时冗余|FK:无|红线:同上
- Remark *string `gorm:"column:remark;type:longtext" json:"remark,omitempty"` // 备注|枚举:无|写入:记账时|FK:无|红线:脱敏
- Imgs *string `gorm:"column:imgs;type:longtext" json:"-"` // 图片地址(JSON 直存)|枚举:无|写入:记账时|FK:无|红线:**凭证可能含敏感信息,json:"-"**(按需授权下载)
- }
- // TableName 指定表名。
- func (ExpenditureLog) TableName() string { return "t_expenditure_log" }
|