update:接口修改
This commit is contained in:
parent
5114a6cb9f
commit
8ba5468c21
@ -295,8 +295,11 @@ public class EventScoreCalculationService : ITransient
|
||||
var userId = operatorUserId ?? 0;
|
||||
|
||||
var personIds = athletes.Select(x => x.Id).ToList();
|
||||
// 同一名运动员可能因重赛等原因在同一项目中产生多条出场记录,
|
||||
// 成绩必须以“出场 + 运动员”为业务键更新,不能覆盖其他出场的历史成绩。
|
||||
var existing = await _resultRepository.GetListAsync(x => x.EventId == ctx.Session.EventId
|
||||
&& x.ProjectId == ctx.Project.Id && personIds.Contains(x.RegistrationPersonId));
|
||||
&& x.ProjectId == ctx.Project.Id && x.SessionEntryId == entry.Id
|
||||
&& personIds.Contains(x.RegistrationPersonId));
|
||||
var registrationIds = athletes.Select(x => x.RegistrationId).Distinct().ToList();
|
||||
var registrations = await _registrationRepository.GetListAsync(x => registrationIds.Contains(x.Id));
|
||||
var certificationByRegistration = registrations.GroupBy(x => x.Id)
|
||||
|
||||
@ -728,6 +728,21 @@ public class JudgeScoringController : IDynamicApiController
|
||||
|| !signature.SequenceEqual(PngSignature))
|
||||
throw Oops.Bah("上传文件不是有效的PNG图片");
|
||||
stream.Position = 0;
|
||||
// 文件头可以伪造;在上传 OSS 前完整解码,拒绝截断或损坏的 PNG。
|
||||
// 限制解码后的像素数,避免小体积压缩文件造成过量内存分配。
|
||||
// 解码器会释放其输入;保留请求流供后续 OSS 上传使用。
|
||||
using (var decoderStream = new SkiaSharp.SKManagedStream(stream, false))
|
||||
using (var codec = SkiaSharp.SKCodec.Create(decoderStream))
|
||||
{
|
||||
if (codec == null || codec.EncodedFormat != SkiaSharp.SKEncodedImageFormat.Png
|
||||
|| codec.Info.Width <= 0 || codec.Info.Height <= 0
|
||||
|| (long)codec.Info.Width * codec.Info.Height > 16_777_216)
|
||||
throw Oops.Bah("画板PNG图片损坏或像素尺寸过大");
|
||||
using var bitmap = new SkiaSharp.SKBitmap(codec.Info);
|
||||
if (codec.GetPixels(bitmap.Info, bitmap.GetPixels()) != SkiaSharp.SKCodecResult.Success)
|
||||
throw Oops.Bah("画板PNG图片不完整或无法解码");
|
||||
}
|
||||
stream.Position = 0;
|
||||
var objectName = $"judge-score-annotations/{sheet.EventId}/{sheet.Id}/{key}.png";
|
||||
var url = await _fileService.UploadFileStreamAsync(objectName, stream);
|
||||
|
||||
|
||||
@ -382,7 +382,11 @@ public class WorkbenchController : IDynamicApiController
|
||||
.ToListAsync();
|
||||
|
||||
var pendingCorrectionSet = pendingCorrections.ToHashSet();
|
||||
var resultDict = results.ToDictionary(r => r.SessionEntryId!.Value);
|
||||
// 集体项目为每名队员留存同一出场成绩,记分台按出场展示一行。
|
||||
// 稳定选取一个代表记录,避免同队多名队员导致重复字典键。
|
||||
var resultDict = results.GroupBy(r => r.SessionEntryId!.Value)
|
||||
.ToDictionary(g => g.Key, g => g.OrderByDescending(r => r.ResultVersion)
|
||||
.ThenBy(r => r.Id).First());
|
||||
|
||||
return entries.Select(e =>
|
||||
{
|
||||
|
||||
36
二期需求/20260915_fix_team_result_unique_index.sql
Normal file
36
二期需求/20260915_fix_team_result_unique_index.sql
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
修复集体项目成绩写入:EventResult 会为队内每名运动员各保留一条成绩,
|
||||
因此活动成绩唯一键应为“出场记录 + 报名人员”,不能只约束出场记录。
|
||||
本脚本可重复执行。
|
||||
*/
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
SET ANSI_NULLS ON;
|
||||
SET ANSI_PADDING ON;
|
||||
SET ANSI_WARNINGS ON;
|
||||
SET ARITHABORT ON;
|
||||
SET CONCAT_NULL_YIELDS_NULL ON;
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
SET NUMERIC_ROUNDABORT OFF;
|
||||
|
||||
BEGIN TRY
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM sys.indexes
|
||||
WHERE object_id = OBJECT_ID(N'dbo.EventResult')
|
||||
AND name = N'UX_EventResult_SessionEntry_Active'
|
||||
)
|
||||
DROP INDEX [UX_EventResult_SessionEntry_Active] ON dbo.[EventResult];
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX [UX_EventResult_SessionEntry_Active]
|
||||
ON dbo.[EventResult]([SessionEntryId], [RegistrationPersonId])
|
||||
WHERE [SessionEntryId] IS NOT NULL AND [DeleteMark] = 0;
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
|
||||
THROW;
|
||||
END CATCH;
|
||||
@ -726,6 +726,13 @@ BEGIN TRY
|
||||
IF NOT EXISTS(SELECT 1 FROM sys.indexes WHERE object_id=OBJECT_ID(N'dbo.[EventJudgeScoreAttachment]') AND name=N'UX_EventJudgeScoreAttachment_Idempotency') CREATE UNIQUE NONCLUSTERED INDEX [UX_EventJudgeScoreAttachment_Idempotency] ON dbo.[EventJudgeScoreAttachment]([ScoreSheetId],[IdempotencyKey]);
|
||||
IF NOT EXISTS(SELECT 1 FROM sys.indexes WHERE object_id=OBJECT_ID(N'dbo.[EventJudgeScoreAttachment]') AND name=N'UX_EventJudgeScoreAttachment_Current') CREATE UNIQUE NONCLUSTERED INDEX [UX_EventJudgeScoreAttachment_Current] ON dbo.[EventJudgeScoreAttachment]([ScoreSheetId],[AttachmentType]) WHERE [IsCurrent]=1 AND [DeleteMark]=0;
|
||||
IF NOT EXISTS(SELECT 1 FROM sys.indexes WHERE object_id=OBJECT_ID(N'dbo.[EventJudgeScoreDetail]') AND name=N'IX_EventJudgeScoreDetail_Sheet') CREATE NONCLUSTERED INDEX [IX_EventJudgeScoreDetail_Sheet] ON dbo.[EventJudgeScoreDetail]([ScoreSheetId],[Sort]);
|
||||
/* 集体项目按队员分别保存成绩;旧版仅以 SessionEntryId 唯一会导致第二名队员写入失败。 */
|
||||
IF EXISTS(SELECT 1 FROM sys.indexes WHERE object_id=OBJECT_ID(N'dbo.[EventResult]') AND name=N'UX_EventResult_SessionEntry_Active'
|
||||
AND (INDEX_COL(N'dbo.EventResult',N'UX_EventResult_SessionEntry_Active',1)<>N'SessionEntryId'
|
||||
OR ISNULL(INDEX_COL(N'dbo.EventResult',N'UX_EventResult_SessionEntry_Active',2),N'')<>N'RegistrationPersonId'))
|
||||
DROP INDEX [UX_EventResult_SessionEntry_Active] ON dbo.[EventResult];
|
||||
IF NOT EXISTS(SELECT 1 FROM sys.indexes WHERE object_id=OBJECT_ID(N'dbo.[EventResult]') AND name=N'UX_EventResult_SessionEntry_Active')
|
||||
CREATE UNIQUE NONCLUSTERED INDEX [UX_EventResult_SessionEntry_Active] ON dbo.[EventResult]([SessionEntryId],[RegistrationPersonId]) WHERE [SessionEntryId] IS NOT NULL AND [DeleteMark]=0;
|
||||
IF NOT EXISTS(SELECT 1 FROM sys.indexes WHERE object_id=OBJECT_ID(N'dbo.[EventResultVersion]') AND name=N'UX_EventResultVersion_Result_Version') CREATE UNIQUE NONCLUSTERED INDEX [UX_EventResultVersion_Result_Version] ON dbo.[EventResultVersion]([EventResultId],[ResultVersion]) WHERE [DeleteMark]=0;
|
||||
IF NOT EXISTS(SELECT 1 FROM sys.indexes WHERE object_id=OBJECT_ID(N'dbo.[EventJudgeMessage]') AND name=N'IX_EventJudgeMessage_Receiver_Unread') CREATE NONCLUSTERED INDEX [IX_EventJudgeMessage_Receiver_Unread] ON dbo.[EventJudgeMessage]([ReceiverUserId],[IsRead],[CreatedTime]) WHERE [DeleteMark]=0;
|
||||
IF NOT EXISTS(SELECT 1 FROM sys.indexes WHERE object_id=OBJECT_ID(N'dbo.[EventResultScreenCast]') AND name=N'IX_EventResultScreenCast_Event_Time') CREATE NONCLUSTERED INDEX [IX_EventResultScreenCast_Event_Time] ON dbo.[EventResultScreenCast]([EventId],[CastTime]) WHERE [DeleteMark]=0;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user