修复交互花样计算规则

This commit is contained in:
马梓彭 2026-10-10 11:07:22 +08:00
parent 1136799fe5
commit 96e128a0d5
5 changed files with 60 additions and 40 deletions

View File

@ -297,21 +297,31 @@ public class EventScoreCalculationService : ITransient
IReadOnlyDictionary<long, List<EventRegistrationPerson>> members, int sourceType, long? correctionId, long? operatorUserId)
{
if (outcome.Aggregate == null) return false;
if (!members.TryGetValue(entry.Id, out var athletes) || athletes.Count == 0) return false;
members.TryGetValue(entry.Id, out var athletes);
athletes ??= new List<EventRegistrationPerson>();
var isImportedSnapshot = athletes.Count == 0 && entry.SourceType == 1;
if (athletes.Count == 0 && !isImportedSnapshot) return false;
var aggregate = outcome.Aggregate;
var isTeam = ctx.Scope == ScoreScope.Team;
var combinedNames = string.Join(", ", athletes.Select(x => x.Name));
var displayName = isTeam
? (string.IsNullOrWhiteSpace(entry.TeamName) ? combinedNames : entry.TeamName!)
: athletes[0].Name;
var displayName = isImportedSnapshot
? DisplayParticipantName(entry, ctx.Scope)
: isTeam
? (string.IsNullOrWhiteSpace(entry.TeamName) ? combinedNames : entry.TeamName!)
: athletes[0].Name;
var scoreText = aggregate.IsDisqualified ? "取消资格" : ctx.Config.Format(aggregate.TotalScore);
var calculationJson = EntryResultCalculationService.BuildCalculationJson(aggregate, ctx.Config);
var now = DateTime.Now;
var userId = operatorUserId ?? 0;
var personIds = athletes.Select(x => x.Id).ToList();
if (!isTeam)
// 秩序册允许导入未走报名流程的出场。此时以 0 作为无报名关联的明确哨兵值,
// 成绩仍以 SessionEntryId 作为业务归属,供记分台确认、排名和投屏使用。
var recipients = isImportedSnapshot
? new List<ResultRecipient> { new(0, 0, entry.GroupName) }
: athletes.Select(x => new ResultRecipient(x.Id, x.RegistrationId, x.GroupName)).ToList();
var personIds = recipients.Select(x => x.PersonId).ToList();
if (!isTeam && !isImportedSnapshot)
{
// 修复旧逻辑已经造成的串成绩:个人项一个出场号只允许归属当前报名人。
// 重算时作废同出场号下误写给其他人的成绩,并保留版本留痕。
@ -325,25 +335,27 @@ public class EventScoreCalculationService : ITransient
var existing = await _resultRepository.GetListAsync(x => x.EventId == ctx.Session.EventId
&& 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 registrationIds = recipients.Where(x => x.RegistrationId > 0).Select(x => x.RegistrationId).Distinct().ToList();
var registrations = registrationIds.Count == 0
? new List<EventRegistration>()
: await _registrationRepository.GetListAsync(x => registrationIds.Contains(x.Id));
var certificationByRegistration = registrations.GroupBy(x => x.Id)
.ToDictionary(x => x.Key, x => x.First().MerchantCertificationId);
var toInsert = new List<EventResult>();
var toUpdate = new List<EventResult>();
var versions = new List<EventResultVersion>();
foreach (var athlete in athletes)
foreach (var recipient in recipients)
{
var result = existing.FirstOrDefault(x => x.RegistrationPersonId == athlete.Id);
var result = existing.FirstOrDefault(x => x.RegistrationPersonId == recipient.PersonId);
var isNew = result == null;
result ??= new EventResult
{
Id = _idGenerator.NextId(),
EventId = ctx.Session.EventId,
ProjectId = ctx.Project.Id,
RegistrationPersonId = athlete.Id,
RegistrationId = athlete.RegistrationId,
RegistrationPersonId = recipient.PersonId,
RegistrationId = recipient.RegistrationId,
ParticipationRecordId = entry.ParticipationRecordId ?? 0,
CreatedUserId = userId,
CreatedTime = now,
@ -351,9 +363,9 @@ public class EventScoreCalculationService : ITransient
};
result.Status = AvailabilityStatus.Enable;
result.MerchantCertificationId = certificationByRegistration.TryGetValue(athlete.RegistrationId, out var certId) ? certId : null;
result.MerchantCertificationId = certificationByRegistration.TryGetValue(recipient.RegistrationId, out var certId) ? certId : null;
result.ParticipantName = displayName;
result.GroupName = string.IsNullOrWhiteSpace(entry.GroupName) ? athlete.GroupName : entry.GroupName;
result.GroupName = string.IsNullOrWhiteSpace(entry.GroupName) ? recipient.GroupName : entry.GroupName;
result.ProjectTypeName = ctx.ProjectTypeName;
result.SessionId = ctx.Session.Id;
result.SessionEntryId = entry.Id;
@ -410,6 +422,8 @@ public class EventScoreCalculationService : ITransient
return true;
}
private sealed record ResultRecipient(long PersonId, long RegistrationId, string GroupName);
/// <summary>
/// 作废某个出场已写入的成绩,成绩不再对外展示也不参与排名
/// </summary>

View File

@ -217,7 +217,7 @@ public static class EntryResultCalculationService
deduction = Math.Max(0m, 1m - penalty);
}
// 总分计算前,D/P/Q 保留两位、M 保留三位;规定缺失量保留小数,失误次数在扣分流程中取整。
// 总分计算前,D/P/Q 保留两位、M 保留三位;规定缺失量按角色聚合配置取整,失误次数在扣分流程中取整。
result.RawFactors = new EntryRawFactors(difficulty, performance, required, deduction);
difficulty = Math.Round(difficulty, 2, MidpointRounding.AwayFromZero);
performance = Math.Round(performance, 2, MidpointRounding.AwayFromZero);

View File

@ -73,10 +73,8 @@ public static class ScoreRuleConfigService
{
if (IsIndividualFamily(config.RuleType))
{
// 个人花样的每类规定元素缺失数先按同类型裁判取平均,再四舍五入为整数参与 Q 计算。
// 同步花样保留半个规定元素的精度。
role.AggregateDecimals = string.Equals(config.RuleType, "FREESTYLE_SINGLE",
StringComparison.OrdinalIgnoreCase) ? 0 : null;
// 单张评分单允许半个规定元素;同类型裁判取平均后四舍五入为整数参与 Q 计算。
role.AggregateDecimals = 0;
}
if (config.RuleType.StartsWith("FREESTYLE_WHEEL", StringComparison.OrdinalIgnoreCase))
role.FactorWeight = 0.025m;

View File

@ -752,7 +752,7 @@ BEGIN TRY
/* ====================================================================================
[4/9] 插入数据 · 初始化种子(统一“存在则更新、缺失才插入”的幂等写法,可重复执行)
顺序:4.1 计分规则(8条) -> 规则Id映射 -> 4.2 规则明细(478条) -> 4.3 角色 -> 4.4 账号管理按钮 -> 4.5 角色菜单授权
顺序:4.1 计分规则(8条) -> 规则Id映射 -> 4.2 规则明细(483条,其中481条启用) -> 4.3 角色 -> 4.4 账号管理按钮 -> 4.5 角色菜单授权
==================================================================================== */
PRINT N'[4/9] 插入数据:初始化计分规则、规则明细、角色与菜单';
/* 4.1 八条计分规则(固定Id:3000规定赛/3100个人花样/3200两人同步花样/3300四人同步花样/3400两人车轮跳花样/3500三人交互绳花样/3600四人交互绳花样/3700表演赛):先按 RuleCode 更新,再插入缺失,Id冲突则报错 */
@ -789,7 +789,7 @@ BEGIN TRY
/* 规则“源Id->实际Id”映射:兼容目标库 Id 被占用时按 RuleCode 对齐,供规则明细写入外键 ScoreRuleId */
CREATE TABLE #RuleMap(SourceId BIGINT PRIMARY KEY,TargetId BIGINT NOT NULL);
INSERT #RuleMap SELECT s.SourceId,r.Id FROM #RuleSeed s JOIN dbo.ScoreRule r ON r.RuleCode=s.RuleCode AND r.DeleteMark=0;
/* 4.2 计分规则评分项明细(共462条;Id段 30xx规定赛/31xx个人花样/32xx两人同步花样/33xx四人同步花样/34xx两人车轮跳花样/35xx三人交互绳花样/36xx四人交互绳花样/37xx表演赛):同样先更新后插入 */
/* 4.2 计分规则评分项明细(483 条种子,其中 481 条启用;Id段 30xx规定赛/31xx个人花样/32xx两人同步花样/33xx四人同步花样/34xx两人车轮跳花样/35xx三人交互绳花样/36xx四人交互绳花样/37xx表演赛):同样先更新后插入 */
CREATE TABLE #ItemSeed(Id BIGINT PRIMARY KEY,SourceRuleId BIGINT,JudgeRoleType NVARCHAR(16),ItemCode NVARCHAR(64),ItemName NVARCHAR(128),ItemType INT,ParentItemCode NVARCHAR(64),UnitValue DECIMAL(18,4),MinValue DECIMAL(18,4),MaxValue DECIMAL(18,4),StepValue DECIMAL(18,4),DefaultValue DECIMAL(18,4),CalcMode INT,ScoreDimension INT,IsRequired BIT,ExtensionJson NVARCHAR(2000),Sort INT,DeleteMark BIT,CreatedUserId BIGINT,CreatedTime DATETIME2(3));
INSERT #ItemSeed VALUES (3001,3000,N'CJ',N'CJ_ARRANGE',N'过渡连接、成套创编、场地运用',6,NULL,1,0,10,1,0,1,2,1,N'{"inputMode":"score","fineTuneStep":0.5}',1,0,1000,N'2026-08-19T00:00:00'),
(3002,3000,N'CJ',N'CJ_EFFECT',N'现场表现效果',6,NULL,1,0,10,1,0,1,2,1,N'{"inputMode":"score","fineTuneStep":0.5}',2,0,1000,N'2026-08-19T00:00:00'),
@ -928,7 +928,7 @@ BEGIN TRY
(3250,3200,N'DP',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3251,3200,N'DP',N'REQUIRED_N2_MINUS',N'2级及以下规定元素数',2,NULL,0.5,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',12,0,1000,N'2026-08-19T00:00:00'),
(3252,3200,N'DP',N'REQUIRED_MISSING',N'规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',13,0,1000,N'2026-08-19T00:00:00'),
(3253,3200,N'T',N'REQ_INTERACTION',N'互动配合',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',53,0,1000,N'2026-08-19T00:00:00'),
(3253,3200,N'T',N'REQ_INTERACTION',N'互动配合',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',53,1,1000,N'2026-08-19T00:00:00'),
(3254,3200,N'DM',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3255,3200,N'T',N'DQ_DISCIPLINE',N'违反赛场纪律',5,NULL,NULL,NULL,NULL,NULL,NULL,2,0,0,NULL,55,0,1000,N'2026-08-19T00:00:00'),
(3256,3200,N'T',N'DQ_MEMBER_COUNT',N'参赛人员不符合规定(不足或超过)',5,NULL,NULL,NULL,NULL,NULL,NULL,2,0,0,NULL,56,0,1000,N'2026-08-19T00:00:00'),
@ -943,7 +943,9 @@ BEGIN TRY
(3265,3200,N'DR',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3266,3200,N'DR',N'REQUIRED_N2_MINUS',N'2级及以下规定元素数',2,NULL,0.5,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',12,0,1000,N'2026-08-19T00:00:00'),
(3267,3200,N'DR',N'REQUIRED_MISSING',N'规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',13,0,1000,N'2026-08-19T00:00:00'),
(3268,3200,N'T',N'REQUIRED_MISSING',N'互动配合规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',54,0,1000,N'2026-08-19T00:00:00'),
(3268,3200,N'T',N'REQUIRED_MISSING',N'互动配合规定元素缺失(个)',2,NULL,NULL,0,4,0.5,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',55,0,1000,N'2026-08-19T00:00:00'),
(3269,3200,N'T',N'REQ_INTERACTION_N3_PLUS',N'互动配合(3级及以上)',2,NULL,1,0,50,1,0,0,3,1,N'{"inputMode":"count","unitLabel":"个"}',53,0,1000,N'2026-08-19T00:00:00'),
(3270,3200,N'T',N'REQ_INTERACTION_N2_MINUS',N'互动配合(2级及以下)',2,NULL,0.5,0,50,1,0,0,3,1,N'{"inputMode":"count","unitLabel":"个"}',54,0,1000,N'2026-08-19T00:00:00'),
(3301,3300,N'DP',N'DIFF_LEVEL_0',N'0 级',1,NULL,0.0,0,50,1,0,0,1,0,N'{"inputMode": "count", "unitLabel": "个"}',1,0,1000,N'2026-08-19T00:00:00'),
(3302,3300,N'DP',N'DIFF_LEVEL_0_5',N'0.5 级',1,NULL,0.12,0,50,1,0,0,1,0,N'{"inputMode": "count", "unitLabel": "个"}',2,0,1000,N'2026-08-19T00:00:00'),
(3303,3300,N'DP',N'DIFF_LEVEL_1',N'1 级',1,NULL,0.15,0,50,1,0,0,1,0,N'{"inputMode": "count", "unitLabel": "个"}',3,0,1000,N'2026-08-19T00:00:00'),
@ -996,7 +998,7 @@ BEGIN TRY
(3350,3300,N'DP',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3351,3300,N'DP',N'REQUIRED_N2_MINUS',N'2级及以下规定元素数',2,NULL,0.5,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',12,0,1000,N'2026-08-19T00:00:00'),
(3352,3300,N'DP',N'REQUIRED_MISSING',N'规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',13,0,1000,N'2026-08-19T00:00:00'),
(3353,3300,N'T',N'REQ_INTERACTION',N'互动配合',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',53,0,1000,N'2026-08-19T00:00:00'),
(3353,3300,N'T',N'REQ_INTERACTION',N'互动配合',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',53,1,1000,N'2026-08-19T00:00:00'),
(3354,3300,N'DM',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3355,3300,N'T',N'DQ_DISCIPLINE',N'违反赛场纪律',5,NULL,NULL,NULL,NULL,NULL,NULL,2,0,0,NULL,55,0,1000,N'2026-08-19T00:00:00'),
(3356,3300,N'T',N'DQ_MEMBER_COUNT',N'参赛人员不符合规定(不足或超过)',5,NULL,NULL,NULL,NULL,NULL,NULL,2,0,0,NULL,56,0,1000,N'2026-08-19T00:00:00'),
@ -1011,7 +1013,9 @@ BEGIN TRY
(3365,3300,N'DR',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3366,3300,N'DR',N'REQUIRED_N2_MINUS',N'2级及以下规定元素数',2,NULL,0.5,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',12,0,1000,N'2026-08-19T00:00:00'),
(3367,3300,N'DR',N'REQUIRED_MISSING',N'规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',13,0,1000,N'2026-08-19T00:00:00'),
(3368,3300,N'T',N'REQUIRED_MISSING',N'互动配合规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',54,0,1000,N'2026-08-19T00:00:00'),
(3368,3300,N'T',N'REQUIRED_MISSING',N'互动配合规定元素缺失(个)',2,NULL,NULL,0,4,0.5,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',55,0,1000,N'2026-08-19T00:00:00'),
(3369,3300,N'T',N'REQ_INTERACTION_N3_PLUS',N'互动配合(3级及以上)',2,NULL,1,0,50,1,0,0,3,1,N'{"inputMode":"count","unitLabel":"个"}',53,0,1000,N'2026-08-19T00:00:00'),
(3370,3300,N'T',N'REQ_INTERACTION_N2_MINUS',N'互动配合(2级及以下)',2,NULL,0.5,0,50,1,0,0,3,1,N'{"inputMode":"count","unitLabel":"个"}',54,0,1000,N'2026-08-19T00:00:00'),
(3401,3400,N'DA',N'DIFF_LEVEL_0',N'0 级',1,NULL,0,0,50,1,0,0,1,0,N'{"inputMode":"count","unitLabel":"个","subjectCode":"A"}',1,0,1000,N'2026-08-19T00:00:00'),
(3402,3400,N'DA',N'DIFF_LEVEL_0_5',N'0.5 级',1,NULL,0.12,0,50,1,0,0,1,0,N'{"inputMode":"count","unitLabel":"个","subjectCode":"A"}',2,0,1000,N'2026-08-19T00:00:00'),
(3403,3400,N'DA',N'DIFF_LEVEL_1',N'1 级',1,NULL,0.15,0,50,1,0,0,1,0,N'{"inputMode":"count","unitLabel":"个","subjectCode":"A"}',3,0,1000,N'2026-08-19T00:00:00'),
@ -1273,17 +1277,17 @@ BEGIN TRY
UPDATE i SET ItemName=s.ItemName,ItemType=s.ItemType,ParentItemCode=s.ParentItemCode,UnitValue=s.UnitValue,MinValue=s.MinValue,
MaxValue=s.MaxValue,StepValue=s.StepValue,DefaultValue=s.DefaultValue,CalcMode=s.CalcMode,ScoreDimension=s.ScoreDimension,
IsRequired=s.IsRequired,ExtensionJson=s.ExtensionJson,Sort=s.Sort,DeleteMark=0,UpdatedUserId=1000,UpdatedTime=SYSUTCDATETIME()
IsRequired=s.IsRequired,ExtensionJson=s.ExtensionJson,Sort=s.Sort,DeleteMark=s.DeleteMark,UpdatedUserId=1000,UpdatedTime=SYSUTCDATETIME()
FROM dbo.ScoreRuleItem i JOIN #RuleMap m ON m.TargetId=i.ScoreRuleId
JOIN #ItemSeed s ON s.SourceRuleId=m.SourceId AND s.JudgeRoleType=i.JudgeRoleType AND s.ItemCode=i.ItemCode
WHERE EXISTS(SELECT i.ItemName,i.ItemType,i.ParentItemCode,i.UnitValue,i.MinValue,i.MaxValue,i.StepValue,i.DefaultValue,i.CalcMode,i.ScoreDimension,i.IsRequired,i.ExtensionJson,i.Sort,i.DeleteMark
EXCEPT SELECT s.ItemName,s.ItemType,s.ParentItemCode,s.UnitValue,s.MinValue,s.MaxValue,s.StepValue,s.DefaultValue,s.CalcMode,s.ScoreDimension,s.IsRequired,s.ExtensionJson,s.Sort,CONVERT(BIT,0));
EXCEPT SELECT s.ItemName,s.ItemType,s.ParentItemCode,s.UnitValue,s.MinValue,s.MaxValue,s.StepValue,s.DefaultValue,s.CalcMode,s.ScoreDimension,s.IsRequired,s.ExtensionJson,s.Sort,s.DeleteMark);
INSERT dbo.ScoreRuleItem(Id,ScoreRuleId,JudgeRoleType,ItemCode,ItemName,ItemType,ParentItemCode,UnitValue,MinValue,MaxValue,StepValue,DefaultValue,CalcMode,ScoreDimension,IsRequired,ExtensionJson,Sort,DeleteMark,CreatedUserId,CreatedTime)
SELECT s.Id,m.TargetId,s.JudgeRoleType,s.ItemCode,s.ItemName,s.ItemType,s.ParentItemCode,s.UnitValue,s.MinValue,s.MaxValue,s.StepValue,s.DefaultValue,s.CalcMode,s.ScoreDimension,s.IsRequired,s.ExtensionJson,s.Sort,s.DeleteMark,s.CreatedUserId,s.CreatedTime
FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId
WHERE NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=0)
WHERE NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode)
AND NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.Id=s.Id);
IF EXISTS(SELECT 1 FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId WHERE NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=0))
IF EXISTS(SELECT 1 FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId WHERE NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=s.DeleteMark))
THROW 51004,N'计分规则明细种子ID被占用且业务键不存在,无法安全初始化。',1;
/* 4.2.1 交互绳基础项目必须绑定交互绳规则,避免新编排场次继承到无评分项的旧规则。 */
@ -1479,7 +1483,7 @@ SELECT r.RuleCode,COUNT(i.Id) AS ItemCount FROM dbo.ScoreRule r LEFT JOIN dbo.Sc
(N'INDEX',N'二期索引',CASE WHEN (SELECT COUNT(*) FROM sys.indexes WHERE name IN(N'UX_Referee_Mobile_Active',N'UX_EventReferee_Event_Referee_Active',N'UX_EventVenue_Event_Code_Active',N'UX_EventSession_Event_No_Active',N'UX_EventJudgeScoreSheet_Idempotency',N'UX_EventResultVersion_Result_Version'))=6 THEN 1 ELSE 0 END,N'6个关键索引'),
(N'CONSTRAINT',N'二期外键',CASE WHEN (SELECT COUNT(*) FROM sys.foreign_keys WHERE name LIKE 'FK[_]%' AND parent_object_id IN(SELECT OBJECT_ID(N'dbo.'+QUOTENAME(Name)) FROM @ExpectedTables))>=20 THEN 1 ELSE 0 END,CONVERT(NVARCHAR(20),(SELECT COUNT(*) FROM sys.foreign_keys WHERE name LIKE 'FK[_]%' AND parent_object_id IN(SELECT OBJECT_ID(N'dbo.'+QUOTENAME(Name)) FROM @ExpectedTables)))),
(N'SEED',N'计分规则',CASE WHEN (SELECT COUNT(DISTINCT r.RuleCode) FROM dbo.ScoreRule r JOIN #RuleSeed s ON s.RuleCode=r.RuleCode WHERE r.DeleteMark=0)=8 THEN 1 ELSE 0 END,N'期望8条'),
(N'SEED',N'计分规则明细',CASE WHEN (SELECT COUNT(*) FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId JOIN dbo.ScoreRuleItem i ON i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=0)=478 THEN 1 ELSE 0 END,N'期望478条'),
(N'SEED',N'计分规则明细',CASE WHEN (SELECT COUNT(*) FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId JOIN dbo.ScoreRuleItem i ON i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=0 WHERE s.DeleteMark=0)=(SELECT COUNT(*) FROM #ItemSeed WHERE DeleteMark=0) THEN 1 ELSE 0 END,CONCAT(N'期望',(SELECT COUNT(*) FROM #ItemSeed WHERE DeleteMark=0),N'条启用评分项')),
(N'SEED',N'角色与菜单',CASE WHEN EXISTS(SELECT 1 FROM dbo.SysRole WHERE Id=1003 AND Code='scoringunit' AND DeleteMark=0)
AND (SELECT COUNT(*) FROM dbo.SysMenu WHERE Id IN(1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,2010) AND DeleteMark=0)=12
AND EXISTS(SELECT 1 FROM dbo.SysMenu WHERE Id=1080 AND Code='system:unit' AND ParentId=1070 AND DeleteMark=0)

View File

@ -47,7 +47,7 @@ BEGIN TRY
/* 源 Id 映射到目标库实际规则 Id。 */
CREATE TABLE #RuleMap(SourceId BIGINT PRIMARY KEY,TargetId BIGINT NOT NULL);
INSERT #RuleMap SELECT s.SourceId,r.Id FROM #RuleSeed s JOIN dbo.ScoreRule r ON r.RuleCode=s.RuleCode AND r.DeleteMark=0;
/* 2. 评分项(478 条,按规则、裁判角色和 ItemCode 同步)。 */
/* 2. 评分项(483 条种子,其中 481 条启用;按规则、裁判角色和 ItemCode 同步)。 */
CREATE TABLE #ItemSeed(Id BIGINT PRIMARY KEY,SourceRuleId BIGINT,JudgeRoleType NVARCHAR(16),ItemCode NVARCHAR(64),ItemName NVARCHAR(128),ItemType INT,ParentItemCode NVARCHAR(64),UnitValue DECIMAL(18,4),MinValue DECIMAL(18,4),MaxValue DECIMAL(18,4),StepValue DECIMAL(18,4),DefaultValue DECIMAL(18,4),CalcMode INT,ScoreDimension INT,IsRequired BIT,ExtensionJson NVARCHAR(2000),Sort INT,DeleteMark BIT,CreatedUserId BIGINT,CreatedTime DATETIME2(3));
INSERT #ItemSeed VALUES (3001,3000,N'CJ',N'CJ_ARRANGE',N'过渡连接、成套创编、场地运用',6,NULL,1,0,10,1,0,1,2,1,N'{"inputMode":"score","fineTuneStep":0.5}',1,0,1000,N'2026-08-19T00:00:00'),
(3002,3000,N'CJ',N'CJ_EFFECT',N'现场表现效果',6,NULL,1,0,10,1,0,1,2,1,N'{"inputMode":"score","fineTuneStep":0.5}',2,0,1000,N'2026-08-19T00:00:00'),
@ -186,7 +186,7 @@ BEGIN TRY
(3250,3200,N'DP',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3251,3200,N'DP',N'REQUIRED_N2_MINUS',N'2级及以下规定元素数',2,NULL,0.5,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',12,0,1000,N'2026-08-19T00:00:00'),
(3252,3200,N'DP',N'REQUIRED_MISSING',N'规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',13,0,1000,N'2026-08-19T00:00:00'),
(3253,3200,N'T',N'REQ_INTERACTION',N'互动配合',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',53,0,1000,N'2026-08-19T00:00:00'),
(3253,3200,N'T',N'REQ_INTERACTION',N'互动配合',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',53,1,1000,N'2026-08-19T00:00:00'),
(3254,3200,N'DM',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3255,3200,N'T',N'DQ_DISCIPLINE',N'违反赛场纪律',5,NULL,NULL,NULL,NULL,NULL,NULL,2,0,0,NULL,55,0,1000,N'2026-08-19T00:00:00'),
(3256,3200,N'T',N'DQ_MEMBER_COUNT',N'参赛人员不符合规定(不足或超过)',5,NULL,NULL,NULL,NULL,NULL,NULL,2,0,0,NULL,56,0,1000,N'2026-08-19T00:00:00'),
@ -201,7 +201,9 @@ BEGIN TRY
(3265,3200,N'DR',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3266,3200,N'DR',N'REQUIRED_N2_MINUS',N'2级及以下规定元素数',2,NULL,0.5,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',12,0,1000,N'2026-08-19T00:00:00'),
(3267,3200,N'DR',N'REQUIRED_MISSING',N'规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',13,0,1000,N'2026-08-19T00:00:00'),
(3268,3200,N'T',N'REQUIRED_MISSING',N'互动配合规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',54,0,1000,N'2026-08-19T00:00:00'),
(3268,3200,N'T',N'REQUIRED_MISSING',N'互动配合规定元素缺失(个)',2,NULL,NULL,0,4,0.5,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',55,0,1000,N'2026-08-19T00:00:00'),
(3269,3200,N'T',N'REQ_INTERACTION_N3_PLUS',N'互动配合(3级及以上)',2,NULL,1,0,50,1,0,0,3,1,N'{"inputMode":"count","unitLabel":"个"}',53,0,1000,N'2026-08-19T00:00:00'),
(3270,3200,N'T',N'REQ_INTERACTION_N2_MINUS',N'互动配合(2级及以下)',2,NULL,0.5,0,50,1,0,0,3,1,N'{"inputMode":"count","unitLabel":"个"}',54,0,1000,N'2026-08-19T00:00:00'),
(3301,3300,N'DP',N'DIFF_LEVEL_0',N'0 级',1,NULL,0.0,0,50,1,0,0,1,0,N'{"inputMode": "count", "unitLabel": "个"}',1,0,1000,N'2026-08-19T00:00:00'),
(3302,3300,N'DP',N'DIFF_LEVEL_0_5',N'0.5 级',1,NULL,0.12,0,50,1,0,0,1,0,N'{"inputMode": "count", "unitLabel": "个"}',2,0,1000,N'2026-08-19T00:00:00'),
(3303,3300,N'DP',N'DIFF_LEVEL_1',N'1 级',1,NULL,0.15,0,50,1,0,0,1,0,N'{"inputMode": "count", "unitLabel": "个"}',3,0,1000,N'2026-08-19T00:00:00'),
@ -254,7 +256,7 @@ BEGIN TRY
(3350,3300,N'DP',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3351,3300,N'DP',N'REQUIRED_N2_MINUS',N'2级及以下规定元素数',2,NULL,0.5,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',12,0,1000,N'2026-08-19T00:00:00'),
(3352,3300,N'DP',N'REQUIRED_MISSING',N'规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',13,0,1000,N'2026-08-19T00:00:00'),
(3353,3300,N'T',N'REQ_INTERACTION',N'互动配合',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',53,0,1000,N'2026-08-19T00:00:00'),
(3353,3300,N'T',N'REQ_INTERACTION',N'互动配合',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',53,1,1000,N'2026-08-19T00:00:00'),
(3354,3300,N'DM',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3355,3300,N'T',N'DQ_DISCIPLINE',N'违反赛场纪律',5,NULL,NULL,NULL,NULL,NULL,NULL,2,0,0,NULL,55,0,1000,N'2026-08-19T00:00:00'),
(3356,3300,N'T',N'DQ_MEMBER_COUNT',N'参赛人员不符合规定(不足或超过)',5,NULL,NULL,NULL,NULL,NULL,NULL,2,0,0,NULL,56,0,1000,N'2026-08-19T00:00:00'),
@ -269,7 +271,9 @@ BEGIN TRY
(3365,3300,N'DR',N'REQUIRED_N3_PLUS',N'3级及以上规定元素数',2,NULL,1,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',11,0,1000,N'2026-08-19T00:00:00'),
(3366,3300,N'DR',N'REQUIRED_N2_MINUS',N'2级及以下规定元素数',2,NULL,0.5,0,50,1,0,0,3,0,N'{"inputMode":"count","unitLabel":"个"}',12,0,1000,N'2026-08-19T00:00:00'),
(3367,3300,N'DR',N'REQUIRED_MISSING',N'规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',13,0,1000,N'2026-08-19T00:00:00'),
(3368,3300,N'T',N'REQUIRED_MISSING',N'互动配合规定元素缺失(个)',2,NULL,NULL,0,4,1,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',54,0,1000,N'2026-08-19T00:00:00'),
(3368,3300,N'T',N'REQUIRED_MISSING',N'互动配合规定元素缺失(个)',2,NULL,NULL,0,4,0.5,0,1,0,0,N'{"inputMode":"score","isAuto":true,"unitLabel":"个","requiredTarget":4.0}',55,0,1000,N'2026-08-19T00:00:00'),
(3369,3300,N'T',N'REQ_INTERACTION_N3_PLUS',N'互动配合(3级及以上)',2,NULL,1,0,50,1,0,0,3,1,N'{"inputMode":"count","unitLabel":"个"}',53,0,1000,N'2026-08-19T00:00:00'),
(3370,3300,N'T',N'REQ_INTERACTION_N2_MINUS',N'互动配合(2级及以下)',2,NULL,0.5,0,50,1,0,0,3,1,N'{"inputMode":"count","unitLabel":"个"}',54,0,1000,N'2026-08-19T00:00:00'),
(3401,3400,N'DA',N'DIFF_LEVEL_0',N'0 级',1,NULL,0,0,50,1,0,0,1,0,N'{"inputMode":"count","unitLabel":"个","subjectCode":"A"}',1,0,1000,N'2026-08-19T00:00:00'),
(3402,3400,N'DA',N'DIFF_LEVEL_0_5',N'0.5 级',1,NULL,0.12,0,50,1,0,0,1,0,N'{"inputMode":"count","unitLabel":"个","subjectCode":"A"}',2,0,1000,N'2026-08-19T00:00:00'),
(3403,3400,N'DA',N'DIFF_LEVEL_1',N'1 级',1,NULL,0.15,0,50,1,0,0,1,0,N'{"inputMode":"count","unitLabel":"个","subjectCode":"A"}',3,0,1000,N'2026-08-19T00:00:00'),
@ -531,17 +535,17 @@ BEGIN TRY
UPDATE i SET ItemName=s.ItemName,ItemType=s.ItemType,ParentItemCode=s.ParentItemCode,UnitValue=s.UnitValue,MinValue=s.MinValue,
MaxValue=s.MaxValue,StepValue=s.StepValue,DefaultValue=s.DefaultValue,CalcMode=s.CalcMode,ScoreDimension=s.ScoreDimension,
IsRequired=s.IsRequired,ExtensionJson=s.ExtensionJson,Sort=s.Sort,DeleteMark=0,UpdatedUserId=1000,UpdatedTime=SYSUTCDATETIME()
IsRequired=s.IsRequired,ExtensionJson=s.ExtensionJson,Sort=s.Sort,DeleteMark=s.DeleteMark,UpdatedUserId=1000,UpdatedTime=SYSUTCDATETIME()
FROM dbo.ScoreRuleItem i JOIN #RuleMap m ON m.TargetId=i.ScoreRuleId
JOIN #ItemSeed s ON s.SourceRuleId=m.SourceId AND s.JudgeRoleType=i.JudgeRoleType AND s.ItemCode=i.ItemCode
WHERE EXISTS(SELECT i.ItemName,i.ItemType,i.ParentItemCode,i.UnitValue,i.MinValue,i.MaxValue,i.StepValue,i.DefaultValue,i.CalcMode,i.ScoreDimension,i.IsRequired,i.ExtensionJson,i.Sort,i.DeleteMark
EXCEPT SELECT s.ItemName,s.ItemType,s.ParentItemCode,s.UnitValue,s.MinValue,s.MaxValue,s.StepValue,s.DefaultValue,s.CalcMode,s.ScoreDimension,s.IsRequired,s.ExtensionJson,s.Sort,CONVERT(BIT,0));
EXCEPT SELECT s.ItemName,s.ItemType,s.ParentItemCode,s.UnitValue,s.MinValue,s.MaxValue,s.StepValue,s.DefaultValue,s.CalcMode,s.ScoreDimension,s.IsRequired,s.ExtensionJson,s.Sort,s.DeleteMark);
INSERT dbo.ScoreRuleItem(Id,ScoreRuleId,JudgeRoleType,ItemCode,ItemName,ItemType,ParentItemCode,UnitValue,MinValue,MaxValue,StepValue,DefaultValue,CalcMode,ScoreDimension,IsRequired,ExtensionJson,Sort,DeleteMark,CreatedUserId,CreatedTime)
SELECT s.Id,m.TargetId,s.JudgeRoleType,s.ItemCode,s.ItemName,s.ItemType,s.ParentItemCode,s.UnitValue,s.MinValue,s.MaxValue,s.StepValue,s.DefaultValue,s.CalcMode,s.ScoreDimension,s.IsRequired,s.ExtensionJson,s.Sort,s.DeleteMark,s.CreatedUserId,s.CreatedTime
FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId
WHERE NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=0)
WHERE NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode)
AND NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.Id=s.Id);
IF EXISTS(SELECT 1 FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId WHERE NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=0))
IF EXISTS(SELECT 1 FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId WHERE NOT EXISTS(SELECT 1 FROM dbo.ScoreRuleItem i WHERE i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=s.DeleteMark))
THROW 51004,N'计分规则明细种子ID被占用且业务键不存在,无法安全初始化。',1;
/* 3. 交互绳项目绑定计分规则。 */
@ -637,7 +641,7 @@ BEGIN TRY
(N'INDEX',N'二期索引',CASE WHEN (SELECT COUNT(*) FROM sys.indexes WHERE name IN(N'UX_Referee_Mobile_Active',N'UX_EventReferee_Event_Referee_Active',N'UX_EventVenue_Event_Code_Active',N'UX_EventSession_Event_No_Active',N'UX_EventJudgeScoreSheet_Idempotency',N'UX_EventResultVersion_Result_Version'))=6 THEN 1 ELSE 0 END,N'6个关键索引'),
(N'CONSTRAINT',N'二期外键',CASE WHEN (SELECT COUNT(*) FROM sys.foreign_keys WHERE name LIKE 'FK[_]%' AND parent_object_id IN(SELECT OBJECT_ID(N'dbo.'+QUOTENAME(Name)) FROM @ExpectedTables))>=20 THEN 1 ELSE 0 END,CONVERT(NVARCHAR(20),(SELECT COUNT(*) FROM sys.foreign_keys WHERE name LIKE 'FK[_]%' AND parent_object_id IN(SELECT OBJECT_ID(N'dbo.'+QUOTENAME(Name)) FROM @ExpectedTables)))),
(N'SEED',N'计分规则',CASE WHEN (SELECT COUNT(DISTINCT r.RuleCode) FROM dbo.ScoreRule r JOIN #RuleSeed s ON s.RuleCode=r.RuleCode WHERE r.DeleteMark=0)=8 THEN 1 ELSE 0 END,N'期望8条'),
(N'SEED',N'计分规则明细',CASE WHEN (SELECT COUNT(*) FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId JOIN dbo.ScoreRuleItem i ON i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=0)=478 THEN 1 ELSE 0 END,N'期望478条'),
(N'SEED',N'计分规则明细',CASE WHEN (SELECT COUNT(*) FROM #ItemSeed s JOIN #RuleMap m ON m.SourceId=s.SourceRuleId JOIN dbo.ScoreRuleItem i ON i.ScoreRuleId=m.TargetId AND i.JudgeRoleType=s.JudgeRoleType AND i.ItemCode=s.ItemCode AND i.DeleteMark=0 WHERE s.DeleteMark=0)=(SELECT COUNT(*) FROM #ItemSeed WHERE DeleteMark=0) THEN 1 ELSE 0 END,CONCAT(N'期望',(SELECT COUNT(*) FROM #ItemSeed WHERE DeleteMark=0),N'条启用评分项')),
(N'SEED',N'角色与菜单',CASE WHEN EXISTS(SELECT 1 FROM dbo.SysRole WHERE Id=1003 AND Code='scoringunit' AND DeleteMark=0)
AND (SELECT COUNT(*) FROM dbo.SysMenu WHERE Id IN(1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,2010) AND DeleteMark=0)=12
AND EXISTS(SELECT 1 FROM dbo.SysMenu WHERE Id=1080 AND Code='system:unit' AND ParentId=1070 AND DeleteMark=0)