逻辑调整
This commit is contained in:
+160
@@ -0,0 +1,160 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 负责从 DOCX 中识别占位符并在不依赖 Word 进程的情况下替换文本节点。
|
||||
/// </summary>
|
||||
public static class DocxTemplateProcessor
|
||||
{
|
||||
private static readonly global::System.Text.RegularExpressions.Regex PlaceholderRegex = new(
|
||||
@"\{\{\s*(?<code>[A-Za-z][A-Za-z0-9_.-]*)\s*\}\}",
|
||||
global::System.Text.RegularExpressions.RegexOptions.Compiled);
|
||||
|
||||
private static readonly global::System.Xml.Linq.XNamespace WordNamespace =
|
||||
"http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
|
||||
/// <summary>
|
||||
/// 扫描正文、表格、页眉和页脚,返回去重后的变量编码。
|
||||
/// </summary>
|
||||
public static List<string> ExtractVariableCodes(byte[] docx)
|
||||
{
|
||||
using var input = new MemoryStream(docx);
|
||||
using var archive = new global::System.IO.Compression.ZipArchive(
|
||||
input,
|
||||
global::System.IO.Compression.ZipArchiveMode.Read);
|
||||
var codes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var entry in archive.Entries.Where(entry => IsWordTextXml(entry.FullName)))
|
||||
{
|
||||
using var stream = entry.Open();
|
||||
var document = global::System.Xml.Linq.XDocument.Load(stream);
|
||||
foreach (var paragraph in document.Descendants(WordNamespace + "p"))
|
||||
{
|
||||
var text = string.Concat(paragraph.Descendants(WordNamespace + "t").Select(node => node.Value));
|
||||
foreach (global::System.Text.RegularExpressions.Match match in PlaceholderRegex.Matches(text))
|
||||
codes.Add(match.Groups["code"].Value.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
return codes.OrderBy(code => code).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用任务变量快照替换 DOCX 中的占位符,支持占位符跨多个 Word 文本节点。
|
||||
/// </summary>
|
||||
public static byte[] Render(byte[] docx, IReadOnlyDictionary<string, string> values)
|
||||
{
|
||||
using var input = new MemoryStream(docx);
|
||||
using var archive = new global::System.IO.Compression.ZipArchive(
|
||||
input,
|
||||
global::System.IO.Compression.ZipArchiveMode.Read);
|
||||
using var output = new MemoryStream();
|
||||
using var target = new global::System.IO.Compression.ZipArchive(
|
||||
output,
|
||||
global::System.IO.Compression.ZipArchiveMode.Create,
|
||||
true);
|
||||
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
var next = target.CreateEntry(
|
||||
entry.FullName,
|
||||
global::System.IO.Compression.CompressionLevel.Optimal);
|
||||
using var sourceStream = entry.Open();
|
||||
using var targetStream = next.Open();
|
||||
if (!IsWordTextXml(entry.FullName))
|
||||
{
|
||||
sourceStream.CopyTo(targetStream);
|
||||
continue;
|
||||
}
|
||||
|
||||
var document = global::System.Xml.Linq.XDocument.Load(sourceStream);
|
||||
foreach (var paragraph in document.Descendants(WordNamespace + "p"))
|
||||
ReplaceParagraphVariables(paragraph, values);
|
||||
document.Save(targetStream, global::System.Xml.Linq.SaveOptions.DisableFormatting);
|
||||
}
|
||||
|
||||
target.Dispose();
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
private static void ReplaceParagraphVariables(
|
||||
global::System.Xml.Linq.XElement paragraph,
|
||||
IReadOnlyDictionary<string, string> values)
|
||||
{
|
||||
var replacements = 0;
|
||||
while (replacements < 1000)
|
||||
{
|
||||
var nodes = paragraph.Descendants(WordNamespace + "t").ToList();
|
||||
var text = string.Concat(nodes.Select(node => node.Value));
|
||||
var match = PlaceholderRegex.Matches(text)
|
||||
.Cast<global::System.Text.RegularExpressions.Match>()
|
||||
.LastOrDefault(item => values.ContainsKey(item.Groups["code"].Value.Trim()));
|
||||
if (match == null)
|
||||
return;
|
||||
|
||||
var code = match.Groups["code"].Value.Trim();
|
||||
ReplaceRange(nodes, match.Index, match.Length, values[code] ?? string.Empty);
|
||||
replacements++;
|
||||
}
|
||||
|
||||
throw Oops.Bah("合同模板单个段落中的变量数量异常");
|
||||
}
|
||||
|
||||
private static void ReplaceRange(
|
||||
List<global::System.Xml.Linq.XElement> nodes,
|
||||
int start,
|
||||
int length,
|
||||
string replacement)
|
||||
{
|
||||
var end = start + length;
|
||||
var offset = 0;
|
||||
global::System.Xml.Linq.XElement startNode = null;
|
||||
global::System.Xml.Linq.XElement endNode = null;
|
||||
var startIndex = 0;
|
||||
var endIndex = 0;
|
||||
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
var nextOffset = offset + node.Value.Length;
|
||||
if (startNode == null && start >= offset && start < nextOffset)
|
||||
{
|
||||
startNode = node;
|
||||
startIndex = start - offset;
|
||||
}
|
||||
|
||||
if (end > offset && end <= nextOffset)
|
||||
{
|
||||
endNode = node;
|
||||
endIndex = end - offset;
|
||||
break;
|
||||
}
|
||||
|
||||
offset = nextOffset;
|
||||
}
|
||||
|
||||
if (startNode == null || endNode == null)
|
||||
return;
|
||||
|
||||
if (ReferenceEquals(startNode, endNode))
|
||||
{
|
||||
startNode.Value = startNode.Value[..startIndex]
|
||||
+ replacement
|
||||
+ startNode.Value[endIndex..];
|
||||
return;
|
||||
}
|
||||
|
||||
var startNodePosition = nodes.IndexOf(startNode);
|
||||
var endNodePosition = nodes.IndexOf(endNode);
|
||||
startNode.Value = startNode.Value[..startIndex] + replacement;
|
||||
for (var index = startNodePosition + 1; index < endNodePosition; index++)
|
||||
nodes[index].Value = string.Empty;
|
||||
endNode.Value = endNode.Value[endIndex..];
|
||||
}
|
||||
|
||||
private static bool IsWordTextXml(string entryName)
|
||||
{
|
||||
return entryName == "word/document.xml"
|
||||
|| ((entryName.StartsWith("word/header", StringComparison.OrdinalIgnoreCase)
|
||||
|| entryName.StartsWith("word/footer", StringComparison.OrdinalIgnoreCase))
|
||||
&& entryName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 合同适用范围分页查询参数。
|
||||
/// </summary>
|
||||
public class OnboardingContractScopePageInput : BasePageInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 入职业务公司 Id。
|
||||
/// </summary>
|
||||
public long? CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 岗位 Id;为空时表示公司级配置。
|
||||
/// </summary>
|
||||
public long? PositionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合同模板 Id。
|
||||
/// </summary>
|
||||
public long? TemplateId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增合同适用范围参数。
|
||||
/// </summary>
|
||||
public class OnboardingContractScopeAddInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 入职业务公司 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "公司Id不能为空")]
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 岗位 Id;为空时表示公司级配置。
|
||||
/// </summary>
|
||||
public long? PositionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合同模板 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "模板Id不能为空")]
|
||||
public long TemplateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合同模板版本 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "模板版本Id不能为空")]
|
||||
public long TemplateVersionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否必填、必传或必签。
|
||||
/// </summary>
|
||||
public bool Required { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 显示排序码。
|
||||
/// </summary>
|
||||
public int? SortCode { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑合同适用范围参数。
|
||||
/// </summary>
|
||||
public class OnboardingContractScopeEditInput : OnboardingContractScopeAddInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 业务数据 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "适用范围Id不能为空")]
|
||||
public long Id { get; set; }
|
||||
}
|
||||
+15
-25
@@ -1,7 +1,7 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 定义合同模板、版本、变量和适用范围业务能力。
|
||||
/// 定义合同模板、DOCX 版本和自动识别变量的管理能力。
|
||||
/// </summary>
|
||||
public interface IOnboardingContractTemplateService : ITransient
|
||||
{
|
||||
@@ -9,59 +9,49 @@ public interface IOnboardingContractTemplateService : ITransient
|
||||
/// 分页查询合同模板主档案。
|
||||
/// </summary>
|
||||
Task<SqlSugarPagedList<ObContractTemplate>> Page(OnboardingContractTemplatePageInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 查询合同模板详情。
|
||||
/// </summary>
|
||||
Task<ObContractTemplate> Detail(BaseIdInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 新增合同模板主档案。
|
||||
/// </summary>
|
||||
Task Add(OnboardingContractTemplateAddInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 编辑合同模板主档案。
|
||||
/// </summary>
|
||||
Task Edit(OnboardingContractTemplateEditInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 删除未被版本、适用范围或任务合同引用的模板。
|
||||
/// 删除未被版本、入职模板或任务合同引用的模板。
|
||||
/// </summary>
|
||||
Task Delete(BaseIdListInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询指定合同模板的版本。
|
||||
/// </summary>
|
||||
Task<SqlSugarPagedList<ObContractTemplateVersion>> VersionPage(OnboardingContractVersionPageInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 新增合同模板 DOCX 版本。
|
||||
/// 新增合同模板 DOCX 版本并自动识别变量。
|
||||
/// </summary>
|
||||
Task AddVersion(OnboardingContractVersionAddInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 启用或停用合同模板版本。
|
||||
/// </summary>
|
||||
Task SetVersionStatus(OnboardingContractVersionStatusInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询指定模板版本的变量定义。
|
||||
/// 分页查询指定模板版本自动识别出的变量。
|
||||
/// </summary>
|
||||
Task<SqlSugarPagedList<ObTemplateVariable>> VariablePage(OnboardingTemplateVariablePageInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 为草稿模板版本新增变量。
|
||||
/// </summary>
|
||||
Task AddVariable(OnboardingTemplateVariableAddInput input);
|
||||
/// <summary>
|
||||
/// 编辑草稿模板版本中的变量。
|
||||
/// 编辑并确认草稿模板版本中的变量定义。
|
||||
/// </summary>
|
||||
Task EditVariable(OnboardingTemplateVariableEditInput input);
|
||||
/// <summary>
|
||||
/// 删除草稿模板版本中的变量。
|
||||
/// </summary>
|
||||
Task DeleteVariable(BaseIdListInput input); Task<SqlSugarPagedList<ObContractScope>> ScopePage(OnboardingContractScopePageInput input);
|
||||
/// <summary>
|
||||
/// 新增公司或岗位的合同适用范围。
|
||||
/// </summary>
|
||||
Task AddScope(OnboardingContractScopeAddInput input);
|
||||
/// <summary>
|
||||
/// 编辑合同适用范围。
|
||||
/// </summary>
|
||||
Task EditScope(OnboardingContractScopeEditInput input);
|
||||
/// <summary>
|
||||
/// 删除合同适用范围。
|
||||
/// </summary>
|
||||
Task DeleteScope(BaseIdListInput input);}
|
||||
}
|
||||
+95
-123
@@ -5,6 +5,12 @@ namespace SimpleAdmin.Application;
|
||||
/// </summary>
|
||||
public class OnboardingContractTemplateService : DbRepository<ObContractTemplate>, IOnboardingContractTemplateService
|
||||
{
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
public OnboardingContractTemplateService(IFileService fileService)
|
||||
{
|
||||
_fileService = fileService;
|
||||
}
|
||||
/// <summary>
|
||||
/// 分页查询合同模板主档案。
|
||||
/// </summary>
|
||||
@@ -64,7 +70,12 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
var hasScopes = await Context.Queryable<ObContractScope>()
|
||||
.AnyAsync(it => input.Ids.Contains(it.TemplateId));
|
||||
if (hasScopes)
|
||||
throw Oops.Bah("模板已配置适用范围,不能删除");
|
||||
throw Oops.Bah("模板已配置旧版适用范围,不能删除");
|
||||
|
||||
var hasOnboardingTemplates = await Context.Queryable<ObOnboardingTemplateContract>()
|
||||
.AnyAsync(it => input.Ids.Contains(it.ContractTemplateId));
|
||||
if (hasOnboardingTemplates)
|
||||
throw Oops.Bah("模板已被入职模板引用,不能删除");
|
||||
|
||||
var hasContracts = await Context.Queryable<ObTaskContract>()
|
||||
.AnyAsync(it => input.Ids.Contains(it.TemplateId));
|
||||
@@ -92,18 +103,45 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
public async Task AddVersion(OnboardingContractVersionAddInput input)
|
||||
{
|
||||
await Detail(new BaseIdInput { Id = input.TemplateId });
|
||||
|
||||
var file = await Context.Queryable<SysFile>().InSingleAsync(input.SourceDocxFileId)
|
||||
?? throw Oops.Bah("DOCX源文件不存在");
|
||||
?? throw Oops.Bah("DOCX 源文件不存在");
|
||||
if (!string.Equals(file.Suffix, "docx", StringComparison.OrdinalIgnoreCase))
|
||||
throw Oops.Bah("合同模板仅支持DOCX文件");
|
||||
throw Oops.Bah("合同模板仅支持 DOCX 文件");
|
||||
|
||||
var versionExists = await Context.Queryable<ObContractTemplateVersion>()
|
||||
.AnyAsync(it => it.TemplateId == input.TemplateId && it.VersionNo == input.VersionNo);
|
||||
.AnyAsync(item => item.TemplateId == input.TemplateId && item.VersionNo == input.VersionNo);
|
||||
if (versionExists)
|
||||
throw Oops.Bah("该模板版本号已存在");
|
||||
|
||||
await Context.Insertable(input.Adapt<ObContractTemplateVersion>()).ExecuteCommandAsync();
|
||||
var sourceResult = await _fileService.Download(new BaseIdInput { Id = file.Id })
|
||||
?? throw Oops.Bah("无法读取 DOCX 源文件");
|
||||
await using var sourceStream = sourceResult.FileStream;
|
||||
using var sourceBytes = new MemoryStream();
|
||||
await sourceStream.CopyToAsync(sourceBytes);
|
||||
var bytes = sourceBytes.ToArray();
|
||||
var variableCodes = DocxTemplateProcessor.ExtractVariableCodes(bytes);
|
||||
var versionId = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId();
|
||||
var version = input.Adapt<ObContractTemplateVersion>();
|
||||
version.Id = versionId;
|
||||
version.VersionStatus = ContractTemplateVersionStatus.Draft;
|
||||
version.FileHash = string.IsNullOrWhiteSpace(input.FileHash)
|
||||
? Convert.ToHexString(global::System.Security.Cryptography.SHA256.HashData(bytes))
|
||||
: input.FileHash;
|
||||
var variables = variableCodes.Select((code, index) => BuildDetectedVariable(versionId, code, index)).ToList();
|
||||
|
||||
await Context.Ado.BeginTranAsync();
|
||||
try
|
||||
{
|
||||
await Context.Insertable(version).ExecuteCommandAsync();
|
||||
if (variables.Count > 0)
|
||||
await Context.Insertable(variables).ExecuteCommandAsync();
|
||||
await Context.Ado.CommitTranAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Ado.RollbackTranAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -122,10 +160,11 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
|
||||
if (input.VersionStatus == ContractTemplateVersionStatus.Enabled)
|
||||
{
|
||||
var hasVariables = await Context.Queryable<ObTemplateVariable>()
|
||||
.AnyAsync(it => it.TemplateVersionId == input.Id);
|
||||
if (!hasVariables)
|
||||
throw Oops.Bah("请先配置模板变量后再启用");
|
||||
var hasUnresolvedVariables = await Context.Queryable<ObTemplateVariable>()
|
||||
.AnyAsync(item => item.TemplateVersionId == input.Id
|
||||
&& item.VariableType == ContractVariableType.Unresolved);
|
||||
if (hasUnresolvedVariables)
|
||||
throw Oops.Bah("仍有未识别来源的合同变量,请逐项确认后再启用");
|
||||
}
|
||||
|
||||
version.VersionStatus = input.VersionStatus;
|
||||
@@ -143,19 +182,8 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
.OrderBy(it => it.VariableCode)
|
||||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为草稿模板版本新增变量。
|
||||
/// </summary>
|
||||
public async Task AddVariable(OnboardingTemplateVariableAddInput input)
|
||||
{
|
||||
await EnsureVersionDraft(input.TemplateVersionId);
|
||||
await EnsureVariableUnique(input.TemplateVersionId, input.VariableCode, 0);
|
||||
await Context.Insertable(input.Adapt<ObTemplateVariable>()).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑草稿模板版本中的变量。
|
||||
/// 编辑草稿模板版本中自动识别出的变量,不允许修改 DOCX 占位符编码。
|
||||
/// </summary>
|
||||
public async Task EditVariable(OnboardingTemplateVariableEditInput input)
|
||||
{
|
||||
@@ -163,77 +191,61 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
?? throw Oops.Bah("模板变量不存在");
|
||||
if (variable.TemplateVersionId != input.TemplateVersionId)
|
||||
throw Oops.Bah("不允许变更变量所属版本");
|
||||
if (!string.Equals(variable.VariableCode, input.VariableCode, StringComparison.Ordinal))
|
||||
throw Oops.Bah("DOCX 占位符编码由系统识别,不能修改");
|
||||
|
||||
await EnsureVersionDraft(input.TemplateVersionId);
|
||||
await EnsureVariableUnique(input.TemplateVersionId, input.VariableCode, input.Id);
|
||||
await Context.Updateable(input.Adapt<ObTemplateVariable>()).ExecuteCommandAsync();
|
||||
var entity = input.Adapt<ObTemplateVariable>();
|
||||
entity.VariableCode = variable.VariableCode;
|
||||
await Context.Updateable(entity).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除草稿模板版本中的变量。
|
||||
/// </summary>
|
||||
public async Task DeleteVariable(BaseIdListInput input)
|
||||
private static ObTemplateVariable BuildDetectedVariable(long versionId, string code, int index)
|
||||
{
|
||||
if (input.Ids.Count == 0)
|
||||
return;
|
||||
var separatorIndex = code.IndexOf('.');
|
||||
var prefix = separatorIndex > 0 ? code[..separatorIndex].ToLowerInvariant() : string.Empty;
|
||||
var sourceField = separatorIndex > 0 ? code[(separatorIndex + 1)..] : code;
|
||||
var variableType = prefix switch
|
||||
{
|
||||
"employee" => ContractVariableType.Employee,
|
||||
"company" => ContractVariableType.Company,
|
||||
"hr" => ContractVariableType.Contract,
|
||||
"system" => ContractVariableType.System,
|
||||
_ => ContractVariableType.Unresolved
|
||||
};
|
||||
|
||||
var versionIds = await Context.Queryable<ObTemplateVariable>()
|
||||
.Where(it => input.Ids.Contains(it.Id))
|
||||
.Select(it => it.TemplateVersionId)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var versionId in versionIds.Distinct())
|
||||
await EnsureVersionDraft(versionId);
|
||||
|
||||
await Context.Deleteable<ObTemplateVariable>().In(input.Ids).ExecuteCommandAsync();
|
||||
return new ObTemplateVariable
|
||||
{
|
||||
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
||||
TemplateVersionId = versionId,
|
||||
VariableCode = code,
|
||||
VariableName = sourceField,
|
||||
VariableType = variableType,
|
||||
DataType = InferDataType(sourceField),
|
||||
EmployeeField = variableType is ContractVariableType.Employee or ContractVariableType.Company
|
||||
? sourceField
|
||||
: null,
|
||||
Required = true,
|
||||
HrEditable = variableType == ContractVariableType.Contract,
|
||||
SortCode = (index + 1) * 10,
|
||||
Remark = variableType == ContractVariableType.Unresolved ? "导入时未识别变量来源,请管理员确认" : null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询合同适用范围。
|
||||
/// </summary>
|
||||
public async Task<SqlSugarPagedList<ObContractScope>> ScopePage(OnboardingContractScopePageInput input)
|
||||
private static string InferDataType(string sourceField)
|
||||
{
|
||||
return await Context.Queryable<ObContractScope>()
|
||||
.WhereIF(input.CompanyId.HasValue, it => it.CompanyId == input.CompanyId.Value)
|
||||
.WhereIF(input.PositionId.HasValue, it => it.PositionId == input.PositionId.Value)
|
||||
.WhereIF(input.TemplateId.HasValue, it => it.TemplateId == input.TemplateId.Value)
|
||||
.OrderBy(it => it.SortCode)
|
||||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||||
}
|
||||
var normalized = sourceField.ToLowerInvariant();
|
||||
if (normalized.Contains("date") || normalized.EndsWith("at"))
|
||||
return "date";
|
||||
if (normalized.Contains("salary")
|
||||
|| normalized.Contains("amount")
|
||||
|| normalized.Contains("wage")
|
||||
|| normalized.Contains("copies"))
|
||||
{
|
||||
return "decimal";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增公司或岗位的合同适用范围。
|
||||
/// </summary>
|
||||
public async Task AddScope(OnboardingContractScopeAddInput input)
|
||||
{
|
||||
await EnsureScopeValid(input.CompanyId, input.PositionId, input.TemplateId, input.TemplateVersionId);
|
||||
await EnsureScopeUnique(input.CompanyId, input.PositionId, input.TemplateVersionId, 0);
|
||||
await Context.Insertable(input.Adapt<ObContractScope>()).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑合同适用范围。
|
||||
/// </summary>
|
||||
public async Task EditScope(OnboardingContractScopeEditInput input)
|
||||
{
|
||||
var scopeExists = await Context.Queryable<ObContractScope>().AnyAsync(it => it.Id == input.Id);
|
||||
if (!scopeExists)
|
||||
throw Oops.Bah("合同适用范围不存在");
|
||||
|
||||
await EnsureScopeValid(input.CompanyId, input.PositionId, input.TemplateId, input.TemplateVersionId);
|
||||
await EnsureScopeUnique(input.CompanyId, input.PositionId, input.TemplateVersionId, input.Id);
|
||||
await Context.Updateable(input.Adapt<ObContractScope>()).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除合同适用范围。
|
||||
/// </summary>
|
||||
public async Task DeleteScope(BaseIdListInput input)
|
||||
{
|
||||
if (input.Ids.Count == 0)
|
||||
return;
|
||||
|
||||
await Context.Deleteable<ObContractScope>().In(input.Ids).ExecuteCommandAsync();
|
||||
return "string";
|
||||
}
|
||||
|
||||
private async Task EnsureCodeUnique(string code, long id)
|
||||
@@ -251,44 +263,4 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
if (version.VersionStatus != ContractTemplateVersionStatus.Draft)
|
||||
throw Oops.Bah("只有草稿版本可维护变量");
|
||||
}
|
||||
|
||||
private async Task EnsureVariableUnique(long versionId, string code, long id)
|
||||
{
|
||||
var exists = await Context.Queryable<ObTemplateVariable>()
|
||||
.AnyAsync(it => it.TemplateVersionId == versionId && it.VariableCode == code && it.Id != id);
|
||||
if (exists)
|
||||
throw Oops.Bah("该版本变量编码已存在");
|
||||
}
|
||||
|
||||
private async Task EnsureScopeValid(long companyId, long? positionId, long templateId, long versionId)
|
||||
{
|
||||
var companyExists = await Context.Queryable<ObCompany>()
|
||||
.AnyAsync(it => it.Id == companyId && it.Status == "enable");
|
||||
if (!companyExists)
|
||||
throw Oops.Bah("所属公司不存在或未启用");
|
||||
|
||||
if (positionId.HasValue)
|
||||
{
|
||||
var positionExists = await Context.Queryable<ObPosition>()
|
||||
.AnyAsync(it => it.Id == positionId.Value && it.CompanyId == companyId && it.Status == "enable");
|
||||
if (!positionExists)
|
||||
throw Oops.Bah("岗位不存在、未启用或不属于该公司");
|
||||
}
|
||||
|
||||
var version = await Context.Queryable<ObContractTemplateVersion>().InSingleAsync(versionId)
|
||||
?? throw Oops.Bah("合同版本不存在");
|
||||
if (version.TemplateId != templateId || version.VersionStatus != ContractTemplateVersionStatus.Enabled)
|
||||
throw Oops.Bah("只能配置该模板的已启用合同版本");
|
||||
}
|
||||
|
||||
private async Task EnsureScopeUnique(long companyId, long? positionId, long versionId, long id)
|
||||
{
|
||||
var exists = await Context.Queryable<ObContractScope>()
|
||||
.AnyAsync(it => it.CompanyId == companyId
|
||||
&& it.PositionId == positionId
|
||||
&& it.TemplateVersionId == versionId
|
||||
&& it.Id != id);
|
||||
if (exists)
|
||||
throw Oops.Bah("该公司、岗位和合同版本的适用范围已存在");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user