요양기관 12종을 옮긴다 — 왕복 12회를 1회로 접었다. 63종 → 75종

ETC_요양기관명칭_* 계열(bzDataInterface.vb:15656-16709). 12종 전부
<b>같은 조인(M_DepMst⨝M_HspMst)의 다른 컬럼</b>이라는 것을 본문에서 확인했다 —
병원명(이미 이식)·병원명_영문·병원대표자·병원장·병원장귀하·전화번호·팩스번호·
전화_팩스·영문_전화_팩스·주소·주소_영문·사업자등록번호·요양기관기호.

레거시는 태그마다 이 조인을 따로 돌린다(12종 = 왕복 12회).
컬럼 9개를 한 번에 뽑아 행으로 들고, 값 선택은 해석기가 한다.
HospitalName(문자열) → HospitalRow(행) 로 일반화했다.

접미·결합도 본문 그대로다:
  병원장 = 병원명+"장" / 병원장귀하 = +"장 귀하"
  전화_팩스 = "Tel : … FAX : …" / 영문판 = "Tel : +82 - … FAX : +82 - …"
접두·접미가 붙는 태그는 <b>재료 컬럼</b>으로 빈 값을 판정한다 —
병원명이 비면 "장"만 찍히는 것을 값으로 세지 않는다.

실DB 확인: --db-patient ㉔ 가 9열·병원명 6자를 돌려줬다.

## 게이트

