UNIVPLMDataIntegration/Admin.NET/Admin.NET.Core/Service/Region/SysRegionService.cs
2024-12-30 02:20:39 +08:00

501 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using NewLife.Remoting;
using NewLife.Serialization;
namespace Admin.NET.Core.Service;
/// <summary>
/// 系统行政区划服务 🧩
/// </summary>
[ApiDescriptionSettings(Order = 310, Description = "行政区划")]
public class SysRegionService : IDynamicApiController, ITransient
{
private readonly SqlSugarRepository<SysRegion> _sysRegionRep;
private readonly SysConfigService _sysConfigService;
public SysRegionService(SqlSugarRepository<SysRegion> sysRegionRep, SysConfigService sysConfigService)
{
_sysRegionRep = sysRegionRep;
_sysConfigService = sysConfigService;
}
/// <summary>
/// 获取行政区划分页列表 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("获取行政区划分页列表")]
public async Task<SqlSugarPagedList<SysRegion>> Page(PageRegionInput input)
{
return await _sysRegionRep.AsQueryable()
.WhereIF(input.Pid > 0, u => u.Pid == input.Pid || u.Id == input.Pid)
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name))
.WhereIF(!string.IsNullOrWhiteSpace(input.Code), u => u.Code.Contains(input.Code))
.ToPagedListAsync(input.Page, input.PageSize);
}
/// <summary>
/// 获取行政区划列表 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("获取行政区划列表")]
public async Task<List<SysRegion>> GetList([FromQuery] RegionInput input)
{
return await _sysRegionRep.GetListAsync(u => u.Pid == input.Id);
}
/// <summary>
/// 查询行政区划列表 🔖
/// post参数方便参数扩展调用api不用大浮动修复参数如果是对象不建议用[FromQuery]方式传参
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[ApiDescriptionSettings(Name = "Query"), HttpPost]
[DisplayName("查询行政区划列表")]
public async Task<List<SysRegion>> QueryList(QueryRegionInput input)
{
return await _sysRegionRep.AsQueryable()
.WhereIF(input.Pid.HasValue, u => u.Pid == input.Pid)
.WhereIF(!string.IsNullOrWhiteSpace(input.Type), u => u.Type == input.Type)
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name))
.WhereIF(!string.IsNullOrWhiteSpace(input.Code), u => u.Code.Contains(input.Code))
.ToListAsync();
}
/// <summary>
/// 增加行政区划 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[ApiDescriptionSettings(Name = "Add"), HttpPost]
[DisplayName("增加行政区划")]
public async Task<long> AddRegion(AddRegionInput input)
{
input.Code = input.Code?.Trim() ?? "";
if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6) throw Oops.Oh(ErrorCodeEnum.R2003);
if (input.Pid != 0)
{
var pRegion = await _sysRegionRep.GetByIdAsync(input.Pid);
pRegion ??= await _sysRegionRep.GetFirstAsync(u => u.Code == input.Pid.ToString());
if (pRegion == null) throw Oops.Oh(ErrorCodeEnum.D2000);
input.Pid = pRegion.Id;
}
var isExist = await _sysRegionRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code);
if (isExist) throw Oops.Oh(ErrorCodeEnum.R2002);
var sysRegion = input.Adapt<SysRegion>();
var newRegion = await _sysRegionRep.AsInsertable(sysRegion).ExecuteReturnEntityAsync();
return newRegion.Id;
}
/// <summary>
/// 更新行政区划 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[ApiDescriptionSettings(Name = "Update"), HttpPost]
[DisplayName("更新行政区划")]
public async Task UpdateRegion(UpdateRegionInput input)
{
input.Code = input.Code?.Trim() ?? "";
if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6) throw Oops.Oh(ErrorCodeEnum.R2003);
var sysRegion = await _sysRegionRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
if (sysRegion.Pid != input.Pid && input.Pid != 0)
{
var pRegion = await _sysRegionRep.GetByIdAsync(input.Pid);
pRegion ??= await _sysRegionRep.GetFirstAsync(u => u.Code == input.Pid.ToString());
if (pRegion == null) throw Oops.Oh(ErrorCodeEnum.D2000);
input.Pid = pRegion.Id;
var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
var childIdList = regionTreeList.Select(u => u.Id).ToList();
if (childIdList.Contains(input.Pid)) throw Oops.Oh(ErrorCodeEnum.R2004);
}
if (input.Id == input.Pid) throw Oops.Oh(ErrorCodeEnum.R2001);
var isExist = await _sysRegionRep.IsAnyAsync(u => (u.Name == input.Name && u.Code == input.Code) && u.Id != sysRegion.Id);
if (isExist) throw Oops.Oh(ErrorCodeEnum.R2002);
//// 父Id不能为自己的子节点
//var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
//var childIdList = regionTreeList.Select(u => u.Id).ToList();
//if (childIdList.Contains(input.Pid))
// throw Oops.Oh(ErrorCodeEnum.R2001);
await _sysRegionRep.AsUpdateable(input.Adapt<SysRegion>()).IgnoreColumns(true).ExecuteCommandAsync();
}
/// <summary>
/// 删除行政区划 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[ApiDescriptionSettings(Name = "Delete"), HttpPost]
[DisplayName("删除行政区划")]
public async Task DeleteRegion(DeleteRegionInput input)
{
var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
var regionIdList = regionTreeList.Select(u => u.Id).ToList();
await _sysRegionRep.DeleteAsync(u => regionIdList.Contains(u.Id));
}
/// <summary>
/// 同步行政区划(民政部) 🔖
/// </summary>
/// <returns></returns>
[DisplayName("同步行政区划(民政部)")]
public async Task SyncRegionMzb()
{
var syncLevel = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysRegionSyncLevel);
if (syncLevel is < 1 or > 5) syncLevel = 3; // 默认区县级
var httpRemoteService = App.GetRequiredService<IHttpRemoteService>();
var html = await httpRemoteService.GetAsStringAsync("http://xzqh.mca.gov.cn/map");
var municipalityList = new List<string> { "北京", "天津", "上海", "重庆" };
var provList = Regex.Match(html, @"(?<=var json = )(\[\{.*?\}\])(?=;)").Value.ToJsonEntity<List<Dictionary<string, string>>>();
foreach (var dict1 in provList)
{
var list = new List<SysRegion>();
var provName = dict1.GetValueOrDefault("shengji");
var province = new SysRegion
{
Id = YitIdHelper.NextId(),
Name = Regex.Replace(provName, "[(].*?[)]", ""),
Code = dict1.GetValueOrDefault("quHuaDaiMa"),
CityCode = dict1.GetValueOrDefault("quhao"),
Level = 1,
Pid = 0,
};
if (municipalityList.Any(m => province.Name.StartsWith(m))) province.Name += "(省)";
list.Add(province);
if (syncLevel <= 1) continue;
var prefList = await GetSelectList(provName);
foreach (var dict2 in prefList)
{
var prefName = dict2.GetValueOrDefault("diji");
var city = new SysRegion
{
Id = YitIdHelper.NextId(),
Code = dict2.GetValueOrDefault("quHuaDaiMa"),
CityCode = dict2.GetValueOrDefault("quhao"),
Pid = province.Id,
Name = prefName,
Level = 2
};
if (municipalityList.Any(m => city.Name.StartsWith(m))) city.Name += "(地)";
list.Add(city);
if (syncLevel <= 2) continue;
var countyList = await GetSelectList(provName, prefName);
foreach (var dict3 in countyList)
{
var countyName = dict3.GetValueOrDefault("xianji");
var county = new SysRegion
{
Id = YitIdHelper.NextId(),
Code = dict3.GetValueOrDefault("quHuaDaiMa"),
CityCode = dict3.GetValueOrDefault("quhao"),
Name = countyName,
Pid = city.Id,
Level = 3
};
list.Add(county);
}
}
await _sysRegionRep.AsDeleteable().ExecuteCommandAsync();
await _sysRegionRep.Context.Fastest<SysRegion>().BulkCopyAsync(list);
}
// 获取选择数据
async Task<List<Dictionary<string, string>>> GetSelectList(string prov, string prefecture = null)
{
var json = await httpRemoteService.PostAsStringAsync("http://xzqh.mca.gov.cn/selectJson", builder => builder.SetJsonContent(new
{
shengji = prov,
diji = prefecture,
}));
return json.ToJsonEntity<List<Dictionary<string, string>>>();
}
}
/// <summary>
/// 同步行政区划(高德) 🔖
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
[DisplayName("同步行政区划(高德)")]
public async Task SyncRegionGD(string key)
{
if (string.IsNullOrWhiteSpace(key) || key.Length < 30)
throw Oops.Oh("请正确输入高德地图开发者 Key 值");
var syncLevel = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysRegionSyncLevel);
if (syncLevel is < 1 or > 5) syncLevel = 3; // 默认区县级
var httpRemoteService = App.GetRequiredService<IHttpRemoteService>();
var res = await httpRemoteService.GetAsync($"https://restapi.amap.com/v3/config/district?subdistrict={syncLevel}&key={key}");
if (!res.IsSuccessStatusCode) return;
var gdResponse = JSON.Deserialize<GDResponse<List<GDRegionResponse>>>(res.Content.ReadAsStringAsync().Result);
if (gdResponse.info != "OK" || gdResponse.districts == null || gdResponse.districts.Count < 1) return;
var regionList = new List<SysRegion>();
foreach (var item in gdResponse.districts)
{
GetChildren(regionList, item.districts, 1, 0); // 排除一级目录(国家)
}
await _sysRegionRep.AsDeleteable().ExecuteCommandAsync();
await _sysRegionRep.Context.Fastest<SysRegion>().BulkCopyAsync(regionList);
}
private void GetChildren(List<SysRegion> regionList, List<GDRegionResponse> responses, int level, long pid)
{
foreach (var region in responses)
{
var sysRegion = new SysRegion { Id = YitIdHelper.NextId(), Pid = pid, Name = region.name, Code = region.adcode, CityCode = region.adcode, Level = level };
regionList.Add(sysRegion);
if (region.districts.Count > 0)
GetChildren(regionList, region.districts, level++, sysRegion.Id);
}
}
/// <summary>
/// 同步行政区划数据国家地名信息库最多支持2级深度 🔖
/// </summary>
/// <returns></returns>
[DisplayName("同步行政区划数据(国家地名信息库)")]
public async Task<int> SyncRegionMca(long code)
{
var url = $"https://dmfw.mca.gov.cn/9095/xzqh/getList?code={code}&maxLevel=4";
var httpClient = new HttpClient();
var regionLevel0 = await httpClient.GetAsync<SysRegion>(url);
if (regionLevel0 == null) return 0;
var areaList = new List<SysRegion>();
if (regionLevel0.Code != "00" && regionLevel0.Level > 0 && !string.IsNullOrEmpty(regionLevel0.Name))
{
areaList.Add(new SysRegion
{
Id = Convert.ToInt64(regionLevel0.Code),
Pid = 0,
Code = regionLevel0.Code,
Name = regionLevel0.Name,
Type = regionLevel0.Type,
Level = regionLevel0.Level,
});
}
foreach (var regionLevel1 in regionLevel0.Children)
{
var region1 = new SysRegion
{
Id = Convert.ToInt64(regionLevel1.Code),
Pid = Convert.ToInt64(regionLevel0.Code),
Code = regionLevel1.Code,
Name = regionLevel1.Name,
Type = regionLevel1.Type,
Level = regionLevel1.Level,
};
if (areaList.Any(u => u.Id == region1.Id))
Console.WriteLine($"1 级:{region1.Id} - {region1.Name} 已存在");
else
areaList.Add(region1);
foreach (var regionLevel2 in regionLevel1.Children)
{
var region2 = new SysRegion
{
Id = Convert.ToInt64(regionLevel2.Code),
Pid = Convert.ToInt64(regionLevel1.Code),
Code = regionLevel2.Code,
Name = regionLevel2.Name,
Type = regionLevel2.Type,
Level = regionLevel2.Level,
};
if (areaList.Any(u => u.Id == region2.Id))
Console.WriteLine($"2 级:{region2.Id} - {region2.Name} 已存在");
else
areaList.Add(region2);
foreach (var regionLevel3 in regionLevel2.Children)
{
var region3 = new SysRegion
{
Id = Convert.ToInt64(regionLevel3.Code),
Pid = Convert.ToInt64(regionLevel2.Code),
Code = regionLevel3.Code,
Name = regionLevel3.Name,
Type = regionLevel3.Type,
Level = regionLevel3.Level,
};
if (areaList.Any(u => u.Id == region3.Id))
Console.WriteLine($"3 级:{region3.Id} - {region3.Name}");
else
areaList.Add(region3);
foreach (var regionLevel4 in regionLevel3.Children)
{
var region4 = new SysRegion
{
Id = Convert.ToInt64(regionLevel4.Code),
Pid = Convert.ToInt64(regionLevel3.Code),
Code = regionLevel4.Code,
Name = regionLevel4.Name,
Type = regionLevel4.Type,
Level = regionLevel4.Level,
};
areaList.Add(region4);
}
}
}
}
if (code == 0)
await _sysRegionRep.AsDeleteable().ExecuteCommandAsync();
else if (await _sysRegionRep.IsAnyAsync(u => u.Id == code)) // 如果存在指定行政区划则删除
await DeleteRegion(new DeleteRegionInput { Id = code });
return await _sysRegionRep.AsInsertable(areaList).ExecuteCommandAsync();
}
/// <summary>
/// 同步行政区划数据(天地图行政区划) 🔖
/// </summary>
/// <returns></returns>
[DisplayName("同步行政区划数据(天地图行政区划)")]
public async Task<int> SyncRegionTianditu(TiandituInput input)
{
// 接口说明及地址http://lbs.tianditu.gov.cn/server/administrative2.html
var url = $"http://api.tianditu.gov.cn/v2/administrative?keyword={input.Keyword}&childLevel={input.ChildLevel}&extensions={input.Extensions}&tk={input.Tk}";
var httpClient = new HttpClient();
var res = await httpClient.GetAsync<TiandituDto>(url);
if (res == null) return 0;
var parent = res.District[0];
var areaList = new List<SysRegion>()
{
new SysRegion
{
Id = Convert.ToInt64(parent.Gb),
Pid = 0,
Code = parent.Gb,
Name = parent.Name,
Level = parent.Level,
Longitude = parent.Center.Lng,
Latitude = parent.Center.Lat
}
};
foreach (var item in parent.Children)
{
var region = new SysRegion
{
Id = Convert.ToInt64(item.Gb),
Pid = Convert.ToInt64(parent.Gb),
Code = item.Gb,
Name = item.Name,
Level = item.Level,
Longitude = item.Center.Lng,
Latitude = item.Center.Lat
};
areaList.Add(region);
foreach (var child in item.Children)
{
areaList.Add(new SysRegion
{
Id = Convert.ToInt64(child.Gb),
Pid = region.Id,
Code = child.Gb,
Name = child.Name,
Level = child.Level,
Longitude = child.Center.Lng,
Latitude = child.Center.Lat
});
}
}
// 若存在指定行政区划则删除
if (await _sysRegionRep.IsAnyAsync(u => u.Name.Contains(input.Keyword) || u.Id.ToString() == input.Keyword))
{
var region = await _sysRegionRep.GetFirstAsync(u => u.Name.Contains(input.Keyword) || u.Id.ToString() == input.Keyword);
await DeleteRegion(new DeleteRegionInput { Id = region.Id });
}
return await _sysRegionRep.AsInsertable(areaList).ExecuteCommandAsync();
}
/// <summary>
/// 生成组织架构 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("生成组织架构")]
public async Task GenOrg(GenOrgInput input)
{
var region = await _sysRegionRep.GetByIdAsync(input.Id);
var orgRep = _sysRegionRep.ChangeRepository<SqlSugarRepository<SysOrg>>();
if (!await orgRep.IsAnyAsync(u => u.Id == region.Pid))
region.Pid = 0;
var regionList = await GetRegionListByLevel(region, input.Level);
var orgList = regionList.Adapt<List<SysOrg>>();
await orgRep.InsertOrUpdateAsync(orgList);
}
/// <summary>
/// 根据层级获取行政区划数据
/// </summary>
/// <param name="region"></param>
/// <param name="level"></param>
/// <returns></returns>
private async Task<List<SysRegion>> GetRegionListByLevel(SysRegion region, int level)
{
var regionList = new List<SysRegion>();
if (level > 5) level = 5;
regionList.Add(region);
if (level == 1) return regionList;
var regionList2 = await GetList(new RegionInput { Id = region.Id });
regionList.AddRange(regionList2);
if (level == 2) return regionList;
foreach (var item in regionList2)
{
var regionList3 = await GetList(new RegionInput { Id = item.Id });
if (regionList3 == null) continue;
regionList.AddRange(regionList3);
if (level == 3) continue;
foreach (var item3 in regionList3)
{
var regionList4 = await GetList(new RegionInput { Id = item3.Id });
if (regionList4 == null) continue;
regionList.AddRange(regionList4);
if (level == 4) continue;
foreach (var item4 in regionList4)
{
var regionList5 = await GetList(new RegionInput { Id = item4.Id });
if (regionList5 == null) continue;
regionList.AddRange(regionList5);
}
}
}
return regionList;
}
}