환자를 골라 태그에 값이 들어간다 — 데이터 계층만 있던 것을 화면에 붙인다
전 커밋까지 환자 조회·시각 판정·권한 규칙은 다 있었지만 화면에 붙은 것이 하나도 없었다. 미리보기를 열면 환자 선택이 없고, 태그는 전부 사유만 찍혔다. ## 만든 사슬 PatientPickerDialogView(모달) → PatientContextStore(문맥 5행) → PatientTagResolver(태그 → 컬럼) → PreviewWindow.UsePatient → 종이 모달로 둔 이유: 30초 안에 끝나는 자족적인 일이고 권한 문이 걸린 동작이다. 사이드 패널로 두면 실제 환자 정보를 꺼내는 창이 항상 열려 있게 되는데 이 창은 열려 있는 것 자체가 비용이다. 환자와 내원을 한 화면에 둔다. 고르는 대상은 환자가 아니라 내원이다 — 문맥 5행이 전부 ComNum + AdpDtm 으로 읽히므로 환자만 골라서는 아무것도 못 읽는다. 기준 시점(마지막/처음)을 화면에 노출한다. 입원 내원의 진료과·병실·자격 값을 실제로 바꾸는 값이라 숨기면 값이 왜 다른지 설명할 수 없다. 문맥 읽기를 창 안에서 한다. 닫고 나서 읽으면 실패를 미리보기 창에서 알려야 하고 그때는 이미 고른 것이 사라진 뒤다. ## 매핑은 확인된 6종만 PAT_이름 / PAT_차트번호 / PAT_주민번호 / PAT_휴대전화 / PAT_전화번호 / PAT_주소. 전부 레거시 조회 SQL 의 SELECT 목록에서 컬럼을 직접 확인한 것이다. PAT_나이·PAT_성별은 넣지 않았다 — UDF_GETAGE·UDF_GETSEX 를 거치는 값이라 컬럼을 짐작해 넣으면 값이 조용히 틀린다. 종이에 그럴듯한 값이 찍히면 아무도 의심하지 않으므로 빈칸보다 나쁘다. 안 옮긴 환자 태그는 "아직 옮기지 않았습니다"라고 말한다. "DB 접속이 없습니다"로 뭉개면 환자를 골랐는데도 그 사유가 나와 접속을 의심하게 된다. ## 문맥 조회를 한 번에 다섯 번 읽는다 레거시는 5행을 프로퍼티 접근 시점에 하나씩 지연 로딩한다(각각 별도 왕복). 태그마다 다시 읽으면 태그 40개짜리 서식에 왕복 40회가 된다. ## 개인정보 - 감사 로그는 고른 직후 남긴다 — 종이에 값이 찍히는 것과 무관하게 조회는 이미 일어났다. - 로그에 환자 이름을 남기지 않는다(내원번호로 추적 가능하고, 로그가 개인정보를 들고 있으면 로그 자체가 위험물이 된다). - 진단 리포트도 값 대신 길이만 적는다. - 권한 문은 수정(TK_MODIFY)과 같은 범위다. 넓히면 서식생성기가 환자 조회 도구가 된다. - 화면에서 감사 기록 사실을 말한다. ## 게이트가 잡은 것 셋 ① --db-patient 가 "전건 통과"인데 ⑧~⑬ 이 통째로 SKIP 이었다. 첫 환자에게 내원이 0건이라 문맥 조회를 한 번도 안 돌린 채 초록불이었다. 내원 있는 환자를 30명까지 훑고, 못 찾으면 SKIP 이 아니라 FAIL 로 바꿨다. ② 그렇게 찾은 내원에서 CodInf·CoiInf·CowInf 가 셋 다 0행이었다. 0 을 적어 두는 것으로는 "조건이 어긋났다"와 "행이 없다"를 못 가른다. 조건 없는 원본 행을 함께 찍게 하니 후자였다(1988년 내원, 행 자체가 없음). ③ 그러면 기간 조회 세 갈래는 아직 한 번도 행을 돌려준 적이 없다. 안 돌아본 갈래는 통과한 갈래가 아니므로, 각 표에서 직접 내원을 찾아 들어가 BETWEEN·MtiSeq 조건이 실제로 행을 뽑는 것까지 확인하게 했다(⑮). --edit-smoke 판정 7건 추가. 전부 대조군이 있다 — 환자 없이 같은 종이를 찍어 사유가 나오는 것을 먼저 확인한 뒤 값으로 바뀌는 것을 본다. 미리보기 창 배선도 따로 본다(붙이면 값, 떼면 사라짐). 그래서 고르는 일과 붙이는 일을 나눠 두었다 — 모달에 묻어 두면 "골랐는데 종이가 그대로"를 자동으로 못 잡는다. ## 게이트 - dotnet test 312/312 - --edit-smoke 실패 0 (환자·미리보기 판정 7건 추가) - --dialog-shots FAIL 0 (08b-patient-picker 추가) - --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 (변화 없음) - --db-smoke 1,271건 diff 0 - --db-patient ①~⑮ 전건 통과 (기간 조회 3갈래 실제 통과 확인) - --db-modify-smoke S999 8/8 (⑤ 는 E_SctMst 0행으로 여전히 SKIP) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
be9889ed4e
commit
88dab2edcf
@@ -0,0 +1,79 @@
|
||||
using SheetMe.Core.Catalog;
|
||||
using SheetMe.Data.Stores;
|
||||
|
||||
namespace SheetMe.Designer.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 환자 문맥으로 태그를 값으로 바꾼다. 못 바꾸면 <b>앞 해석기</b>(시각·로그인 사용자)로 넘긴다.
|
||||
///
|
||||
/// <b>매핑을 확인된 것만 넣는다.</b> 레거시 태그 384종 중 238종은 함수 본문에 자기 SQL 을 들고 있고,
|
||||
/// 나머지도 형식 가공(날짜 포맷·마스킹·결합)이 제각각이다. 컬럼명을 짐작해서 넣으면
|
||||
/// <b>값이 조용히 틀린다</b> — 빈칸보다 나쁘다. 종이에 그럴듯한 값이 찍히면 아무도 의심하지 않는다.
|
||||
///
|
||||
/// 그래서 여기 있는 것은 <b>조회 SQL 에서 컬럼을 직접 확인한</b> 것뿐이다
|
||||
/// (dtPatVisitList.vb:51-91 · dtCommonLib.vb:3637-3780 의 SELECT 목록).
|
||||
/// 나머지는 "아직 옮기지 않았다"고 <b>말한다</b> — 환자 문맥이 없어서가 아니라는 사실을 구분해 준다.
|
||||
///
|
||||
/// 표를 늘리는 것이 A4 다. 늘릴 때마다 레거시 함수 본문을 읽고 근거를 주석에 남긴다.
|
||||
/// </summary>
|
||||
public sealed class PatientTagResolver : ITagValueResolver
|
||||
{
|
||||
#region Member Fields
|
||||
/// <summary>태그 → (문맥 행, 컬럼). 전부 조회 SQL 에서 컬럼을 확인한 것이다.</summary>
|
||||
private static readonly Dictionary<string, (string Row, string Column)> Direct =
|
||||
new(StringComparer.Ordinal)
|
||||
{
|
||||
// P_PatInf — dtPatVisitList.vb:51 SELECT 목록에서 확인
|
||||
["PAT_이름"] = ("PatInf", "PatNam"),
|
||||
["PAT_차트번호"] = ("PatInf", "PatChtNum"),
|
||||
["PAT_주민번호"] = ("PatInf", "PatResNum"),
|
||||
["PAT_휴대전화"] = ("PatInf", "PatMblPhn"),
|
||||
["PAT_전화번호"] = ("PatInf", "PatHomPhn"),
|
||||
["PAT_주소"] = ("PatInf", "PatAdrDtl"),
|
||||
};
|
||||
|
||||
private readonly ITagValueResolver next;
|
||||
private readonly PatientContext context;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public PatientTagResolver(PatientContext context, ITagValueResolver next)
|
||||
{
|
||||
this.context = context;
|
||||
this.next = next;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public TagValue Resolve(string tag)
|
||||
{
|
||||
if (!Direct.TryGetValue(tag, out var map))
|
||||
{
|
||||
// 환자 문맥이 <b>있는데도</b> 못 만든 것과, 문맥이 없어서 못 만든 것은 다른 일이다.
|
||||
// 앞 해석기가 시각·사용자 태그를 처리하고, 그것도 아니면 사유를 돌려준다.
|
||||
var fallback = next.Resolve(tag);
|
||||
if (fallback.Resolved || TagPreviewCatalog.KindOf(tag) != TagPreviewKind.PatientContext)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
return new TagValue(false, "환자 태그이지만 아직 옮기지 않았습니다");
|
||||
}
|
||||
|
||||
var row = map.Row switch
|
||||
{
|
||||
"PatInf" => context.PatInf,
|
||||
"ComInf" => context.ComInf,
|
||||
"CodInf" => context.CodInf,
|
||||
"CoiInf" => context.CoiInf,
|
||||
_ => context.CowInf,
|
||||
};
|
||||
var value = PatientContext.Value(row, map.Column);
|
||||
return value.Length > 0
|
||||
? new TagValue(true, value)
|
||||
: new TagValue(false, $"이 환자에게는 {map.Column} 값이 비어 있습니다");
|
||||
}
|
||||
|
||||
/// <summary>지금 옮겨진 환자 태그 수 — 화면이 커버리지를 정직하게 말할 때 쓴다</summary>
|
||||
public static int MappedCount => Direct.Count;
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user