赛事判断

This commit is contained in:
马梓彭 2026-04-16 11:08:23 +08:00
parent 2ee874554d
commit fc1d85449d
3 changed files with 76 additions and 44 deletions

View File

@ -382,6 +382,7 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
private async Task<DocumentData> ExtractDocumentData(Stream docStream)
{
var documentData = new DocumentData();
var processedTexts = new HashSet<string>(); // 记录已处理的文本,避免重复
using var wordprocessingDocument = WordprocessingDocument.Open(docStream, false);
var body = wordprocessingDocument.MainDocumentPart?.Document?.Body;
@ -392,11 +393,19 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
if (body != null)
{
// 首先处理 Drawing 对象(浮于文字上方的文本框)
ExtractDrawingObjects(body, documentData);
ExtractDrawingObjects(body, documentData, processedTexts);
// 提取段落(包含位置信息)
// 提取段落(包含位置信息),但跳过包含Drawing对象的段落(避免重复)
foreach (var paragraph in body.Elements<Paragraph>())
{
// 检查段落是否包含 Drawing 对象,如果是则跳过(已在 ExtractDrawingObjects 中处理)
var hasDrawing = paragraph.Descendants<DocumentFormat.OpenXml.Wordprocessing.Drawing>().Any();
if (hasDrawing)
{
_logger.LogDebug("跳过包含 Drawing 对象的段落");
continue;
}
var paragraphText = paragraph.InnerText?.Trim();
if (!string.IsNullOrEmpty(paragraphText))
{
@ -414,6 +423,13 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
continue;
}
// 检查是否已经处理过相同文本,避免重复
if (processedTexts.Contains(cleanedText))
{
_logger.LogDebug("跳过重复文本: {Text}", cleanedText);
continue;
}
var paragraphData = new ParagraphData
{
Text = cleanedText,
@ -431,6 +447,7 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
cleanedText, paragraphData.PositionX, paragraphData.PositionY);
documentData.Paragraphs.Add(paragraphData);
processedTexts.Add(cleanedText);
}
}
@ -658,7 +675,7 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
/// <summary>
/// 提取 Drawing 对象(浮于文字上方的文本框)
/// </summary>
private void ExtractDrawingObjects(Body body, DocumentData documentData)
private void ExtractDrawingObjects(Body body, DocumentData documentData, HashSet<string> processedTexts = null)
{
try
{
@ -670,7 +687,7 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
foreach (var drawing in drawings)
{
ExtractTextFromDrawing(drawing, documentData);
ExtractTextFromDrawing(drawing, documentData, processedTexts);
}
}
}
@ -683,7 +700,7 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
/// <summary>
/// 从 Drawing 对象中提取文本和位置信息
/// </summary>
private void ExtractTextFromDrawing(DocumentFormat.OpenXml.Wordprocessing.Drawing drawing, DocumentData documentData)
private void ExtractTextFromDrawing(DocumentFormat.OpenXml.Wordprocessing.Drawing drawing, DocumentData documentData, HashSet<string> processedTexts = null)
{
try
{
@ -714,6 +731,13 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
continue;
}
// 检查是否已经处理过相同文本,避免重复
if (processedTexts != null && processedTexts.Contains(cleanedText))
{
_logger.LogDebug("跳过 Drawing 对象中的重复文本: {Text}", cleanedText);
continue;
}
var paragraphData = new ParagraphData
{
Text = cleanedText,
@ -731,6 +755,10 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
cleanedText, paragraphData.PositionX, paragraphData.PositionY);
documentData.Paragraphs.Add(paragraphData);
if (processedTexts != null)
{
processedTexts.Add(cleanedText);
}
}
}
}
@ -1033,7 +1061,7 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
.FitArea();
}
// 使用 Column 来放置所有内容
// 使用 Column 来放置所有内容,但使用 Translate 而非 Padding 定位
page.Content().Column(column =>
{
// 根据段落信息进行定位(使用从Word文档提取的位置信息)
@ -1046,14 +1074,14 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
_logger.LogDebug("处理段落: Text={Text}, X={X}, Y={Y}, FontSize={FontSize}, Bold={IsBold}",
paragraph.Text, paragraph.PositionX, paragraph.PositionY, fontSize, isBold);
// 直接使用从Word文档提取的位置信息
float x = (float)(paragraph.PositionX ?? 50);
float y = (float)(paragraph.PositionY ?? 50);
// 直接使用从Word文档提取的位置信息,并增加110个单位进行调整
float x = (float)(paragraph.PositionX ?? 50) + 90;
float y = (float)(paragraph.PositionY ?? 50) + 50;
_logger.LogDebug("最终位置 - X={X}, Y={Y}", x, y);
// 使用 Padding 来定位文本
var textElement = column.Item().PaddingLeft(x).PaddingTop(y).Text(paragraph.Text)
// 使用 Translate 而非 Padding 来定位,避免流式布局累加导致分页
var textElement = column.Item().TranslateX(x).TranslateY(y).Text(paragraph.Text)
.FontSize((float)fontSize)
.FontColor(QuestPDF.Helpers.Colors.Black)
.AlignLeft();

View File

@ -1131,41 +1131,45 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
// 判断是否为团队项目
var isTeamProject = project.IsTeamProject();
// 计算分配的人数(去重)
var totalAssignedMembers = input.GroupMembers
.Where(g => assignedProjectGroups.Select(pg => pg.Id).Contains(g.GroupId))
.SelectMany(g => g.MemberIds)
.Distinct()
.Count();
if (project.CompletionCount > 0)
// 对每个分配的组别进行独立验证
foreach (var assignedGroup in assignedProjectGroups)
{
if (totalAssignedMembers < project.CompletionCount)
// 找到该组别的队员分配
var groupMemberAssignment = input.GroupMembers.FirstOrDefault(gm => gm.GroupId == assignedGroup.Id);
if (groupMemberAssignment == null) continue;
// 计算该组别的分配人数(不跨组别,也不跨项目去重)
var assignedCount = groupMemberAssignment.MemberIds?.Distinct().Count() ?? 0;
if (project.CompletionCount > 0)
{
result.HasError = true;
result.Errors.Add(new ProjectCompletionError
if (assignedCount < project.CompletionCount)
{
ProjectId = eventProject.Id,
ProjectName = project.Name ?? "未知项目",
ErrorType = "Insufficient",
ErrorMessage = isTeamProject
? $"项目\"{project.Name}\"要求完成人数为{project.CompletionCount}人,当前只分配了{totalAssignedMembers}人"
: $"项目\"{project.Name}\"要求完成人数为{project.CompletionCount}人,当前只分配了{totalAssignedMembers}人"
});
}
else if (totalAssignedMembers >= project.CompletionCount)
{
// 只要达到或超过完成人数就提示超限(特别是个人项目)
result.HasError = true;
result.Errors.Add(new ProjectCompletionError
result.HasError = true;
result.Errors.Add(new ProjectCompletionError
{
ProjectId = eventProject.Id,
ProjectName = project.Name ?? "未知项目",
ErrorType = "Insufficient",
ErrorMessage = isTeamProject
? $"项目\"{project.Name}\"的组别\"{assignedGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前只分配了{assignedCount}人,请补充队员"
: $"项目\"{project.Name}\"的组别\"{assignedGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前只分配了{assignedCount}人,请补充队员"
});
}
else if (assignedCount > project.CompletionCount)
{
ProjectId = eventProject.Id,
ProjectName = project.Name ?? "未知项目",
ErrorType = "Excessive",
ErrorMessage = isTeamProject
? $"项目\"{project.Name}\"要求完成人数为{project.CompletionCount}人,当前分配了{totalAssignedMembers}人,人数超额"
: $"项目\"{project.Name}\"要求完成人数为{project.CompletionCount}人,当前分配了{totalAssignedMembers}人,人数超额"
});
// 只要达到或超过完成人数就提示超限(特别是个人项目)
result.HasError = true;
result.Errors.Add(new ProjectCompletionError
{
ProjectId = eventProject.Id,
ProjectName = project.Name ?? "未知项目",
ErrorType = "Excessive",
ErrorMessage = isTeamProject
? $"项目\"{project.Name}\"的组别\"{assignedGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前分配了{assignedCount}人,人数超额,请移除多余队员"
: $"项目\"{project.Name}\"的组别\"{assignedGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前分配了{assignedCount}人,人数超额,请移除多余队员"
});
}
}
}
}

View File

@ -852,12 +852,12 @@
清理段落文本,移除不需要的内容
</summary>
</member>
<member name="M:YD_Event.Application.Certificate.CertificateGenerateService.ExtractDrawingObjects(DocumentFormat.OpenXml.Wordprocessing.Body,YD_Event.Application.Certificate.DocumentData)">
<member name="M:YD_Event.Application.Certificate.CertificateGenerateService.ExtractDrawingObjects(DocumentFormat.OpenXml.Wordprocessing.Body,YD_Event.Application.Certificate.DocumentData,System.Collections.Generic.HashSet{System.String})">
<summary>
提取 Drawing 对象(浮于文字上方的文本框)
</summary>
</member>
<member name="M:YD_Event.Application.Certificate.CertificateGenerateService.ExtractTextFromDrawing(DocumentFormat.OpenXml.Wordprocessing.Drawing,YD_Event.Application.Certificate.DocumentData)">
<member name="M:YD_Event.Application.Certificate.CertificateGenerateService.ExtractTextFromDrawing(DocumentFormat.OpenXml.Wordprocessing.Drawing,YD_Event.Application.Certificate.DocumentData,System.Collections.Generic.HashSet{System.String})">
<summary>
从 Drawing 对象中提取文本和位置信息
</summary>