feat: KPI分区明细列表支持表头字段后端筛选
All checks were successful
Build and Deploy / deploy (push) Successful in 1m22s

EOF

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zzh 2026-10-29 16:04:02 +08:00
parent b87414aafd
commit 8a4848644f
4 changed files with 150 additions and 9 deletions

View File

@ -19,4 +19,39 @@ public class KpiAreaInfoListInput
/// 每页条数。
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 国家/地区(匹配国家名称或国家代码,模糊)。
/// </summary>
public string? Country { get; set; }
/// <summary>
/// 匹配维度(如 zip/state/city/country,模糊)。
/// </summary>
public string? MatchDim { get; set; }
/// <summary>
/// 匹配方式(如 prefix/exact/range/all,模糊)。
/// </summary>
public string? MatchMode { get; set; }
/// <summary>
/// 州/省(模糊)。
/// </summary>
public string? Province { get; set; }
/// <summary>
/// 城市(模糊)。
/// </summary>
public string? City { get; set; }
/// <summary>
/// 起始邮编(模糊)。
/// </summary>
public string? LeftPostcode { get; set; }
/// <summary>
/// 截止邮编(模糊)。
/// </summary>
public string? RightPostcode { get; set; }
}

View File

@ -106,8 +106,9 @@ public class KpiAreaService : ApplicationService
await GetAreaOrThrowAsync(input.AreaId);
var pageIndex = OmsDocHelper.NormalizePage(input.Page);
var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
var entities = await KpiAreaRepository.GetInfosAsync(input.AreaId, pageIndex, pageSize);
var total = await KpiAreaRepository.CountInfosAsync(input.AreaId);
var filter = BuildInfoQueryFilter(input);
var entities = await KpiAreaRepository.GetInfosAsync(input.AreaId, pageIndex, pageSize, filter);
var total = await KpiAreaRepository.CountInfosAsync(input.AreaId, filter);
return new OmsPageResult<KpiAreaInfoOutput>
{
Items = entities.Select(MapInfoOutput).ToList(),
@ -115,6 +116,18 @@ public class KpiAreaService : ApplicationService
};
}
private static KpiAreaInfoQueryFilter BuildInfoQueryFilter(KpiAreaInfoListInput input) =>
new()
{
Country = input.Country,
MatchDim = input.MatchDim,
MatchMode = input.MatchMode,
Province = input.Province,
City = input.City,
LeftPostcode = input.LeftPostcode,
RightPostcode = input.RightPostcode
};
/// <summary>
/// 新增KPI分区明细。
/// </summary>

View File

@ -3,15 +3,37 @@ using Oms.Domain.ChannelsAggregate.Entities;
namespace Oms.Domain.ChannelsAggregate.Repositories;
/// <summary>
/// KPI 分区明细列表筛选条件(字段均为可选,模糊匹配)。
/// </summary>
public class KpiAreaInfoQueryFilter
{
public string? Country { get; set; }
public string? MatchDim { get; set; }
public string? MatchMode { get; set; }
public string? Province { get; set; }
public string? City { get; set; }
public string? LeftPostcode { get; set; }
public string? RightPostcode { get; set; }
}
public interface IKpiAreaRepository : IRepository<KpiArea>, IKpiAreaPublishRepository
{
Task<KpiArea?> GetByAppKeyTypeAndNameAsync(string appKey, string areaType, string name, CancellationToken cancellationToken = default);
Task<bool> ExistsNameAsync(string appKey, string areaType, string name, CancellationToken cancellationToken = default);
Task<List<KpiAreaInfo>> GetInfosAsync(int areaId, int pageIndex, int pageSize, CancellationToken cancellationToken = default);
Task<List<KpiAreaInfo>> GetInfosAsync(
int areaId,
int pageIndex,
int pageSize,
KpiAreaInfoQueryFilter? filter = null,
CancellationToken cancellationToken = default);
Task<int> CountInfosAsync(int areaId, CancellationToken cancellationToken = default);
Task<int> CountInfosAsync(
int areaId,
KpiAreaInfoQueryFilter? filter = null,
CancellationToken cancellationToken = default);
Task<KpiAreaInfo> InsertInfoAsync(KpiAreaInfo info, CancellationToken cancellationToken = default);

View File

@ -18,20 +18,91 @@ public class KpiAreaRepository : OmsSqlSugarRepository<KpiArea>, IKpiAreaReposit
return AnyAsync(x => x.AppKey == appKey && x.AreaType == areaType && x.Name == name, cancellationToken);
}
public async Task<List<KpiAreaInfo>> GetInfosAsync(int areaId, int pageIndex, int pageSize, CancellationToken cancellationToken = default)
public async Task<List<KpiAreaInfo>> GetInfosAsync(
int areaId,
int pageIndex,
int pageSize,
KpiAreaInfoQueryFilter? filter = null,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
// 依赖 idx_kpi_info_area_id (AreaId, Id),避免大分区 filesort
return await db.Queryable<KpiAreaInfo>()
.Where(x => x.AreaId == areaId)
return await BuildInfoQueryable(db, areaId, filter)
.OrderBy(x => x.Id, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, cancellationToken);
}
public async Task<int> CountInfosAsync(int areaId, CancellationToken cancellationToken = default)
public async Task<int> CountInfosAsync(
int areaId,
KpiAreaInfoQueryFilter? filter = null,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
return await db.Queryable<KpiAreaInfo>().Where(x => x.AreaId == areaId).CountAsync(cancellationToken);
return await BuildInfoQueryable(db, areaId, filter).CountAsync(cancellationToken);
}
private static ISugarQueryable<KpiAreaInfo> BuildInfoQueryable(
ISqlSugarClient db,
int areaId,
KpiAreaInfoQueryFilter? filter)
{
var query = db.Queryable<KpiAreaInfo>().Where(x => x.AreaId == areaId);
if (filter == null)
{
return query;
}
var country = TrimOrNull(filter.Country);
if (country != null)
{
query = query.Where(x =>
(x.CountryName != null && x.CountryName.Contains(country))
|| (x.CountryCode != null && x.CountryCode.Contains(country)));
}
var matchDim = TrimOrNull(filter.MatchDim);
if (matchDim != null)
{
query = query.Where(x => x.MatchDim != null && x.MatchDim.Contains(matchDim));
}
var matchMode = TrimOrNull(filter.MatchMode);
if (matchMode != null)
{
query = query.Where(x => x.MatchMode != null && x.MatchMode.Contains(matchMode));
}
var province = TrimOrNull(filter.Province);
if (province != null)
{
query = query.Where(x => x.Province != null && x.Province.Contains(province));
}
var city = TrimOrNull(filter.City);
if (city != null)
{
query = query.Where(x => x.City != null && x.City.Contains(city));
}
var leftPostcode = TrimOrNull(filter.LeftPostcode);
if (leftPostcode != null)
{
query = query.Where(x => x.LeftPostcode != null && x.LeftPostcode.Contains(leftPostcode));
}
var rightPostcode = TrimOrNull(filter.RightPostcode);
if (rightPostcode != null)
{
query = query.Where(x => x.RightPostcode != null && x.RightPostcode.Contains(rightPostcode));
}
return query;
}
private static string? TrimOrNull(string? value)
{
var trimmed = value?.Trim();
return string.IsNullOrEmpty(trimmed) ? null : trimmed;
}
public async Task<KpiAreaInfo> InsertInfoAsync(KpiAreaInfo info, CancellationToken cancellationToken = default)