using YD_Event.Core.Entities; using Mapster; using System.ComponentModel; using Furion.DynamicApiController; using YD_Event.Application.Region.Dtos; using Microsoft.AspNetCore.Mvc; using EasyCaching.Core; using SqlSugar; using Furion.FriendlyException; using Microsoft.AspNetCore.Authorization; using YD_Event.Core.Shared; namespace YD_Event.Application.Region { /// /// 行政区域服务 /// public class RegionService : ITransient, IDynamicApiController { private readonly ISqlSugarRepository _provinceRepository; private readonly ISqlSugarRepository _cityRepository; private readonly ISqlSugarRepository _districtRepository; private readonly IEasyCachingProvider _easyCachingProvider; private readonly IIdGenerator _idGenerator; // 缓存键定义 private const string CacheKey_Provinces = "Region:Provinces"; private const string CacheKey_Cities = "Region:Cities:"; private const string CacheKey_Districts = "Region:Districts:"; public RegionService( ISqlSugarRepository provinceRepository, ISqlSugarRepository cityRepository, ISqlSugarRepository districtRepository, IEasyCachingProvider easyCachingProvider, IIdGenerator idGenerator) { _provinceRepository = provinceRepository; _cityRepository = cityRepository; _districtRepository = districtRepository; _easyCachingProvider = easyCachingProvider; _idGenerator = idGenerator; } /// /// 获取所有省份 (带缓存) /// /// [DisplayName("获取所有省份")] [AllowAnonymous] public async Task> GetProvinces() { var cacheValue = await _easyCachingProvider.GetAsync>(CacheKey_Provinces); if (cacheValue.HasValue) return cacheValue.Value; var provinces = await _provinceRepository.GetListAsync(x => true); var dtos = provinces.Select(x => new RegionDto { Id = x.Id, Code = x.ProvinceCode, Name = x.ProvinceName, ParentCode = x.RegionCode }).ToList(); // 防击穿与雪崩:设置随机过期时间 (12-24小时) var expire = TimeSpan.FromHours(new Random().Next(12, 24)); await _easyCachingProvider.SetAsync(CacheKey_Provinces, dtos, expire); return dtos; } /// /// 根据省份代码获取城市列表 (带缓存) /// /// 省份代码 /// [DisplayName("根据省份代码获取城市列表")] [AllowAnonymous] public async Task> GetCitiesByProvince([FromQuery] string provinceCode) { if (string.IsNullOrWhiteSpace(provinceCode)) throw Oops.Oh("省份代码不能为空"); string cacheKey = $"{CacheKey_Cities}{provinceCode}"; var cacheValue = await _easyCachingProvider.GetAsync>(cacheKey); if (cacheValue.HasValue) return cacheValue.Value; var cities = await _cityRepository.GetListAsync(c => c.ProvinceCode == provinceCode); // 防穿透:如果查询为空,也缓存一个空列表,时间较短 if (!cities.Any()) { await _easyCachingProvider.SetAsync(cacheKey, new List(), TimeSpan.FromMinutes(5)); return new List(); } var dtos = cities.Select(x => new RegionDto { Id = x.Id, Code = x.CityCode, Name = x.CityName, ParentCode = x.ProvinceCode }).ToList(); await _easyCachingProvider.SetAsync(cacheKey, dtos, TimeSpan.FromHours(new Random().Next(12, 24))); return dtos; } /// /// 根据城市代码获取区县列表 (带缓存) /// /// 城市代码 /// [DisplayName("根据城市代码获取区县列表")] [AllowAnonymous] public async Task> GetDistrictsByCity([FromQuery] string cityCode) { if (string.IsNullOrWhiteSpace(cityCode)) throw Oops.Oh("城市代码不能为空"); string cacheKey = $"{CacheKey_Districts}{cityCode}"; var cacheValue = await _easyCachingProvider.GetAsync>(cacheKey); if (cacheValue.HasValue) return cacheValue.Value; var districts = await _districtRepository.GetListAsync(d => d.CityCode == cityCode); if (!districts.Any()) { await _easyCachingProvider.SetAsync(cacheKey, new List(), TimeSpan.FromMinutes(5)); return new List(); } var dtos = districts.Select(x => new RegionDto { Id = x.Id, Code = x.DistrictCode, Name = x.DistrictName, ParentCode = x.CityCode }).ToList(); await _easyCachingProvider.SetAsync(cacheKey, dtos, TimeSpan.FromHours(new Random().Next(12, 24))); return dtos; } /// /// 分页查询省份信息 /// /// /// [DisplayName("分页查询省份信息")] [HttpPost("page/provinces")] public async Task> GetProvincePage([FromBody] RegionPageInput input) { var result = await _provinceRepository.AsQueryable() .WhereIF(!string.IsNullOrWhiteSpace(input.Name), x => x.ProvinceName.Contains(input.Name)) .WhereIF(!string.IsNullOrWhiteSpace(input.Code), x => x.ProvinceCode.Contains(input.Code)) .OrderBy(x => x.ProvinceCode) .Select(x => new RegionDto { Id = x.Id, Code = x.ProvinceCode, Name = x.ProvinceName, ParentCode = x.RegionCode }) .ToPagedListAsync(input); return result; } /// /// 添加省份 /// [DisplayName("添加省份")] [UnitOfWork] public async Task AddProvince(RegionInput input) { var exists = await _provinceRepository.IsAnyAsync(x => x.ProvinceCode == input.Code); if (exists) throw Oops.Oh("省份编码已存在"); var entity = new SysProvince { Id = _idGenerator.NewLong(), ProvinceCode = input.Code, ProvinceName = input.Name, RegionCode = input.ParentCode, CreatedTime = DateTime.Now }; await _provinceRepository.InsertAsync(entity); await _easyCachingProvider.RemoveAsync(CacheKey_Provinces); } /// /// 更新省份 /// [DisplayName("更新省份")] [UnitOfWork] public async Task UpdateProvince(RegionInput input) { var entity = await _provinceRepository.GetByIdAsync(input.Id); if (entity == null) throw Oops.Oh("省份不存在"); var exists = await _provinceRepository.IsAnyAsync(x => x.ProvinceCode == input.Code && x.Id != input.Id); if (exists) throw Oops.Oh("省份编码已存在"); entity.ProvinceCode = input.Code; entity.ProvinceName = input.Name; entity.RegionCode = input.ParentCode; entity.UpdatedTime = DateTime.Now; await _provinceRepository.UpdateAsync(entity); await _easyCachingProvider.RemoveAsync(CacheKey_Provinces); } /// /// 删除省份 /// [DisplayName("删除省份")] [UnitOfWork] public async Task DeleteProvince([FromQuery] long id) { var entity = await _provinceRepository.GetByIdAsync(id); if (entity == null) throw Oops.Oh("省份不存在"); var hasCity = await _cityRepository.IsAnyAsync(x => x.ProvinceCode == entity.ProvinceCode); if (hasCity) throw Oops.Oh("该省份下存在城市,无法删除"); await _provinceRepository.SoftDeleteByIdAsync(entity.Id); await _easyCachingProvider.RemoveAsync(CacheKey_Provinces); } } }