228 lines
8.7 KiB
C#
Raw Normal View History

2026-03-18 00:05:53 +08:00
using YD_Event.Core.Entities;
using Mapster;
using System.ComponentModel;
using Furion.DynamicApiController;
using YD_Event.Application.Region.Dtos;
2026-03-18 01:30:27 +08:00
using Microsoft.AspNetCore.Mvc;
using EasyCaching.Core;
using SqlSugar;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Authorization;
using YD_Event.Core.Shared;
2026-03-18 00:05:53 +08:00
namespace YD_Event.Application.Region
{
/// <summary>
/// 行政区域服务
/// </summary>
public class RegionService : ITransient, IDynamicApiController
{
private readonly ISqlSugarRepository<SysProvince> _provinceRepository;
private readonly ISqlSugarRepository<SysCity> _cityRepository;
private readonly ISqlSugarRepository<SysDistrict> _districtRepository;
2026-03-18 01:30:27 +08:00
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:";
2026-03-18 00:05:53 +08:00
public RegionService(
ISqlSugarRepository<SysProvince> provinceRepository,
ISqlSugarRepository<SysCity> cityRepository,
2026-03-18 01:30:27 +08:00
ISqlSugarRepository<SysDistrict> districtRepository,
IEasyCachingProvider easyCachingProvider,
IIdGenerator idGenerator)
2026-03-18 00:05:53 +08:00
{
_provinceRepository = provinceRepository;
_cityRepository = cityRepository;
_districtRepository = districtRepository;
2026-03-18 01:30:27 +08:00
_easyCachingProvider = easyCachingProvider;
_idGenerator = idGenerator;
2026-03-18 00:05:53 +08:00
}
/// <summary>
2026-03-18 01:30:27 +08:00
/// 获取所有省份 (带缓存)
2026-03-18 00:05:53 +08:00
/// </summary>
/// <returns></returns>
[DisplayName("获取所有省份")]
2026-03-18 01:30:27 +08:00
[AllowAnonymous]
2026-03-18 00:05:53 +08:00
public async Task<List<RegionDto>> GetProvinces()
{
2026-03-18 01:30:27 +08:00
var cacheValue = await _easyCachingProvider.GetAsync<List<RegionDto>>(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;
2026-03-18 00:05:53 +08:00
}
/// <summary>
2026-03-18 01:30:27 +08:00
/// 根据省份代码获取城市列表 (带缓存)
2026-03-18 00:05:53 +08:00
/// </summary>
2026-03-18 01:30:27 +08:00
/// <param name="provinceCode">省份代码</param>
2026-03-18 00:05:53 +08:00
/// <returns></returns>
[DisplayName("根据省份代码获取城市列表")]
2026-03-18 01:30:27 +08:00
[AllowAnonymous]
public async Task<List<RegionDto>> GetCitiesByProvince([FromQuery] string provinceCode)
2026-03-18 00:05:53 +08:00
{
2026-03-18 01:30:27 +08:00
if (string.IsNullOrWhiteSpace(provinceCode)) throw Oops.Oh("省份代码不能为空");
string cacheKey = $"{CacheKey_Cities}{provinceCode}";
var cacheValue = await _easyCachingProvider.GetAsync<List<RegionDto>>(cacheKey);
if (cacheValue.HasValue) return cacheValue.Value;
2026-03-18 00:05:53 +08:00
var cities = await _cityRepository.GetListAsync(c => c.ProvinceCode == provinceCode);
2026-03-18 01:30:27 +08:00
// 防穿透:如果查询为空,也缓存一个空列表,时间较短
if (!cities.Any())
{
await _easyCachingProvider.SetAsync(cacheKey, new List<RegionDto>(), TimeSpan.FromMinutes(5));
return new List<RegionDto>();
}
2026-03-18 00:05:53 +08:00
2026-03-18 01:30:27 +08:00
var dtos = cities.Select(x => new RegionDto
{
Id = x.Id,
Code = x.CityCode,
Name = x.CityName,
ParentCode = x.ProvinceCode
}).ToList();
2026-03-18 00:05:53 +08:00
2026-03-18 01:30:27 +08:00
await _easyCachingProvider.SetAsync(cacheKey, dtos, TimeSpan.FromHours(new Random().Next(12, 24)));
return dtos;
}
2026-03-18 00:05:53 +08:00
/// <summary>
2026-03-18 01:30:27 +08:00
/// 根据城市代码获取区县列表 (带缓存)
2026-03-18 00:05:53 +08:00
/// </summary>
2026-03-18 01:30:27 +08:00
/// <param name="cityCode">城市代码</param>
2026-03-18 00:05:53 +08:00
/// <returns></returns>
[DisplayName("根据城市代码获取区县列表")]
2026-03-18 01:30:27 +08:00
[AllowAnonymous]
public async Task<List<RegionDto>> GetDistrictsByCity([FromQuery] string cityCode)
2026-03-18 00:05:53 +08:00
{
2026-03-18 01:30:27 +08:00
if (string.IsNullOrWhiteSpace(cityCode)) throw Oops.Oh("城市代码不能为空");
string cacheKey = $"{CacheKey_Districts}{cityCode}";
var cacheValue = await _easyCachingProvider.GetAsync<List<RegionDto>>(cacheKey);
if (cacheValue.HasValue) return cacheValue.Value;
2026-03-18 00:05:53 +08:00
var districts = await _districtRepository.GetListAsync(d => d.CityCode == cityCode);
2026-03-18 01:30:27 +08:00
if (!districts.Any())
{
await _easyCachingProvider.SetAsync(cacheKey, new List<RegionDto>(), TimeSpan.FromMinutes(5));
return new List<RegionDto>();
}
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;
}
/// <summary>
/// 分页查询省份信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("分页查询省份信息")]
[HttpPost("page/provinces")]
public async Task<SqlSugar.PageResult<RegionDto>> 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;
}
/// <summary>
/// 添加省份
/// </summary>
[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);
}
/// <summary>
/// 更新省份
/// </summary>
[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);
}
/// <summary>
/// 删除省份
/// </summary>
[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("该省份下存在城市,无法删除");
2026-06-06 23:34:12 +08:00
await _provinceRepository.SoftDeleteByIdAsync(entity.Id);
2026-03-18 01:30:27 +08:00
await _easyCachingProvider.RemoveAsync(CacheKey_Provinces);
2026-03-18 00:05:53 +08:00
}
}
2026-03-18 01:30:27 +08:00
}