赛事判断

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

View File

@ -1131,41 +1131,45 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
// 判断是否为团队项目 // 判断是否为团队项目
var isTeamProject = project.IsTeamProject(); var isTeamProject = project.IsTeamProject();
// 计算分配的人数(去重) // 对每个分配的组别进行独立验证
var totalAssignedMembers = input.GroupMembers foreach (var assignedGroup in assignedProjectGroups)
.Where(g => assignedProjectGroups.Select(pg => pg.Id).Contains(g.GroupId))
.SelectMany(g => g.MemberIds)
.Distinct()
.Count();
if (project.CompletionCount > 0)
{ {
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; if (assignedCount < project.CompletionCount)
result.Errors.Add(new ProjectCompletionError
{ {
ProjectId = eventProject.Id, result.HasError = true;
ProjectName = project.Name ?? "未知项目", result.Errors.Add(new ProjectCompletionError
ErrorType = "Insufficient", {
ErrorMessage = isTeamProject ProjectId = eventProject.Id,
? $"项目\"{project.Name}\"要求完成人数为{project.CompletionCount}人,当前只分配了{totalAssignedMembers}人" ProjectName = project.Name ?? "未知项目",
: $"项目\"{project.Name}\"要求完成人数为{project.CompletionCount}人,当前只分配了{totalAssignedMembers}人" ErrorType = "Insufficient",
}); ErrorMessage = isTeamProject
} ? $"项目\"{project.Name}\"的组别\"{assignedGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前只分配了{assignedCount}人,请补充队员"
else if (totalAssignedMembers >= project.CompletionCount) : $"项目\"{project.Name}\"的组别\"{assignedGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前只分配了{assignedCount}人,请补充队员"
{ });
// 只要达到或超过完成人数就提示超限(特别是个人项目) }
result.HasError = true; else if (assignedCount > project.CompletionCount)
result.Errors.Add(new ProjectCompletionError
{ {
ProjectId = eventProject.Id, // 只要达到或超过完成人数就提示超限(特别是个人项目)
ProjectName = project.Name ?? "未知项目", result.HasError = true;
ErrorType = "Excessive", result.Errors.Add(new ProjectCompletionError
ErrorMessage = isTeamProject {
? $"项目\"{project.Name}\"要求完成人数为{project.CompletionCount}人,当前分配了{totalAssignedMembers}人,人数超额" ProjectId = eventProject.Id,
: $"项目\"{project.Name}\"要求完成人数为{project.CompletionCount}人,当前分配了{totalAssignedMembers}人,人数超额" 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> </summary>
</member> </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> <summary>
提取 Drawing 对象(浮于文字上方的文本框) 提取 Drawing 对象(浮于文字上方的文本框)
</summary> </summary>
</member> </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> <summary>
从 Drawing 对象中提取文本和位置信息 从 Drawing 对象中提取文本和位置信息
</summary> </summary>