제자리 갱신 시 E_SctMst.SctObjTxt 갱신 누락 수정
ShtCneYon='Y' 서식은 기존 이름 행을 continue 로 건너뛰기만 해서, 라벨 문구를 고쳐도 항목사전(SctObjTxt)이 옛 문구로 남았다. 이 값은 외부인터페이스·환자전달사항· 알람이 리터럴로 매칭하는 조회 키라 조용한 데이터 불일치가 된다. 레거시 bzSaveSheetDesignNControlInfo.vb:378 과 동일하게 SctObjTxt 만 덮어쓴다: - SctObjTyp/SctParObj/SctObjSeq/SctObjID 는 갱신하지 않음(SctKey 보존 의미론) - 삭제된 컨트롤의 잔존 행도 지우지 않음(레거시 동일) - SctKey 기준 UPDATE — 동명 중복 오염 데이터에서 다중 행 덮어쓰기 방지 - Spread AsTemplate 항목 행은 ObjName 이 항목 텍스트라 컨트롤 이름과 분리해 매칭. 레거시의 매 저장 중복 INSERT 버그는 복제하지 않는다(런타임이 개수를 세지 않음). --db-save-smoke 에 문구 변경 검증 추가 — 무변경 재저장으로는 이 경로를 증명할 수 없으므로 실제로 Text 를 바꿔 SctObjTxt 반영·SctKey 보존을 확인하고 원복한다. 검증: P163(ShtCneYon=Y) 2회 연속 통과(행 6/6 안정, SctKey 보존, 원복 확인), S999(버저닝) diff 0 + 이전 버전 SdgDelYon='Y', 전수 왕복 1,271건 diff 0/예외 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
16c07f48dc
commit
20e982faae
@@ -13,13 +13,16 @@ public sealed record DesignVersionInfo(decimal SdgKey, string UpdDtm, string Upd
|
||||
/// <summary>
|
||||
/// 레거시 테이블(E_ShtMst/E_SdgMst/E_SctMst) 기반 서식 저장소 — 레거시 디자이너와 동일 저장 의미론.
|
||||
/// 저장 흐름(bzSaveSheetDesignNControlInfo 이식, 단일 트랜잭션):
|
||||
/// ShtCneYon='Y'+활성행 존재 → 제자리 갱신(E_SctMst 는 신규 이름만 추가),
|
||||
/// ShtCneYon='Y'+활성행 존재 → 제자리 갱신(E_SctMst 는 신규 이름만 추가 + 기존 행 SctObjTxt 갱신),
|
||||
/// 그 외 → 기존 활성행 SdgDelYon='Y' 후 신규 행 INSERT + E_SctMst 전체 재생성.
|
||||
/// 채번: 전용 시퀀스 NEXTVAL 우선, 부재 시 MAX+1 폴백(트랜잭션 내 — [200]SheetMe 검증 방식).
|
||||
/// </summary>
|
||||
public sealed class OracleLegacyFormStore
|
||||
{
|
||||
#region Member Fields
|
||||
/// <summary>Spread AsTemplate 항목 행의 SctObjTyp — 이 행은 ObjName 이 항목 텍스트라 컨트롤 이름과 구분해야 한다</summary>
|
||||
private const string SpreadItemType = "ItemOfSpreadAsTemplate";
|
||||
|
||||
private readonly string connectionString;
|
||||
private readonly LegacyXmlSerializer serializer = new();
|
||||
#endregion
|
||||
@@ -324,29 +327,64 @@ public sealed class OracleLegacyFormStore
|
||||
{
|
||||
var rows = SctMstXmlWalker.Walk(mergedXml);
|
||||
|
||||
HashSet<string>? existingNames = null;
|
||||
// 제자리 갱신일 때만 기존 행을 읽는다. 컨트롤 행은 SctKey 로 갱신해야 하므로 키까지 가져오고,
|
||||
// Spread AsTemplate 항목 행은 ObjName 이 항목 텍스트라 컨트롤 이름과 섞이면 안 되므로 따로 담는다.
|
||||
Dictionary<string, decimal>? existingControls = null;
|
||||
HashSet<string>? existingSpreadItems = null;
|
||||
if (updateInPlace)
|
||||
{
|
||||
existingNames = new HashSet<string>(StringComparer.Ordinal);
|
||||
existingControls = new Dictionary<string, decimal>(StringComparer.Ordinal);
|
||||
existingSpreadItems = new HashSet<string>(StringComparer.Ordinal);
|
||||
using var query = connection.CreateCommand();
|
||||
query.Transaction = transaction;
|
||||
query.BindByName = true;
|
||||
query.CommandText = "SELECT SctObjNam FROM E_SctMst WHERE SctSdgKey = :k";
|
||||
query.CommandText =
|
||||
"SELECT SctKey, SctObjNam, NVL(SctObjTyp,' ') FROM E_SctMst WHERE SctSdgKey = :k ORDER BY SctKey";
|
||||
query.Parameters.Add(new OracleParameter("k", sdgKey));
|
||||
using var reader = query.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
existingNames.Add(reader.GetString(0));
|
||||
var key = reader.GetDecimal(0);
|
||||
var name = reader.GetString(1);
|
||||
var type = reader.GetString(2).Trim();
|
||||
if (string.Equals(type, SpreadItemType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
existingSpreadItems.Add(name);
|
||||
}
|
||||
else if (!existingControls.ContainsKey(name))
|
||||
{
|
||||
// 동명 행이 이미 중복된 오염 데이터에서는 레거시 Select(...)(0) 과 동일하게 최초 1건만 채택
|
||||
existingControls[name] = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
// 제자리 갱신: 기존 이름은 유지(레거시 — SctKey 보존), 신규 이름만 추가
|
||||
if (existingNames is not null && existingNames.Contains(row.ObjName))
|
||||
if (updateInPlace)
|
||||
{
|
||||
continue;
|
||||
if (string.Equals(row.ObjType, SpreadItemType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 레거시(bzSaveSheetDesignNControlInfo.vb:355-370)는 존재 검사 없이 매 저장마다 항목 행을
|
||||
// 중복 INSERT 한다. 런타임은 개수를 세지 않으므로 그 버그는 복제하지 않고 행 수를 안정시킨다.
|
||||
if (existingSpreadItems!.Contains(row.ObjName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (existingControls!.TryGetValue(row.ObjName, out var existingKey))
|
||||
{
|
||||
// 레거시 bzSaveSheetDesignNControlInfo.vb:378 — 기존 행은 SctObjTxt 만 덮어쓴다.
|
||||
// SctObjTyp/SctParObj/SctObjSeq/SctObjID 는 의도적으로 갱신하지 않는다(SctKey 보존 의미론).
|
||||
// 삭제된 컨트롤의 잔존 행도 레거시와 동일하게 지우지 않는다.
|
||||
NonQuery(connection, transaction,
|
||||
"UPDATE E_SctMst SET SctObjTxt = :txt WHERE SctKey = :k",
|
||||
new OracleParameter("txt", row.ObjText.Length == 0 ? (object)DBNull.Value : row.ObjText),
|
||||
new OracleParameter("k", existingKey));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var sctKey = NextVal(connection, transaction, "E_SCTMST_SCTKEY",
|
||||
"SELECT NVL(MAX(SctKey),0)+1 FROM E_SctMst");
|
||||
NonQuery(connection, transaction,
|
||||
@@ -413,14 +451,15 @@ public sealed class OracleLegacyFormStore
|
||||
return command.ExecuteScalar();
|
||||
}
|
||||
|
||||
private static void NonQuery(OracleConnection connection, OracleTransaction transaction, string sql, params OracleParameter[] parameters)
|
||||
/// <summary>DML 실행 — 영향 행 수 반환(호출부가 무시해도 무방)</summary>
|
||||
private static int NonQuery(OracleConnection connection, OracleTransaction transaction, string sql, params OracleParameter[] parameters)
|
||||
{
|
||||
using var command = connection.CreateCommand();
|
||||
command.Transaction = transaction;
|
||||
command.BindByName = true;
|
||||
command.CommandText = sql;
|
||||
command.Parameters.AddRange(parameters);
|
||||
command.ExecuteNonQuery();
|
||||
return command.ExecuteNonQuery();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user