package service import ( "errors" "fmt" "photowall/internal/model" "gorm.io/gorm" ) type RegionService struct { db *gorm.DB } func NewRegionService(db *gorm.DB) *RegionService { return &RegionService{db: db} } // GetByID 根据 ID 查询地区 func (s *RegionService) GetByID(id uint) (*model.Region, error) { var r model.Region if err := s.db.First(&r, id).Error; err != nil { return nil, errors.New("地区不存在") } return &r, nil } // ListByParent 根据父级ID查子地区;parentID=0 查国家;可选按层级筛选 func (s *RegionService) ListByParent(parentID uint, level ...model.RegionLevel) ([]model.Region, error) { query := s.db.Where("parent_id = ?", parentID) if len(level) > 0 && level[0] != "" { query = query.Where("level = ?", level[0]) } var list []model.Region err := query.Order("sort_order, id").Find(&list).Error return list, err } // ListCountries 查所有国家 func (s *RegionService) ListCountries() ([]model.Region, error) { var list []model.Region err := s.db.Where("level = ?", model.LevelCountry).Order("sort_order, id").Find(&list).Error return list, err } // GetPath 获取某地区的完整路径(国家->省->市->县) func (s *RegionService) GetPath(regionID uint) ([]model.Region, error) { var r model.Region if err := s.db.First(&r, regionID).Error; err != nil { return nil, err } path := []model.Region{r} cur := r for cur.ParentID != 0 { var parent model.Region if err := s.db.First(&parent, cur.ParentID).Error; err != nil { break } path = append([]model.Region{parent}, path...) cur = parent } return path, nil } // Search 按名称或代码搜索地区 func (s *RegionService) Search(keyword string, limit int) ([]model.Region, error) { if limit <= 0 { limit = 50 } var list []model.Region err := s.db.Where("name LIKE ? OR name_en LIKE ? OR code LIKE ?", "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%"). Order("level, sort_order, id").Limit(limit).Find(&list).Error return list, err } // Create 创建地区 func (s *RegionService) Create(r *model.Region) error { if r.Name == "" { return errors.New("地区名称不能为空") } if r.Level == "" { return errors.New("请选择地区层级") } if r.ParentID > 0 { var parent model.Region if err := s.db.First(&parent, r.ParentID).Error; err != nil { return errors.New("所选上级地区不存在") } } return s.db.Create(r).Error } // Update 更新地区信息 func (s *RegionService) Update(id uint, updates map[string]interface{}) error { res := s.db.Model(&model.Region{}).Where("id = ?", id).Updates(updates) if res.Error != nil { return res.Error } if res.RowsAffected == 0 { return errors.New("地区不存在") } return nil } // Delete 删除地区(包含子项/学校引用防误删检查) func (s *RegionService) Delete(id uint) error { // 检查是否有子地区 var childCount int64 s.db.Model(&model.Region{}).Where("parent_id = ?", id).Count(&childCount) if childCount > 0 { return errors.New("该地区下仍有下级地区,请先处理下级地区") } // 检查是否有学校关联 var schoolCount int64 s.db.Model(&model.School{}).Where("region_id = ?", id).Count(&schoolCount) if schoolCount > 0 { return errors.New("该地区下已有关联学校,无法直接删除") } return s.db.Delete(&model.Region{}, id).Error } // GetTree 获取地区层级树(parentID=0 表示从顶级国家开始) func (s *RegionService) GetTree(parentID uint) ([]*model.RegionTree, error) { var all []model.Region if err := s.db.Order("sort_order, id").Find(&all).Error; err != nil { return nil, err } return buildRegionTree(all, parentID), nil } func buildRegionTree(list []model.Region, parentID uint) []*model.RegionTree { var tree []*model.RegionTree for i := range list { if list[i].ParentID == parentID { node := &model.RegionTree{ Region: list[i], Children: buildRegionTree(list, list[i].ID), } tree = append(tree, node) } } return tree } // BatchCreate 批量录入地区 func (s *RegionService) BatchCreate(regions []model.Region) error { if len(regions) == 0 { return nil } return s.db.CreateInBatches(regions, 100).Error } // InitChinaRegions 生成/初始化整个中国的地域选项(34个省/直辖市/自治区、地级市、区县) func (s *RegionService) InitChinaRegions() (createdCount int, totalCount int, err error) { // 1. 预加载所有已存在的地区,极大提升比对性能(避免数千次单条查询导致超时) var allExisting []model.Region if err := s.db.Select("id, name, level, parent_id, code").Find(&allExisting).Error; err != nil { return 0, 0, err } regMap := make(map[string]uint, len(allExisting)+3500) for _, r := range allExisting { key := fmt.Sprintf("%d:%s:%s", r.ParentID, r.Level, r.Name) regMap[key] = r.ID } // 2. 查找或创建国家:中国 chinaKey := fmt.Sprintf("0:%s:中国", model.LevelCountry) chinaID, exists := regMap[chinaKey] if !exists { china := model.Region{ Name: "中国", NameEn: "China", Level: model.LevelCountry, ParentID: 0, Code: "CN", SortOrder: 1, } if err := s.db.Create(&china).Error; err != nil { return 0, 0, err } chinaID = china.ID regMap[chinaKey] = chinaID createdCount++ } totalCount++ // 2.1 清理历史残留错误数据:挂在中国省级下的直接 county 记录(如无学校关联) var wrongProvCounties []model.Region s.db.Raw(` SELECT c.id FROM yz_pw_regions c JOIN yz_pw_regions p ON c.parent_id = p.id WHERE c.level = 'county' AND p.level = 'province' AND p.parent_id = ? `, chinaID).Scan(&wrongProvCounties) if len(wrongProvCounties) > 0 { var toDel []uint for _, wc := range wrongProvCounties { var scCount int64 s.db.Model(&model.School{}).Where("region_id = ?", wc.ID).Count(&scCount) if scCount == 0 { toDel = append(toDel, wc.ID) } } if len(toDel) > 0 { s.db.Where("id IN ?", toDel).Delete(&model.Region{}) } } // 3. 遍历全国省市县数据并批量写入 for pIdx, pData := range chinaProvincesData { provKey := fmt.Sprintf("%d:%s:%s", chinaID, model.LevelProvince, pData.Name) provID, provExists := regMap[provKey] if !provExists { prov := model.Region{ Name: pData.Name, Level: model.LevelProvince, ParentID: chinaID, Code: pData.Code, SortOrder: pIdx + 1, } if err := s.db.Create(&prov).Error; err != nil { return createdCount, totalCount, err } provID = prov.ID regMap[provKey] = provID createdCount++ } totalCount++ for cIdx, cData := range pData.Cities { cityKey := fmt.Sprintf("%d:%s:%s", provID, model.LevelCity, cData.Name) cityID, cityExists := regMap[cityKey] if !cityExists { city := model.Region{ Name: cData.Name, Level: model.LevelCity, ParentID: provID, Code: cData.Code, SortOrder: cIdx + 1, } if err := s.db.Create(&city).Error; err != nil { return createdCount, totalCount, err } cityID = city.ID regMap[cityKey] = cityID createdCount++ } totalCount++ var missingCounties []model.Region for kIdx, countyName := range cData.Counties { totalCount++ countyKey := fmt.Sprintf("%d:%s:%s", cityID, model.LevelCounty, countyName) if _, ctyExists := regMap[countyKey]; !ctyExists { missingCounties = append(missingCounties, model.Region{ Name: countyName, Level: model.LevelCounty, ParentID: cityID, SortOrder: kIdx + 1, }) regMap[countyKey] = 1 } } if len(missingCounties) > 0 { if err := s.db.CreateInBatches(missingCounties, 100).Error; err != nil { return createdCount, totalCount, err } createdCount += len(missingCounties) } } } return createdCount, totalCount, nil }