278 lines
7.7 KiB
Go
278 lines
7.7 KiB
Go
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
|
|
}
|