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.DeleteAsync(entity);
await _easyCachingProvider.RemoveAsync(CacheKey_Provinces);
}
}
}