package model import ( "encoding/json" "sort" "strings" "time" ) // RegionLevel 地区层级 type RegionLevel string const ( LevelCountry RegionLevel = "country" LevelProvince RegionLevel = "province" // 省/州/直辖市/自治区 LevelCity RegionLevel = "city" // 市/地区/自治州/盟 LevelCounty RegionLevel = "county" // 县/区/旗/县级市 ) // Region 地区表,自关联多级:国家 -> 省/州 -> 市 -> 县/区 type Region struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"size:128;not null;index" json:"name"` NameEn string `gorm:"size:128" json:"name_en"` Level RegionLevel `gorm:"size:16;index;not null" json:"level"` ParentID uint `gorm:"index" json:"parent_id"` Code string `gorm:"size:32;index" json:"code"` // 行政区划代码/国家代码,可选 SortOrder int `gorm:"default:0" json:"sort_order"` CreatedAt time.Time `json:"created_at"` } // RegionTree 地区树节点 type RegionTree struct { Region Children []*RegionTree `json:"children,omitempty"` } // SchoolStatus 学校审核状态 type SchoolStatus string const ( SchoolStatusPending SchoolStatus = "pending" // 待审核 SchoolStatusActive SchoolStatus = "active" // 正常 SchoolStatusRejected SchoolStatus = "rejected" // 已打回 ) // SchoolType 学校类型 type SchoolType string const ( SchoolUniversity SchoolType = "university" // 大学(含学院层级) SchoolJuniorHigh SchoolType = "junior_high" // 初中 SchoolSeniorHigh SchoolType = "senior_high" // 高中 SchoolPrimary SchoolType = "primary" // 小学 SchoolKindergarten SchoolType = "kindergarten" // 幼儿园 SchoolVocational SchoolType = "vocational" // 职业学校/中专(也可设学院) SchoolOther SchoolType = "other" ) // SchoolTypeOrder 学校类型的展示与主类型优先级(数值越小优先级越高) // 一所学校可同时涵盖多个学段(如完全中学 = 初中 + 高中), // 主类型 school_type 取其中优先级最高的一个,用于兼容旧逻辑与统计。 var SchoolTypeOrder = map[SchoolType]int{ SchoolUniversity: 0, SchoolVocational: 1, SchoolSeniorHigh: 2, SchoolJuniorHigh: 3, SchoolPrimary: 4, SchoolKindergarten: 5, SchoolOther: 6, } // IsValid 是否为已定义的学校类型 func (s SchoolType) IsValid() bool { _, ok := SchoolTypeOrder[s] return ok } // HasCollege 该类型学校是否有学院层级 func (s SchoolType) HasCollege() bool { return s == SchoolUniversity || s == SchoolVocational } // School 学校 type School struct { ID uint `gorm:"primaryKey" json:"id"` RegionID uint `gorm:"index;not null" json:"region_id"` // 挂在 county 或 city 级地区下 Name string `gorm:"size:128;not null;index" json:"name"` NameEn string `gorm:"size:128" json:"name_en"` // SchoolType 主类型:SchoolTypes 中优先级最高的一个,保留用于兼容旧数据与统计 SchoolType SchoolType `gorm:"size:24;index;not null" json:"school_type"` // SchoolTypes 全部涵盖的学段,英文逗号分隔且无空格(如 "junior_high,senior_high"), // 便于使用 MySQL FIND_IN_SET 检索 SchoolTypes string `gorm:"size:128;index" json:"school_types_raw"` Address string `gorm:"size:255" json:"address"` Status SchoolStatus `gorm:"size:16;default:active;index" json:"status"` // pending, active, rejected RejectReason string `gorm:"size:255" json:"reject_reason"` CreatedBy uint `gorm:"default:0;index" json:"created_by"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` // 关联字段(非数据库持久化) RegionPath string `gorm:"-" json:"region_path,omitempty"` } // TypeList 返回学校涵盖的全部学段;旧数据未写 school_types 时回退到主类型 func (s *School) TypeList() []SchoolType { list := ParseSchoolTypes(s.SchoolTypes) if len(list) == 0 && s.SchoolType != "" { return []SchoolType{s.SchoolType} } return list } // HasType 学校是否涵盖指定学段 func (s *School) HasType(t SchoolType) bool { for _, item := range s.TypeList() { if item == t { return true } } return false } // HasCollegeType 学校是否存在支持学院层级的学段(大学/职业院校) func (s *School) HasCollegeType() bool { for _, item := range s.TypeList() { if item.HasCollege() { return true } } return false } // RequiresCollege 建班时是否必须选择学院:仅当所有学段都支持学院层级 // (例如"大学 + 高中"的学校,走高中学段建班时无需选学院) func (s *School) RequiresCollege() bool { list := s.TypeList() if len(list) == 0 { return false } for _, item := range list { if !item.HasCollege() { return false } } return true } // MarshalJSON 输出时附带 school_types 数组,前端可直接使用 func (s School) MarshalJSON() ([]byte, error) { type schoolAlias School list := (&s).TypeList() strs := make([]string, 0, len(list)) for _, item := range list { strs = append(strs, string(item)) } return json.Marshal(struct { schoolAlias SchoolTypeList []string `json:"school_types"` }{schoolAlias(s), strs}) } // ParseSchoolTypes 解析逗号分隔的学校类型,去重并按优先级排序 func ParseSchoolTypes(raw string) []SchoolType { return NormalizeSchoolTypes(strings.Split(raw, ",")) } // NormalizeSchoolTypes 校验、去重并按优先级排序学校类型集合 func NormalizeSchoolTypes(items []string) []SchoolType { seen := map[SchoolType]bool{} res := make([]SchoolType, 0, len(items)) for _, item := range items { t := SchoolType(strings.TrimSpace(item)) if t == "" || !t.IsValid() || seen[t] { continue } seen[t] = true res = append(res, t) } sort.SliceStable(res, func(i, j int) bool { return SchoolTypeOrder[res[i]] < SchoolTypeOrder[res[j]] }) return res } // JoinSchoolTypes 将类型集合拼为可供 FIND_IN_SET 使用的字符串 func JoinSchoolTypes(items []SchoolType) string { strs := make([]string, 0, len(items)) for _, item := range items { strs = append(strs, string(item)) } return strings.Join(strs, ",") } // College 学院(仅大学/职业学校有) type College struct { ID uint `gorm:"primaryKey" json:"id"` SchoolID uint `gorm:"index;not null" json:"school_id"` Name string `gorm:"size:128;not null" json:"name"` CreatedAt time.Time `json:"created_at"` }