203 lines
6.4 KiB
Go
203 lines
6.4 KiB
Go
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"`
|
|
}
|