feat: 国家地区支持增删改与导入,列表返回创建和更新时间
All checks were successful
Build and Deploy / deploy (push) Successful in 1m0s
All checks were successful
Build and Deploy / deploy (push) Successful in 1m0s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
5afd204d36
commit
8f64bbf02d
25
src/Oms.Application/Dtos/System/CountryImportInput.cs
Normal file
25
src/Oms.Application/Dtos/System/CountryImportInput.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Oms.Application.Dtos.System;
|
||||
|
||||
/// <summary>
|
||||
/// 国家/地区 Excel 导入入参。
|
||||
/// </summary>
|
||||
public class CountryImportInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 导入模式:incremental=按二字码新增或更新;full=按二字码新增或更新,并软删除文件中未出现的国家。
|
||||
/// </summary>
|
||||
public string ImportMode { get; set; } = ImportModes.Incremental;
|
||||
|
||||
/// <summary>
|
||||
/// Excel 文件。
|
||||
/// </summary>
|
||||
public IFormFile File { get; set; } = null!;
|
||||
|
||||
public static class ImportModes
|
||||
{
|
||||
public const string Incremental = "incremental";
|
||||
public const string Full = "full";
|
||||
}
|
||||
}
|
||||
48
src/Oms.Application/Dtos/System/CountryImportOutput.cs
Normal file
48
src/Oms.Application/Dtos/System/CountryImportOutput.cs
Normal file
@ -0,0 +1,48 @@
|
||||
namespace Oms.Application.Dtos.System;
|
||||
|
||||
/// <summary>
|
||||
/// 国家/地区 Excel 导入结果。
|
||||
/// </summary>
|
||||
public class CountryImportOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 成功写入条数。
|
||||
/// </summary>
|
||||
public int SuccessCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 跳过条数。
|
||||
/// </summary>
|
||||
public int SkipCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 失败条数。
|
||||
/// </summary>
|
||||
public int FailCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 全量导入时软删除的条数。
|
||||
/// </summary>
|
||||
public int DeletedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 失败行明细。
|
||||
/// </summary>
|
||||
public List<CountryImportFailRow> Failures { get; set; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导入失败行。
|
||||
/// </summary>
|
||||
public class CountryImportFailRow
|
||||
{
|
||||
/// <summary>
|
||||
/// Excel 行号(含表头,从 2 起为数据行)。
|
||||
/// </summary>
|
||||
public int RowNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 失败原因。
|
||||
/// </summary>
|
||||
public string Reason { get; set; } = string.Empty;
|
||||
}
|
||||
@ -24,4 +24,14 @@ public class CountryOutput
|
||||
/// ISO 3166-1 alpha-2 国家二字码。
|
||||
/// </summary>
|
||||
public string CountryCode { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间。
|
||||
/// </summary>
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间;未更新时为空。
|
||||
/// </summary>
|
||||
public DateTime? UpdateTime { get; set; }
|
||||
}
|
||||
|
||||
27
src/Oms.Application/Dtos/System/CreateCountryInput.cs
Normal file
27
src/Oms.Application/Dtos/System/CreateCountryInput.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Oms.Application.Dtos.System;
|
||||
|
||||
/// <summary>
|
||||
/// 新增国家/地区入参。
|
||||
/// </summary>
|
||||
public class CreateCountryInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 国家中文名。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string ChineseName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 国家英文名。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string EnglishName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// ISO 3166-1 alpha-2 国家二字码。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryCode { get; set; } = string.Empty;
|
||||
}
|
||||
33
src/Oms.Application/Dtos/System/UpdateCountryInput.cs
Normal file
33
src/Oms.Application/Dtos/System/UpdateCountryInput.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Oms.Application.Dtos.System;
|
||||
|
||||
/// <summary>
|
||||
/// 更新国家/地区入参。
|
||||
/// </summary>
|
||||
public class UpdateCountryInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家中文名。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string ChineseName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 国家英文名。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string EnglishName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// ISO 3166-1 alpha-2 国家二字码。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryCode { get; set; } = string.Empty;
|
||||
}
|
||||
@ -1,6 +1,9 @@
|
||||
using Fake.Application;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oms.Application.Dtos.Shared;
|
||||
using Oms.Application.Dtos.System;
|
||||
using Oms.Application.Shared;
|
||||
using Oms.Domain.SystemAggregate.Entities;
|
||||
using Oms.Domain.SystemAggregate.Repositories;
|
||||
|
||||
namespace Oms.Application.Services.System;
|
||||
@ -16,20 +19,249 @@ public class CountryService : ApplicationService
|
||||
/// <summary>
|
||||
/// 查询国家列表。
|
||||
/// </summary>
|
||||
/// <returns>国家列表,返回全部有效国家主数据。</returns>
|
||||
/// <returns>国家列表,返回全部有效国家主数据,按二字码、英文名排序。</returns>
|
||||
[HttpGet("/system/country/list")]
|
||||
public async Task<List<CountryOutput>> GetListAsync()
|
||||
{
|
||||
return (await CountryRepository.GetListAsync())
|
||||
.OrderBy(x => x.CountryCode)
|
||||
.ThenBy(x => x.EnglishName)
|
||||
.Select(x => new CountryOutput
|
||||
{
|
||||
Id = x.Id,
|
||||
ChineseName = x.ChineseName,
|
||||
EnglishName = x.EnglishName,
|
||||
CountryCode = x.CountryCode
|
||||
})
|
||||
.Select(ToOutput)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增国家/地区。二字码已存在时失败;若仅存在同码软删除记录则恢复该记录。
|
||||
/// </summary>
|
||||
/// <param name="input">中文名、英文名、二字码。</param>
|
||||
/// <returns>无返回体,仅完成新增。</returns>
|
||||
[HttpPost("/system/country/create")]
|
||||
public async Task CreateAsync(CreateCountryInput input)
|
||||
{
|
||||
var row = ToImportRow(input.ChineseName, input.EnglishName, input.CountryCode);
|
||||
await SaveAsync(row, excludeId: null, updateWhenCodeExists: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新国家/地区。二字码与其他有效记录冲突时失败。
|
||||
/// </summary>
|
||||
/// <param name="input">主键及中文名、英文名、二字码。</param>
|
||||
/// <returns>无返回体,仅完成更新。</returns>
|
||||
[HttpPost("/system/country/update")]
|
||||
public async Task UpdateAsync(UpdateCountryInput input)
|
||||
{
|
||||
var entity = await GetOrThrowAsync(input.Id);
|
||||
var row = ToImportRow(input.ChineseName, input.EnglishName, input.CountryCode);
|
||||
await SaveAsync(row, excludeId: entity.Id, updateWhenCodeExists: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除国家/地区(软删除)。
|
||||
/// </summary>
|
||||
/// <param name="input">目标主键。</param>
|
||||
/// <returns>无返回体,仅完成删除。</returns>
|
||||
[HttpPost("/system/country/delete")]
|
||||
public async Task DeleteAsync(IdInput input)
|
||||
{
|
||||
var entity = await GetOrThrowAsync(input.Id);
|
||||
await CountryRepository.DeleteAsync(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载国家/地区导入模板。
|
||||
/// </summary>
|
||||
/// <returns>xlsx 文件,列为国家二字码、中文名、英文名。</returns>
|
||||
[HttpGet("/system/country/import-template")]
|
||||
public async Task<FileContentResult> DownloadImportTemplateAsync()
|
||||
{
|
||||
var bytes = await CountryImportHelper.BuildTemplateBytesAsync();
|
||||
return new FileContentResult(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
{
|
||||
FileDownloadName = CountryImportHelper.TemplateFileName
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导入国家/地区(Excel)。增量按二字码新增或更新;全量还会软删除文件中未出现的国家。
|
||||
/// </summary>
|
||||
/// <param name="input">Excel 文件与导入模式。</param>
|
||||
/// <returns>成功、跳过、失败及全量删除条数。</returns>
|
||||
[HttpPost("/system/country/import")]
|
||||
public async Task<CountryImportOutput> ImportAsync([FromForm] CountryImportInput input)
|
||||
{
|
||||
if (input.File == null || input.File.Length == 0)
|
||||
{
|
||||
throw new BusinessException("请上传 Excel 文件");
|
||||
}
|
||||
|
||||
var importMode = NormalizeImportMode(input.ImportMode);
|
||||
await using var stream = input.File.OpenReadStream();
|
||||
var parsedRows = await CountryImportHelper.ParseImportRowsAsync(stream);
|
||||
if (parsedRows.Count == 0)
|
||||
{
|
||||
throw new BusinessException("Excel 中无有效数据行");
|
||||
}
|
||||
|
||||
var output = new CountryImportOutput();
|
||||
var touchedCodes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
var seenCodes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var (rowNumber, row) in parsedRows)
|
||||
{
|
||||
var rawCode = row.CountryCode?.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(rawCode))
|
||||
{
|
||||
touchedCodes.Add(rawCode.ToUpperInvariant());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CountryImportHelper.Normalize(row);
|
||||
var code = row.CountryCode ?? string.Empty;
|
||||
if (!seenCodes.Add(code))
|
||||
{
|
||||
output.SkipCount++;
|
||||
output.Failures.Add(new CountryImportFailRow
|
||||
{
|
||||
RowNumber = rowNumber,
|
||||
Reason = "文件内二字码重复,已跳过"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
await SaveAsync(row, excludeId: null, updateWhenCodeExists: true);
|
||||
output.SuccessCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
output.FailCount++;
|
||||
output.Failures.Add(new CountryImportFailRow
|
||||
{
|
||||
RowNumber = rowNumber,
|
||||
Reason = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (importMode == CountryImportInput.ImportModes.Full && output.SuccessCount > 0)
|
||||
{
|
||||
var existing = await CountryRepository.GetListAsync();
|
||||
foreach (var entity in existing)
|
||||
{
|
||||
var code = entity.CountryCode?.Trim().ToUpperInvariant() ?? string.Empty;
|
||||
if (touchedCodes.Contains(code))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await CountryRepository.DeleteAsync(entity);
|
||||
output.DeletedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private async Task SaveAsync(CountryImportRow row, int? excludeId, bool updateWhenCodeExists)
|
||||
{
|
||||
try
|
||||
{
|
||||
CountryImportHelper.Normalize(row);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
throw new BusinessException(ex.Message);
|
||||
}
|
||||
|
||||
var active = await CountryRepository.GetByCountryCodeAsync(row.CountryCode);
|
||||
if (active != null && active.Id != excludeId)
|
||||
{
|
||||
if (updateWhenCodeExists)
|
||||
{
|
||||
ApplyNames(active, row);
|
||||
await CountryRepository.UpdateAsync(active);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new BusinessException("国家二字码已存在");
|
||||
}
|
||||
|
||||
var deleted = await CountryRepository.GetByCountryCodeIncludingDeletedAsync(row.CountryCode);
|
||||
if (deleted != null && deleted.IsDeleted && deleted.Id != excludeId)
|
||||
{
|
||||
if (excludeId != null)
|
||||
{
|
||||
throw new BusinessException("国家二字码已被删除记录占用,请新增同码以恢复");
|
||||
}
|
||||
|
||||
deleted.IsDeleted = false;
|
||||
deleted.CountryCode = row.CountryCode;
|
||||
ApplyNames(deleted, row);
|
||||
await CountryRepository.UpdateAsync(deleted);
|
||||
return;
|
||||
}
|
||||
|
||||
if (excludeId != null)
|
||||
{
|
||||
var entity = await GetOrThrowAsync(excludeId.Value);
|
||||
entity.CountryCode = row.CountryCode;
|
||||
ApplyNames(entity, row);
|
||||
await CountryRepository.UpdateAsync(entity);
|
||||
return;
|
||||
}
|
||||
|
||||
await CountryRepository.InsertAsync(new Country
|
||||
{
|
||||
CountryCode = row.CountryCode,
|
||||
ChineseName = row.ChineseName,
|
||||
EnglishName = row.EnglishName
|
||||
});
|
||||
}
|
||||
|
||||
private static void ApplyNames(Country entity, CountryImportRow row)
|
||||
{
|
||||
entity.ChineseName = row.ChineseName;
|
||||
entity.EnglishName = row.EnglishName;
|
||||
}
|
||||
|
||||
private static CountryImportRow ToImportRow(string chineseName, string englishName, string countryCode) =>
|
||||
new()
|
||||
{
|
||||
ChineseName = chineseName,
|
||||
EnglishName = englishName,
|
||||
CountryCode = countryCode
|
||||
};
|
||||
|
||||
private async Task<Country> GetOrThrowAsync(int id)
|
||||
{
|
||||
var entity = await CountryRepository.FirstOrDefaultAsync(x => x.Id == id);
|
||||
if (entity == null)
|
||||
{
|
||||
throw new BusinessException("国家不存在");
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static CountryOutput ToOutput(Country x) => new()
|
||||
{
|
||||
Id = x.Id,
|
||||
ChineseName = x.ChineseName,
|
||||
EnglishName = x.EnglishName,
|
||||
CountryCode = x.CountryCode,
|
||||
CreateTime = x.CreateTime,
|
||||
UpdateTime = x.UpdateTime == default ? null : x.UpdateTime
|
||||
};
|
||||
|
||||
private static string NormalizeImportMode(string? mode)
|
||||
{
|
||||
if (string.Equals(mode, CountryImportInput.ImportModes.Full, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(mode, "全量", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(mode, "replace", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return CountryImportInput.ImportModes.Full;
|
||||
}
|
||||
|
||||
return CountryImportInput.ImportModes.Incremental;
|
||||
}
|
||||
}
|
||||
|
||||
179
src/Oms.Application/Shared/CountryImportHelper.cs
Normal file
179
src/Oms.Application/Shared/CountryImportHelper.cs
Normal file
@ -0,0 +1,179 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using MiniExcelLibs;
|
||||
|
||||
namespace Oms.Application.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// 国家/地区 Excel 模板与导入解析。
|
||||
/// </summary>
|
||||
public static class CountryImportHelper
|
||||
{
|
||||
public const string TemplateSheetName = "导入模板";
|
||||
public const string TemplateFileName = "国家地区导入模板.xlsx";
|
||||
|
||||
private static readonly Regex CountryCodePattern = new("^[A-Za-z]{2}$", RegexOptions.Compiled);
|
||||
|
||||
private static readonly Dictionary<string, string> HeaderAliases = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["国家二字码"] = nameof(CountryImportRow.CountryCode),
|
||||
["二字码"] = nameof(CountryImportRow.CountryCode),
|
||||
["国家代码"] = nameof(CountryImportRow.CountryCode),
|
||||
["CountryCode"] = nameof(CountryImportRow.CountryCode),
|
||||
["countryCode"] = nameof(CountryImportRow.CountryCode),
|
||||
["中文名"] = nameof(CountryImportRow.ChineseName),
|
||||
["国家中文名"] = nameof(CountryImportRow.ChineseName),
|
||||
["ChineseName"] = nameof(CountryImportRow.ChineseName),
|
||||
["chineseName"] = nameof(CountryImportRow.ChineseName),
|
||||
["英文名"] = nameof(CountryImportRow.EnglishName),
|
||||
["国家英文名"] = nameof(CountryImportRow.EnglishName),
|
||||
["EnglishName"] = nameof(CountryImportRow.EnglishName),
|
||||
["englishName"] = nameof(CountryImportRow.EnglishName),
|
||||
};
|
||||
|
||||
public static async Task<byte[]> BuildTemplateBytesAsync()
|
||||
{
|
||||
var templateRows = new List<Dictionary<string, object?>>
|
||||
{
|
||||
new()
|
||||
{
|
||||
["国家二字码"] = "CN",
|
||||
["中文名"] = "中国",
|
||||
["英文名"] = "China"
|
||||
},
|
||||
new()
|
||||
{
|
||||
["国家二字码"] = "US",
|
||||
["中文名"] = "美国",
|
||||
["英文名"] = "United States"
|
||||
}
|
||||
};
|
||||
|
||||
var sheets = new Dictionary<string, object>
|
||||
{
|
||||
[TemplateSheetName] = templateRows
|
||||
};
|
||||
|
||||
await using var stream = new MemoryStream();
|
||||
await MiniExcel.SaveAsAsync(stream, sheets);
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
public static async Task<List<(int RowNumber, CountryImportRow Row)>> ParseImportRowsAsync(Stream fileStream)
|
||||
{
|
||||
if (fileStream.CanSeek)
|
||||
{
|
||||
fileStream.Position = 0;
|
||||
}
|
||||
|
||||
var sheetNames = MiniExcel.GetSheetNames(fileStream);
|
||||
var sheetName = sheetNames.FirstOrDefault(x =>
|
||||
string.Equals(x, TemplateSheetName, StringComparison.OrdinalIgnoreCase))
|
||||
?? sheetNames.FirstOrDefault();
|
||||
|
||||
if (fileStream.CanSeek)
|
||||
{
|
||||
fileStream.Position = 0;
|
||||
}
|
||||
|
||||
var rows = await MiniExcel.QueryAsync(fileStream, useHeaderRow: true, sheetName: sheetName);
|
||||
var result = new List<(int RowNumber, CountryImportRow Row)>();
|
||||
var rowNumber = 1;
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
rowNumber++;
|
||||
if (row is not IDictionary<string, object> rowDict || IsEmptyRow(rowDict))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add((rowNumber, ParseRow(rowDict)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Normalize(CountryImportRow row)
|
||||
{
|
||||
row.CountryCode = row.CountryCode?.Trim().ToUpperInvariant() ?? string.Empty;
|
||||
row.ChineseName = row.ChineseName?.Trim() ?? string.Empty;
|
||||
row.EnglishName = row.EnglishName?.Trim() ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(row.CountryCode))
|
||||
{
|
||||
throw new InvalidOperationException("国家二字码不能为空");
|
||||
}
|
||||
|
||||
if (!CountryCodePattern.IsMatch(row.CountryCode))
|
||||
{
|
||||
throw new InvalidOperationException("国家二字码须为 2 位字母,如 CN、US");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(row.ChineseName))
|
||||
{
|
||||
throw new InvalidOperationException("中文名不能为空");
|
||||
}
|
||||
|
||||
if (row.ChineseName.Length > 128)
|
||||
{
|
||||
throw new InvalidOperationException("中文名不可超过 128 个字符");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(row.EnglishName))
|
||||
{
|
||||
throw new InvalidOperationException("英文名不能为空");
|
||||
}
|
||||
|
||||
if (row.EnglishName.Length > 128)
|
||||
{
|
||||
throw new InvalidOperationException("英文名不可超过 128 个字符");
|
||||
}
|
||||
}
|
||||
|
||||
private static CountryImportRow ParseRow(IDictionary<string, object> rowDict)
|
||||
{
|
||||
var mapped = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var (header, value) in rowDict)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(header) || !HeaderAliases.TryGetValue(header.Trim(), out var field))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
mapped[field] = NormalizeCell(value);
|
||||
}
|
||||
|
||||
return new CountryImportRow
|
||||
{
|
||||
CountryCode = mapped.GetValueOrDefault(nameof(CountryImportRow.CountryCode)) ?? string.Empty,
|
||||
ChineseName = mapped.GetValueOrDefault(nameof(CountryImportRow.ChineseName)) ?? string.Empty,
|
||||
EnglishName = mapped.GetValueOrDefault(nameof(CountryImportRow.EnglishName)) ?? string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
private static string? NormalizeCell(object? value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var text = Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture)?.Trim();
|
||||
return string.IsNullOrWhiteSpace(text) ? null : text;
|
||||
}
|
||||
|
||||
private static bool IsEmptyRow(IDictionary<string, object> rowDict) =>
|
||||
rowDict.Values.All(value => string.IsNullOrWhiteSpace(NormalizeCell(value)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 国家导入行。
|
||||
/// </summary>
|
||||
public class CountryImportRow
|
||||
{
|
||||
public string CountryCode { get; set; } = string.Empty;
|
||||
|
||||
public string ChineseName { get; set; } = string.Empty;
|
||||
|
||||
public string EnglishName { get; set; } = string.Empty;
|
||||
}
|
||||
@ -6,4 +6,9 @@ namespace Oms.Domain.SystemAggregate.Repositories;
|
||||
public interface ICountryRepository : IRepository<Country>
|
||||
{
|
||||
Task<Country?> GetByCountryCodeAsync(string countryCode, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 按二字码查询,包含已软删除记录,用于恢复同码数据。
|
||||
/// </summary>
|
||||
Task<Country?> GetByCountryCodeIncludingDeletedAsync(string countryCode, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@ -11,4 +11,15 @@ public class CountryRepository : OmsSqlSugarRepository<Country>, ICountryReposit
|
||||
{
|
||||
return FirstOrDefaultAsync(x => x.CountryCode == countryCode, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Country?> GetByCountryCodeIncludingDeletedAsync(
|
||||
string countryCode,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var db = await GetDbContextAsync(cancellationToken);
|
||||
return await SoftDeleteQuery.FirstIncludingDeletedAsync<Country>(
|
||||
db,
|
||||
x => x.CountryCode == countryCode,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user