수술 일정 6종을 옮긴다 — 100종 → 106종
S_OprInf(수술 접수 일정 — GetOprInfDT 의 E_OprInf 축 큰 조회와 다른 표) 를 OprComNum + OprStt='E' 로 거르고 OprKey 순으로 한 행: - ETC_수술일자_1_몇년/_1_몇년2자리/_2_몇월/_3_몇일 — DESC(마지막 수술, '수술실 요청' 주석 그대로), OprDte 자름 - OCM_수술일자_마지막수술 — DESC, "9999-99-99" 형식 - OCM_수술일자 — Else 갈래는 ASC(첫 수술). PURME(주사오더 O_OdrInf 조회)· BSGH·HIMCHAN_*·GJHNSS(DESC) 분기는 미이식 안내 — Else 로 뭉개면 그 병원에서 첫/마지막이 뒤바뀐 날짜가 조용히 찍힌다. ## 게이트 - dotnet test 338/338 · --edit-smoke 실패 0(이름 검사 106종) - --db-patient 전건 통과 · --db-render P062 md5 불변 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e065c5a645
commit
4dad271e9c
@@ -161,6 +161,15 @@ public static class PatientIdentity
|
||||
: new VitalStore(connection).One(context.ComNum, expr, pick, notNull, order);
|
||||
}
|
||||
|
||||
/// <summary>수술 일정(S_OprInf) 공급자 — 최신순 여부별 1회</summary>
|
||||
public static Func<bool, IReadOnlyDictionary<string, string>> ScheduledSurgerySourceFor(PatientContext context)
|
||||
{
|
||||
var connection = ConfigService.Current.ConnectionString;
|
||||
return latest => connection.Length == 0
|
||||
? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
: new PatientContextStore(connection).ScheduledSurgery(context.ComNum, latest);
|
||||
}
|
||||
|
||||
/// <summary>수술 행 공급자 — 상병과 같은 정책(필요할 때 1회, 실패는 던져 사유로)</summary>
|
||||
public static Func<IReadOnlyList<Dictionary<string, string>>> SurgerySourceFor(PatientContext context)
|
||||
{
|
||||
|
||||
@@ -72,7 +72,8 @@ public static class PatientSession
|
||||
PatientIdentity.DiagnosisSourceFor(picked),
|
||||
PatientIdentity.SurgerySourceFor(picked),
|
||||
PatientIdentity.ReviewDiagnosisSourceFor(picked),
|
||||
PatientIdentity.VitalSourceFor(picked))
|
||||
PatientIdentity.VitalSourceFor(picked),
|
||||
PatientIdentity.ScheduledSurgerySourceFor(picked))
|
||||
: session;
|
||||
return (tags, new MDataTableRunner(document, Variables()));
|
||||
}
|
||||
|
||||
@@ -190,6 +190,11 @@ public sealed class PatientTagResolver : ITagValueResolver
|
||||
|
||||
private readonly Dictionary<string, string> vitalCache = new(StringComparer.Ordinal);
|
||||
|
||||
/// <summary>수술 일정 공급자 — (최신순?) 별 1회 캐시</summary>
|
||||
private readonly Func<bool, IReadOnlyDictionary<string, string>>? scheduledSurgerySource;
|
||||
|
||||
private readonly Dictionary<bool, IReadOnlyDictionary<string, string>> scheduledSurgeryCache = new();
|
||||
|
||||
private readonly Dictionary<DiagnosisStore.Scope, IReadOnlyList<Dictionary<string, string>>> reviewCache = new();
|
||||
|
||||
public PatientTagResolver(PatientContext context, ITagValueResolver next,
|
||||
@@ -201,7 +206,8 @@ public sealed class PatientTagResolver : ITagValueResolver
|
||||
Func<DiagnosisStore.Scope, IReadOnlyList<Dictionary<string, string>>>? diagnosisSource = null,
|
||||
Func<IReadOnlyList<Dictionary<string, string>>>? surgerySource = null,
|
||||
Func<DiagnosisStore.Scope, IReadOnlyList<Dictionary<string, string>>>? reviewSource = null,
|
||||
Func<string, string, string[], string, string>? vitalSource = null)
|
||||
Func<string, string, string[], string, string>? vitalSource = null,
|
||||
Func<bool, IReadOnlyDictionary<string, string>>? scheduledSurgerySource = null)
|
||||
{
|
||||
this.context = context;
|
||||
this.next = next;
|
||||
@@ -215,6 +221,7 @@ public sealed class PatientTagResolver : ITagValueResolver
|
||||
this.surgerySource = surgerySource;
|
||||
this.reviewSource = reviewSource;
|
||||
this.vitalSource = vitalSource;
|
||||
this.scheduledSurgerySource = scheduledSurgerySource;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -254,6 +261,10 @@ public sealed class PatientTagResolver : ITagValueResolver
|
||||
{
|
||||
return vitalValue;
|
||||
}
|
||||
if (FromScheduledSurgery(tag) is { } scheduleValue)
|
||||
{
|
||||
return scheduleValue;
|
||||
}
|
||||
if (Computed.TryGetValue(tag, out var compute))
|
||||
{
|
||||
var made = compute(context);
|
||||
@@ -745,6 +756,66 @@ public sealed class PatientTagResolver : ITagValueResolver
|
||||
new[] { "EmrAdpTim" }, "EmrAdpTim DESC"),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 수술 일정(S_OprInf) 6종 — 해당 태그가 아니면 null.
|
||||
/// ETC_수술일자_* 와 마지막수술은 <b>DESC(마지막)</b>, OCM_수술일자는 Else 갈래가 ASC(첫).
|
||||
/// OCM_수술일자의 병원 분기(PURME 주사오더 / BSGH·HIMCHAN_*·GJHNSS DESC)는 미이식 안내.
|
||||
/// </summary>
|
||||
private TagValue? FromScheduledSurgery(string tag)
|
||||
{
|
||||
var latest = tag switch
|
||||
{
|
||||
"ETC_수술일자_1_몇년" or "ETC_수술일자_1_몇년2자리"
|
||||
or "ETC_수술일자_2_몇월" or "ETC_수술일자_3_몇일"
|
||||
or "OCM_수술일자_마지막수술" => true,
|
||||
"OCM_수술일자" => false,
|
||||
_ => (bool?)null,
|
||||
} is { } l ? l : (bool?)null;
|
||||
if (latest is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var hsp = UserSession.Current.HspCod;
|
||||
if (tag == "OCM_수술일자"
|
||||
&& (hsp is "PURME" or "BSGH" or "GJHNSS"
|
||||
|| hsp.StartsWith("HIMCHAN_", StringComparison.Ordinal)))
|
||||
{
|
||||
return new TagValue(false, $"이 병원({hsp})의 수술일자 분기는 아직 옮기지 않았습니다");
|
||||
}
|
||||
if (scheduledSurgerySource is null)
|
||||
{
|
||||
return new TagValue(false, "수술 일정 조회가 연결되지 않았습니다");
|
||||
}
|
||||
if (!scheduledSurgeryCache.TryGetValue(latest.Value, out var row))
|
||||
{
|
||||
try
|
||||
{
|
||||
row = scheduledSurgerySource(latest.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new TagValue(false, $"수술 일정 조회에 실패했습니다: {ex.GetType().Name}");
|
||||
}
|
||||
scheduledSurgeryCache[latest.Value] = row;
|
||||
}
|
||||
var oprDte = row.TryGetValue("OprDte", out var d) ? d.Trim() : string.Empty;
|
||||
if (row.Count == 0 || oprDte.Length == 0)
|
||||
{
|
||||
return new TagValue(false, "이 내원에 수술 일정이 없습니다");
|
||||
}
|
||||
var value = tag switch
|
||||
{
|
||||
"ETC_수술일자_1_몇년" => Head(oprDte, 4),
|
||||
"ETC_수술일자_1_몇년2자리" => oprDte.Length >= 4 ? oprDte.Substring(2, 2) : string.Empty,
|
||||
"ETC_수술일자_2_몇월" => oprDte.Length >= 6 ? oprDte.Substring(4, 2) : string.Empty,
|
||||
"ETC_수술일자_3_몇일" => oprDte.Length >= 8 ? oprDte.Substring(6, 2) : string.Empty,
|
||||
_ => Hyphenate(Head(oprDte, 8)),
|
||||
};
|
||||
return value.Length > 0
|
||||
? new TagValue(true, value)
|
||||
: new TagValue(false, "수술일자 값이 형식에 맞지 않습니다");
|
||||
}
|
||||
|
||||
private TagValue? FromVital(string tag)
|
||||
{
|
||||
// 머리둘레 두 종은 레거시 결함이다 — SELECT 는 EmdHc AS HC 인데 Item("BP") 를 읽어
|
||||
@@ -813,6 +884,8 @@ public sealed class PatientTagResolver : ITagValueResolver
|
||||
"OCM_맥박", "OCM_맥박_LAST", "OCM_호흡", "OCM_호흡_LAST", "OCM_SPO2", "OCM_SPO2_LAST",
|
||||
"OCM_혈압", "OCM_혈압_LAST", "OCM_혈압_BPS", "OCM_혈압_BPD", "OCM_VITAL접수일시_LAST",
|
||||
"OCM_머리둘레", "OCM_머리둘레_LAST",
|
||||
"OCM_수술일자", "OCM_수술일자_마지막수술", "ETC_수술일자_1_몇년",
|
||||
"ETC_수술일자_1_몇년2자리", "ETC_수술일자_2_몇월", "ETC_수술일자_3_몇일",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -229,7 +229,8 @@ public partial class PreviewWindow : Window
|
||||
PatientIdentity.DiagnosisSourceFor(picked),
|
||||
PatientIdentity.SurgerySourceFor(picked),
|
||||
PatientIdentity.ReviewDiagnosisSourceFor(picked),
|
||||
PatientIdentity.VitalSourceFor(picked));
|
||||
PatientIdentity.VitalSourceFor(picked),
|
||||
PatientIdentity.ScheduledSurgerySourceFor(picked));
|
||||
// 러너를 <b>새로</b> 만든다. 같은 러너를 재사용하면 옛 환자의 조회 결과가 캐시에 남아
|
||||
// 환자를 바꿨는데 표는 그대로가 된다 — 태그만 바뀌고 표는 안 바뀌면 아무도 눈치채지 못한다.
|
||||
fields = new MDataTableRunner(designer.Document,
|
||||
|
||||
Reference in New Issue
Block a user