修复相关漏洞

This commit is contained in:
fuenmao
2026-09-17 09:53:50 +08:00
parent 20ea1bc70c
commit e9894538e8
28 changed files with 757 additions and 51 deletions
@@ -10,6 +10,7 @@ namespace SimpleAdmin.Application;
/// </summary>
public class OnboardingEmployeeService : IOnboardingEmployeeService
{
private const string EmployeeExportTemplateFileName = "员工导出模版.xlsx";
private readonly SqlSugarScope _db;
private readonly IOnboardingDataPermissionService _dataPermission;
@@ -110,12 +111,17 @@ public class OnboardingEmployeeService : IOnboardingEmployeeService
};
}).ToList();
using var package = new ExcelPackage();
var templatePath = ResolveEmployeeExportTemplatePath();
using var package = new ExcelPackage(new FileInfo(templatePath));
package.Workbook.Properties.Title = "员工明细";
package.Workbook.Properties.Subject = "员工基本、人事状态及主要合同信息";
package.Workbook.Properties.Created = DateTime.Now;
WriteEmployeeSheet(package.Workbook.Worksheets.Add("员工明细"), employeeRows);
var worksheet = package.Workbook.Worksheets.FirstOrDefault(item =>
item.Dimension != null
&& string.Equals(item.Cells[1, 1].Text.Trim(), "姓名", StringComparison.OrdinalIgnoreCase))
?? throw Oops.Bah("员工导出模板未找到以“姓名”开头的表头行,请检查模板内容");
WriteEmployeeSheet(worksheet, employeeRows);
return new OnboardingEmployeeExportFile
{
@@ -192,6 +198,11 @@ public class OnboardingEmployeeService : IOnboardingEmployeeService
/// <inheritdoc />
public async Task UpdateHrInfo(OnboardingEmployeeHrUpdateInput input)
{
await OnboardingConcurrency.RunEmployee(_db, input.Id, () => UpdateHrInfoCore(input));
}
private async Task UpdateHrInfoCore(OnboardingEmployeeHrUpdateInput input)
{
ValidateEmploymentType(input.EmploymentType);
ValidateSocialInsuranceStatus(input.SocialInsuranceStatus);
@@ -275,6 +286,11 @@ public class OnboardingEmployeeService : IOnboardingEmployeeService
/// <inheritdoc />
public async Task Regularize(OnboardingEmployeeRegularizeInput input)
{
await OnboardingConcurrency.RunEmployee(_db, input.Id, () => RegularizeCore(input));
}
private async Task RegularizeCore(OnboardingEmployeeRegularizeInput input)
{
if (!input.RegularizationDate.HasValue)
throw Oops.Bah("请选择转正日期");
@@ -370,6 +386,11 @@ public class OnboardingEmployeeService : IOnboardingEmployeeService
/// <inheritdoc />
public async Task RevokeRegularization(OnboardingEmployeeRegularizationRevokeInput input)
{
await OnboardingConcurrency.RunEmployee(_db, input.Id, () => RevokeRegularizationCore(input));
}
private async Task RevokeRegularizationCore(OnboardingEmployeeRegularizationRevokeInput input)
{
if (string.IsNullOrWhiteSpace(input.Remark))
throw Oops.Bah("请填写撤销原因");
@@ -775,7 +796,100 @@ public class OnboardingEmployeeService : IOnboardingEmployeeService
TextColumn("工资卡开户行", row => row.Employee.BankName, 22)
};
WriteWorksheet(worksheet, columns, rows);
WriteEmployeeTemplateWorksheet(worksheet, columns, rows);
}
private static void WriteEmployeeTemplateWorksheet(
ExcelWorksheet worksheet,
IReadOnlyList<ExportColumn<EmployeeExportContext>> columns,
IReadOnlyList<EmployeeExportContext> rows)
{
var templateColumnCount = worksheet.Dimension?.End.Column ?? 0;
if (templateColumnCount == 0)
throw Oops.Bah("员工导出模板没有配置表头");
var columnsByHeader = columns.ToDictionary(
item => item.Header,
item => item,
StringComparer.OrdinalIgnoreCase);
var headerAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["户口所在地"] = "户籍所在地"
};
var templateColumns = new List<(int Index, ExportColumn<EmployeeExportContext> Column)>();
var mappedHeaders = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
for (var columnIndex = 1; columnIndex <= templateColumnCount; columnIndex++)
{
var templateHeader = worksheet.Cells[1, columnIndex].Text?.Trim();
if (string.IsNullOrWhiteSpace(templateHeader))
throw Oops.Bah($"员工导出模板第 {columnIndex} 列表头为空,请补充后重新导出");
var exportHeader = headerAliases.GetValueOrDefault(templateHeader, templateHeader);
if (!columnsByHeader.TryGetValue(exportHeader, out var exportColumn))
throw Oops.Bah($"员工导出模板包含无法识别的表头“{templateHeader}”,请检查模板");
if (!mappedHeaders.Add(exportHeader))
throw Oops.Bah($"员工导出模板表头“{templateHeader}”重复,请检查模板");
templateColumns.Add((columnIndex, exportColumn));
}
var missingHeaders = columns
.Where(item => !mappedHeaders.Contains(item.Header))
.Select(item => item.Header)
.ToList();
if (missingHeaders.Count > 0)
throw Oops.Bah($"员工导出模板缺少表头:{string.Join("、", missingHeaders)}");
var existingLastRow = worksheet.Dimension?.End.Row ?? 1;
for (var rowIndex = 2; rowIndex <= existingLastRow; rowIndex++)
{
for (var columnIndex = 1; columnIndex <= templateColumnCount; columnIndex++)
worksheet.Cells[rowIndex, columnIndex].Value = null;
}
for (var rowIndex = 0; rowIndex < rows.Count; rowIndex++)
{
var excelRow = rowIndex + 2;
foreach (var templateColumn in templateColumns)
{
var cell = worksheet.Cells[excelRow, templateColumn.Index];
var value = templateColumn.Column.Value(rows[rowIndex]);
if (value != null)
cell.Value = value;
var templateNumberFormat = worksheet.Cells[1, templateColumn.Index]
.Style.Numberformat.Format;
cell.Style.Numberformat.Format = string.IsNullOrWhiteSpace(templateNumberFormat)
|| string.Equals(
templateNumberFormat,
"General",
StringComparison.OrdinalIgnoreCase)
? templateColumn.Column.NumberFormat
: templateNumberFormat;
}
}
}
private static string ResolveEmployeeExportTemplatePath()
{
var currentDirectory = Directory.GetCurrentDirectory();
var candidates = new[]
{
Path.Combine(AppContext.BaseDirectory, "Upload", EmployeeExportTemplateFileName),
Path.Combine(currentDirectory, "Upload", EmployeeExportTemplateFileName),
Path.Combine(currentDirectory, "SimpleAdmin.Web.Core", "Upload", EmployeeExportTemplateFileName),
Path.GetFullPath(Path.Combine(
currentDirectory,
"..",
"SimpleAdmin.Web.Core",
"Upload",
EmployeeExportTemplateFileName))
};
var templatePath = candidates.FirstOrDefault(File.Exists);
return templatePath
?? throw Oops.Bah("员工导出模板文件不存在,请检查部署目录中的 Upload/员工导出模版.xlsx");
}
private static void WriteContractSheet(