- dotnet test 338/338 · --edit-smoke 실패 0(이름 검사 75종)
- --db-patient 전건 통과 · --db-render P062 md5 불변

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Msystech
2026-08-19 15:42:26 +09:00
co-authored by Claude Opus 5
parent ef4f36419f
commit 26d9606d1e
6 changed files with 85 additions and 23 deletions
+3 -2
View File
@@ -2200,8 +2200,9 @@ public static class DbSmoke
Check("㉓ 진료과 마스터가 이름을 돌려준다", korNam.Trim().Length > 0,
$"코드 '{depCod}' 로 M_DepMst 에서 이름을 못 찾았다");
var hosp = contexts.HospitalName(depCod, octx.AdpDtm);
lines.Add($" 요양기관명: {hosp.Length}자");
var hospRow = contexts.HospitalRow(depCod, octx.AdpDtm);
var hosp = PatientContext.Value(hospRow, "HspInsNam");
lines.Add($" 요양기관: {hospRow.Count}열, 병원명 {hosp.Length}자");
Check("㉔ 요양기관명이 나온다", hosp.Trim().Length > 0,
"M_DepMst⨝M_HspMst 조인이 0행 — 유효기간이나 DepHspCod 를 의심할 것");
@@ -154,25 +154,26 @@ public static class PatientIdentity
}
/// <summary>
/// 요양기관 — 못 읽으면 빈 문자열.
/// 요양기관 — 못 읽으면 빈 사전.
/// 판정 일자가 진료과와 <b>다르다</b>(bzDataInterface.vb:15815-15825):
/// 입원·퇴원(ILV)은 퇴원일시, 입원 중은 지금, 외래는 접수일시 — 문맥의 적용일시가 그 판정과 같다.
/// </summary>
public static string HospitalNameOf(PatientContext context)
public static IReadOnlyDictionary<string, string> HospitalRowOf(PatientContext context)
{
var connection = ConfigService.Current.ConnectionString;
var empty = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (connection.Length == 0)
{
return string.Empty;
return empty;
}
try
{
return new PatientContextStore(connection).HospitalName(
return new PatientContextStore(connection).HospitalRow(
PatientContext.Value(context.CodInf, "CodDepCod"), context.AdpDtm);
}
catch
{
return string.Empty;
return empty;
}
}
@@ -67,7 +67,7 @@ public static class PatientSession
? new PatientTagResolver(picked, session,
PatientIdentity.ResidentNumberOf(picked), PatientIdentity.AddressOf(picked),
PatientIdentity.DoctorOf(picked), PatientIdentity.DepartmentOf(picked),
PatientIdentity.HospitalNameOf(picked),
PatientIdentity.HospitalRowOf(picked),
PatientIdentity.DiagnosisSourceFor(picked),
PatientIdentity.SurgerySourceFor(picked),
PatientIdentity.ReviewDiagnosisSourceFor(picked))
@@ -160,8 +160,8 @@ public sealed class PatientTagResolver : ITagValueResolver
/// <summary>이 내원의 진료과 마스터 행(M_DepMst) — 없으면 빈 사전</summary>
private readonly IReadOnlyDictionary<string, string> department;
/// <summary>요양기관명(HspInsNam) — 못 읽었으면 빈 문자열</summary>
private readonly string hospitalName;
/// <summary>요양기관 행(M_HspMst 컬럼들) — 없으면 빈 사전</summary>
private readonly IReadOnlyDictionary<string, string> hospital;
/// <summary>
/// 상병 행 공급자 — 주/부/전체 별로 <b>물을 때 한 번만</b> 읽는다.
@@ -187,7 +187,8 @@ public sealed class PatientTagResolver : ITagValueResolver
public PatientTagResolver(PatientContext context, ITagValueResolver next,
string residentNumber = "", IReadOnlyDictionary<string, string>? address = null,
IReadOnlyDictionary<string, string>? doctor = null,
IReadOnlyDictionary<string, string>? department = null, string hospitalName = "",
IReadOnlyDictionary<string, string>? department = null,
IReadOnlyDictionary<string, string>? hospital = null,
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)
@@ -198,7 +199,7 @@ public sealed class PatientTagResolver : ITagValueResolver
this.address = address ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
this.doctor = doctor ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
this.department = department ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
this.hospitalName = hospitalName;
this.hospital = hospital ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
this.diagnosisSource = diagnosisSource;
this.surgerySource = surgerySource;
this.reviewSource = reviewSource;
@@ -401,10 +402,59 @@ public sealed class PatientTagResolver : ITagValueResolver
? "이 내원의 진료과를 찾지 못했습니다"
: "진료과 마스터에 이름이 비어 있습니다");
}
// ── 요양기관 12종 — 같은 조인의 다른 컬럼(bzDataInterface.vb:15656-16709) ──
// 접미·결합도 본문 그대로: 병원장 = 병원명+"장", 귀하 = +"장 귀하",
// 전화_팩스 = "Tel : … FAX : …", 영문판은 "+82 - " 접두가 붙는다.
case "ETC_요양기관명칭_병원명":
return hospitalName.Trim().Length > 0
? new TagValue(true, hospitalName.Trim())
: new TagValue(false, "요양기관명을 찾지 못했습니다(M_DepMst·M_HspMst 조인)");
case "ETC_요양기관명칭_병원명_영문":
case "ETC_요양기관명칭_병원대표자":
case "ETC_요양기관명칭_병원장":
case "ETC_요양기관명칭_병원장귀하":
case "ETC_요양기관명칭_병원전화번호":
case "ETC_요양기관명칭_병원팩스번호":
case "ETC_요양기관명칭_병원전화_팩스번호":
case "ETC_요양기관명칭_영문_병원전화_팩스번호":
case "ETC_요양기관명칭_병원주소":
case "ETC_요양기관명칭_병원주소_영문":
case "ETC_요양기관명칭_사업자등록번호":
case "ETC_요양기관기호":
{
if (hospital.Count == 0)
{
return new TagValue(false, "요양기관 정보를 찾지 못했습니다(M_DepMst·M_HspMst 조인)");
}
string Hsp(string column) => hospital.TryGetValue(column, out var v) ? v.Trim() : string.Empty;
var hspValue = tag switch
{
"ETC_요양기관명칭_병원명" => Hsp("HspInsNam"),
"ETC_요양기관명칭_병원명_영문" => Hsp("HspEngNam"),
"ETC_요양기관명칭_병원대표자" => Hsp("HspOwnNam"),
"ETC_요양기관명칭_병원장" => Hsp("HspInsNam") + "장",
"ETC_요양기관명칭_병원장귀하" => Hsp("HspInsNam") + "장 귀하",
"ETC_요양기관명칭_병원전화번호" => Hsp("HspTelNum"),
"ETC_요양기관명칭_병원팩스번호" => Hsp("HspFaxNum"),
"ETC_요양기관명칭_병원전화_팩스번호" =>
$"Tel : {Hsp("HspTelNum")} FAX : {Hsp("HspFaxNum")}",
"ETC_요양기관명칭_영문_병원전화_팩스번호" =>
$"Tel : +82 - {Hsp("HspTelNum")} FAX : +82 - {Hsp("HspFaxNum")}",
"ETC_요양기관명칭_병원주소" => Hsp("HspKorAdr"),
"ETC_요양기관명칭_병원주소_영문" => Hsp("HspEngAdr"),
"ETC_요양기관명칭_사업자등록번호" => Hsp("HspRgnNum"),
_ => Hsp("HspInsNum"),
};
// 병원장·전화_팩스는 접두·접미가 붙어 "장"만 남을 수 있다 — 재료 컬럼으로 빈 값을 판정한다
var material = tag switch
{
"ETC_요양기관명칭_병원장" or "ETC_요양기관명칭_병원장귀하" => Hsp("HspInsNam"),
"ETC_요양기관명칭_병원전화_팩스번호" or "ETC_요양기관명칭_영문_병원전화_팩스번호"
=> Hsp("HspTelNum") + Hsp("HspFaxNum"),
_ => hspValue,
};
return material.Trim().Length > 0
? new TagValue(true, hspValue)
: new TagValue(false, "요양기관 행은 있는데 그 값이 비어 있습니다");
}
// ── User 계열 — 로그인 사용자가 의사면 <b>사용자의</b> 과·이름, 아니면 환자의 것 ──
// (bzDataInterface.vb:5697-5826). 개원일 조건(HspStrDte>=20220701)은 세션에 없어
@@ -642,6 +692,12 @@ public sealed class PatientTagResolver : ITagValueResolver
"PAT_우편번호", "PAT_도로명주소", "PAT_지번주소", "PAT_영문도로명주소",
"OCM_담당의사", "OCM_담당의사_담당의사코드", "OCM_담당의사_연락처",
"OCM_진료과", "ETC_요양기관명칭_병원명",
"ETC_요양기관명칭_병원명_영문", "ETC_요양기관명칭_병원대표자",
"ETC_요양기관명칭_병원장", "ETC_요양기관명칭_병원장귀하",
"ETC_요양기관명칭_병원전화번호", "ETC_요양기관명칭_병원팩스번호",
"ETC_요양기관명칭_병원전화_팩스번호", "ETC_요양기관명칭_영문_병원전화_팩스번호",
"ETC_요양기관명칭_병원주소", "ETC_요양기관명칭_병원주소_영문",
"ETC_요양기관명칭_사업자등록번호", "ETC_요양기관기호",
"OCM_담당의사_영문", "OCM_담당의사면허번호", "OCM_담당의사전문의번호",
"OCM_User진료과", "OCM_User진료과_한글명칭", "OCM_User진료의사명",
"OCM_상병명_가로", "OCM_상병명_세로", "OCM_상병명_영어_가로", "OCM_상병명_영어_세로",
@@ -224,7 +224,7 @@ public partial class PreviewWindow : Window
: new PatientTagResolver(picked, session,
PatientIdentity.ResidentNumberOf(picked), PatientIdentity.AddressOf(picked),
PatientIdentity.DoctorOf(picked), PatientIdentity.DepartmentOf(picked),
PatientIdentity.HospitalNameOf(picked),
PatientIdentity.HospitalRowOf(picked),
PatientIdentity.DiagnosisSourceFor(picked),
PatientIdentity.SurgerySourceFor(picked),
PatientIdentity.ReviewDiagnosisSourceFor(picked));