20261008-ide1.0发布 #1

Merged
zzh merged 42 commits from test into master 2026-10-08 09:18:00 +08:00
2 changed files with 110 additions and 6 deletions
Showing only changes of commit 04dc9ca1ff - Show all commits

View File

@ -11,6 +11,9 @@ namespace Oms.Application.Shared;
/// </summary> /// </summary>
public static class KpiAreaInfoImportHelper public static class KpiAreaInfoImportHelper
{ {
public const string TemplateSheetName = "导入模板";
public const string EnumSheetName = "枚举值";
private static readonly Regex ChinesePattern = new(@"[\u4E00-\u9FA5]", RegexOptions.Compiled); private static readonly Regex ChinesePattern = new(@"[\u4E00-\u9FA5]", RegexOptions.Compiled);
private static readonly Dictionary<string, string> HeaderAliases = new(StringComparer.OrdinalIgnoreCase) private static readonly Dictionary<string, string> HeaderAliases = new(StringComparer.OrdinalIgnoreCase)
@ -48,12 +51,11 @@ public static class KpiAreaInfoImportHelper
public static async Task<byte[]> BuildTemplateBytesAsync() public static async Task<byte[]> BuildTemplateBytesAsync()
{ {
var rows = new List<Dictionary<string, object?>> var templateRows = new List<Dictionary<string, object?>>
{ {
new() new()
{ {
["国家代码"] = "US", ["国家代码"] = "US",
["国家名称"] = "美国",
["匹配维度"] = "zip", ["匹配维度"] = "zip",
["匹配方式"] = "range", ["匹配方式"] = "range",
["州/省"] = "", ["州/省"] = "",
@ -64,7 +66,6 @@ public static class KpiAreaInfoImportHelper
new() new()
{ {
["国家代码"] = "US", ["国家代码"] = "US",
["国家名称"] = "美国",
["匹配维度"] = "zip", ["匹配维度"] = "zip",
["匹配方式"] = "prefix", ["匹配方式"] = "prefix",
["州/省"] = "", ["州/省"] = "",
@ -75,7 +76,6 @@ public static class KpiAreaInfoImportHelper
new() new()
{ {
["国家代码"] = "US", ["国家代码"] = "US",
["国家名称"] = "美国",
["匹配维度"] = "state", ["匹配维度"] = "state",
["匹配方式"] = "exact", ["匹配方式"] = "exact",
["州/省"] = "CA", ["州/省"] = "CA",
@ -85,11 +85,44 @@ public static class KpiAreaInfoImportHelper
} }
}; };
var enumRows = new List<Dictionary<string, object?>>
{
new() { ["枚举类型"] = "匹配维度", ["编码"] = "country", ["名称"] = "仅国家" },
new() { ["枚举类型"] = "匹配维度", ["编码"] = "zip", ["名称"] = "邮编" },
new() { ["枚举类型"] = "匹配维度", ["编码"] = "state", ["名称"] = "州/省" },
new() { ["枚举类型"] = "匹配维度", ["编码"] = "city", ["名称"] = "城市" },
new() { ["枚举类型"] = "匹配方式", ["编码"] = "prefix", ["名称"] = "前缀" },
new() { ["枚举类型"] = "匹配方式", ["编码"] = "exact", ["名称"] = "精确" },
new() { ["枚举类型"] = "匹配方式", ["编码"] = "range", ["名称"] = "区间" },
new() { ["枚举类型"] = "匹配方式", ["编码"] = "all", ["名称"] = "不限" }
};
var sheets = new Dictionary<string, object>
{
[TemplateSheetName] = templateRows,
[EnumSheetName] = enumRows
};
await using var stream = new MemoryStream(); await using var stream = new MemoryStream();
await MiniExcel.SaveAsAsync(stream, rows); await MiniExcel.SaveAsAsync(stream, sheets);
return stream.ToArray(); return stream.ToArray();
} }
internal static string? ResolveImportSheetName(IEnumerable<string> sheetNames)
{
var names = sheetNames.Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
if (names.Count == 0)
{
return null;
}
return names.FirstOrDefault(x =>
string.Equals(x, TemplateSheetName, StringComparison.OrdinalIgnoreCase))
?? names.FirstOrDefault(x =>
!string.Equals(x, EnumSheetName, StringComparison.OrdinalIgnoreCase))
?? names[0];
}
public static string ResolveTemplateFileName(string? areaType) => public static string ResolveTemplateFileName(string? areaType) =>
areaType?.Trim().ToLowerInvariant() switch areaType?.Trim().ToLowerInvariant() switch
{ {
@ -101,7 +134,20 @@ public static class KpiAreaInfoImportHelper
public static async Task<List<(int RowNumber, KpiAreaInfo Entity)>> ParseImportRowsAsync(Stream fileStream) public static async Task<List<(int RowNumber, KpiAreaInfo Entity)>> ParseImportRowsAsync(Stream fileStream)
{ {
var rows = await MiniExcel.QueryAsync(fileStream, useHeaderRow: true); if (fileStream.CanSeek)
{
fileStream.Position = 0;
}
var sheetNames = MiniExcel.GetSheetNames(fileStream);
var sheetName = ResolveImportSheetName(sheetNames);
if (fileStream.CanSeek)
{
fileStream.Position = 0;
}
var rows = await MiniExcel.QueryAsync(fileStream, useHeaderRow: true, sheetName: sheetName);
var result = new List<(int RowNumber, KpiAreaInfo Entity)>(); var result = new List<(int RowNumber, KpiAreaInfo Entity)>();
var rowNumber = 1; var rowNumber = 1;

View File

@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using MiniExcelLibs;
using Oms.Application.Shared; using Oms.Application.Shared;
using Oms.Domain.ChannelsAggregate.Entities; using Oms.Domain.ChannelsAggregate.Entities;
using Oms.Domain.SystemAggregate.Entities; using Oms.Domain.SystemAggregate.Entities;
@ -7,6 +8,63 @@ namespace Oms.Tests;
public class KpiAreaInfoImportHelperTests public class KpiAreaInfoImportHelperTests
{ {
[Fact]
public async Task BuildTemplateBytesAsync_ShouldUseNamedSheetsWithoutCountryName()
{
var bytes = await KpiAreaInfoImportHelper.BuildTemplateBytesAsync();
await using var stream = new MemoryStream(bytes);
var sheetNames = MiniExcel.GetSheetNames(stream);
sheetNames.Should().Equal(
KpiAreaInfoImportHelper.TemplateSheetName,
KpiAreaInfoImportHelper.EnumSheetName);
stream.Position = 0;
var templateRows = (await MiniExcel.QueryAsync(
stream,
useHeaderRow: true,
sheetName: KpiAreaInfoImportHelper.TemplateSheetName))
.Cast<IDictionary<string, object>>()
.ToList();
templateRows.Should().NotBeEmpty();
templateRows[0].Keys.Should().NotContain(key => key.Contains("国家名称"));
templateRows[0].Should().ContainKey("国家代码");
templateRows[0].Should().ContainKey("匹配维度");
templateRows[0].Should().ContainKey("匹配方式");
stream.Position = 0;
var enumRows = (await MiniExcel.QueryAsync(
stream,
useHeaderRow: true,
sheetName: KpiAreaInfoImportHelper.EnumSheetName))
.Cast<IDictionary<string, object>>()
.ToList();
enumRows.Should().HaveCount(8);
enumRows.Should().Contain(row =>
Convert.ToString(row["枚举类型"]) == "匹配维度"
&& Convert.ToString(row["编码"]) == "zip"
&& Convert.ToString(row["名称"]) == "邮编");
enumRows.Should().Contain(row =>
Convert.ToString(row["枚举类型"]) == "匹配方式"
&& Convert.ToString(row["编码"]) == "range"
&& Convert.ToString(row["名称"]) == "区间");
}
[Fact]
public async Task ParseImportRowsAsync_ShouldReadTemplateSheetOnly()
{
var bytes = await KpiAreaInfoImportHelper.BuildTemplateBytesAsync();
await using var stream = new MemoryStream(bytes);
var rows = await KpiAreaInfoImportHelper.ParseImportRowsAsync(stream);
rows.Should().HaveCount(3);
rows[0].Entity.CountryCode.Should().Be("US");
rows[0].Entity.CountryName.Should().BeNull();
rows[0].Entity.MatchDim.Should().Be("zip");
rows[0].Entity.MatchMode.Should().Be("range");
}
[Fact] [Fact]
public void PrepareEntityForImport_ValidRow_ShouldFillCountryName() public void PrepareEntityForImport_ValidRow_ShouldFillCountryName()
{ {