diff --git a/YD_Event.Application/Region/Dtos/RegionDto.cs b/YD_Event.Application/Region/Dtos/RegionDto.cs
index 80b06b7..99ccd24 100644
--- a/YD_Event.Application/Region/Dtos/RegionDto.cs
+++ b/YD_Event.Application/Region/Dtos/RegionDto.cs
@@ -1,11 +1,34 @@
+using System.ComponentModel.DataAnnotations;
+using SqlSugar;
namespace YD_Event.Application.Region.Dtos
{
+ ///
+ /// 区域分页查询输入
+ ///
+ public class RegionPageInput : Pagination
+ {
+ ///
+ /// 区域名称(模糊查询)
+ ///
+ public string? Name { get; set; }
+
+ ///
+ /// 区域编码
+ ///
+ public string? Code { get; set; }
+ }
+
///
/// 行政区域数据传输对象
///
public class RegionDto
{
+ ///
+ /// 主键ID
+ ///
+ public long Id { get; set; }
+
///
/// 区域编码
///
@@ -15,5 +38,41 @@ namespace YD_Event.Application.Region.Dtos
/// 区域名称
///
public string Name { get; set; }
+
+ ///
+ /// 父级编码 (省份为大区Code,城市为省份Code,区县为城市Code)
+ ///
+ public string ParentCode { get; set; }
}
-}
+
+ ///
+ /// 新增/更新行政区域输入
+ ///
+ public class RegionInput
+ {
+ ///
+ /// 主键ID (更新时必填)
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// 区域编码
+ ///
+ [Required(ErrorMessage = "区域编码不能为空")]
+ [MaxLength(50, ErrorMessage = "区域编码最大长度为50")]
+ public string Code { get; set; }
+
+ ///
+ /// 区域名称
+ ///
+ [Required(ErrorMessage = "区域名称不能为空")]
+ [MaxLength(50, ErrorMessage = "区域名称最大长度为50")]
+ public string Name { get; set; }
+
+ ///
+ /// 父级编码
+ ///
+ [MaxLength(50, ErrorMessage = "父级编码最大长度为50")]
+ public string ParentCode { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/YD_Event.Application/Region/RegionService.cs b/YD_Event.Application/Region/RegionService.cs
index b70e1da..2aeaf30 100644
--- a/YD_Event.Application/Region/RegionService.cs
+++ b/YD_Event.Application/Region/RegionService.cs
@@ -3,63 +3,227 @@ 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
{
///
/// 行政区域服务
///
+ [ApiDescriptionSettings("系统管理", Name = "Region", Order = 100)]
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)
+ ISqlSugarRepository districtRepository,
+ IEasyCachingProvider easyCachingProvider,
+ IIdGenerator idGenerator)
{
_provinceRepository = provinceRepository;
_cityRepository = cityRepository;
_districtRepository = districtRepository;
+ _easyCachingProvider = easyCachingProvider;
+ _idGenerator = idGenerator;
}
///
- /// 获取所有省份
+ /// 获取所有省份 (带缓存)
///
///
[DisplayName("获取所有省份")]
+ [AllowAnonymous]
public async Task> GetProvinces()
{
- var provinces = await _provinceRepository.GetListAsync();
- return provinces.Adapt>();
+ 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("根据省份代码获取城市列表")]
- public async Task> GetCitiesByProvince(string provinceCode)
+ [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);
- return cities.Adapt>();
+ // 防穿透:如果查询为空,也缓存一个空列表,时间较短
+ 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("根据城市代码获取区县列表")]
- public async Task> GetDistrictsByCity(string cityCode)
+ [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);
- return districts.Adapt>();
+ 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);
}
}
-}
+}
\ No newline at end of file
diff --git a/YD_Event.Application/YD_Event.Application.xml b/YD_Event.Application/YD_Event.Application.xml
index 4a0b9c3..c1c5818 100644
--- a/YD_Event.Application/YD_Event.Application.xml
+++ b/YD_Event.Application/YD_Event.Application.xml
@@ -4536,11 +4536,31 @@
赛事开始时间
应退金额
+
+
+ 区域分页查询输入
+
+
+
+
+ 区域名称(模糊查询)
+
+
+
+
+ 区域编码
+
+
行政区域数据传输对象
+
+
+ 主键ID
+
+
区域编码
@@ -4551,6 +4571,36 @@
区域名称
+
+
+ 父级编码 (省份为大区Code,城市为省份Code,区县为城市Code)
+
+
+
+
+ 新增/更新行政区域输入
+
+
+
+
+ 主键ID (更新时必填)
+
+
+
+
+ 区域编码
+
+
+
+
+ 区域名称
+
+
+
+
+ 父级编码
+
+
行政区域服务
@@ -4558,24 +4608,46 @@
- 获取所有省份
+ 获取所有省份 (带缓存)
- 根据省份代码获取城市列表
+ 根据省份代码获取城市列表 (带缓存)
-
+ 省份代码
- 根据城市代码获取区县列表
+ 根据城市代码获取区县列表 (带缓存)
-
+ 城市代码
+
+
+ 分页查询省份信息
+
+
+
+
+
+
+ 添加省份
+
+
+
+
+ 更新省份
+
+
+
+
+ 删除省份
+
+
角色名称
diff --git a/YD_Event.Core/YD_Event.Core.xml b/YD_Event.Core/YD_Event.Core.xml
index b90c0e6..eb69859 100644
--- a/YD_Event.Core/YD_Event.Core.xml
+++ b/YD_Event.Core/YD_Event.Core.xml
@@ -3011,11 +3011,6 @@
城市表
-
-
- 城市Id
-
-
省份Code
@@ -3061,11 +3056,6 @@
区县表
-
-
- 区县Id
-
-
城市Code
@@ -3401,11 +3391,6 @@
省份表
-
-
- 省份Id
-
-
省份Code