싸인·직인·로고 17종 — 경로 조회와 이미지 렌더 이식(전송 계층은 재현 불가)
워크플로 조사(읽기 5 + 반박 검증 5)로 전송 계층과 태그 17종을 훑었고,
검증이 조사서의 "FTP" 단정을 반박해 실체가 드러났다: 전송 수단은 단일
프로토콜이 아니라 DB 설정(M_DtlMst 'EMR_FileTransfer' Type)이 고르는
5갈래이고, 새 진단 --db-filecfg 로 확인한 우리 사이트 값은 Type=1 =
FileServer = ActiveX OCX(AxBITSFTLib.AxbitSFT)다. OCX 내부 프로토콜은
코드에 없고 서드파티·ActiveX 는 배포 금지라 <b>다운로드는 재현할 수 없다</b>.
그래서 받지 않고 찾는다: ①레거시 EMR 이 같은 단말에 이미 받아 둔 캐시
({설치경로}\Tmp\DownLoadedFiles\Signature\ — 레거시가 쓰는 그 폴더),
②DB 경로 자체가 열리는 경우(이 병원 저장 형태는 드라이브 절대경로).
둘 다 없으면 사유 → 종이는 빈칸(어제 결정). 판정은 순수 함수
SignatureImagePath 로 두고 테스트 7건(캐시 우선·직접 경로·둘 다 없음·
빈 경로·설치경로 없음·UNC).
경로 조회는 전부 순수 SQL 이라 그대로 이식(SignatureStore): 집도의
(DTRUIDCOD)·마취(수술 ANEUIDCOD / 부서 — 개원일 2021-03-01 분기)·
영상의학(XRAY)·진단검사(LAB)·외출외박 승인자(병동/원무)·병동 수간호사·
병원로고(M_HspMst.HspLgoPth)·병원직인(주석과 달리 DB_REGISTRY 의
'직인경로'). 로그인 사용자 3종은 세션 행에 UidImgPth 를 얹어 왕복 0,
담당의·진단서 의사는 프리페치 행 재사용.
레거시 결함 보존 2건: ETC_병리판독의사싸인은 SELECT 에 UidNam 만 있고
UidImgPth 를 읽어 항상 예외→빈 값(:17434), OCM_Cosign_싸인은 저장 기록
키(EmrKey) 의존이라 미리보기에서 값이 될 수 없다 — 둘 다 사유로 완결.
렌더: PictureBox 가 자리표시자만 그리던 것을 실제 이미지로. 파일을
잠그지 않고(OnLoad) 읽으며, 인쇄가 SizeMode 를 무시하고 사각형에 늘려
그리는 레거시 동작에 맞춰 기본 Stretch=Fill(Zoom 만 비율 유지).
WithTagValue 는 이미지류에서 Text 대신 경로 속성을 채운다.
분류도 정정 — 싸인 계열이 Image(글자만 보여 준다)에서 PatientContext 로.
- dotnet test 354/354 (경로 판정 7건 추가) · --edit-smoke 실패 0
- --db-patient ①~㊲ 전건 통과 — ㊲ 신설: 9개 경로 조회 실행,
마취과 의사 싸인 경로가 실값 29자로 나온다
- --db-filecfg 신설(값 없이 모드만): Type=1 · E_FilInf 102만행 ·
UidImgPth 157행 · 경로 모양 드라이브 절대경로
- --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 불변
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
2310971a10
commit
a0333791b6
@@ -0,0 +1,84 @@
|
||||
namespace SheetMe.Core.Catalog;
|
||||
|
||||
/// <summary>이미지 경로를 어떻게 얻었는가 — 화면·진단이 사유를 정확히 말하기 위해 구분한다</summary>
|
||||
public enum SignatureImageOrigin
|
||||
{
|
||||
/// <summary>쓸 수 있는 파일이 없다</summary>
|
||||
None,
|
||||
|
||||
/// <summary>레거시 EMR 이 이미 내려받아 둔 캐시 파일</summary>
|
||||
LegacyCache,
|
||||
|
||||
/// <summary>DB 가 준 경로 자체가 이 단말에서 바로 열린다</summary>
|
||||
DirectPath,
|
||||
}
|
||||
|
||||
/// <summary>이미지 경로 판정 결과</summary>
|
||||
/// <param name="Origin">어디서 얻었는가</param>
|
||||
/// <param name="LocalPath">열 수 있는 파일의 전체 경로 — 없으면 빈 문자열</param>
|
||||
/// <param name="FileName">원격 경로의 파일명(사유 문구·캐시 조회용)</param>
|
||||
public readonly record struct SignatureImageResult(
|
||||
SignatureImageOrigin Origin, string LocalPath, string FileName);
|
||||
|
||||
/// <summary>
|
||||
/// DB 가 준 <b>원격 경로</b>에서 실제로 열 수 있는 <b>로컬 파일</b>을 정한다.
|
||||
///
|
||||
/// <b>왜 이 규칙인가.</b> 레거시는 원격 경로를 파일서버에서 내려받아 로컬 경로를 돌려준다
|
||||
/// (bzDataInterface.vb 싸인 17종 공통). 그 전송 계층은 이 병원 설정에서 ActiveX OCX
|
||||
/// (<c>AxBITSFTLib.AxbitSFT</c>, M_DtlMst 'EMR_FileTransfer' Type=1)라 .NET 으로 재현할 수 없다 —
|
||||
/// OCX 내부 프로토콜이 코드에 없고, 우리 배포는 서드파티·ActiveX 를 금지한다.
|
||||
///
|
||||
/// 그래서 <b>받지 않고 찾는다</b>: ①레거시 EMR 이 같은 단말에 이미 받아 둔 캐시
|
||||
/// (<c>{설치경로}\Tmp\DownLoadedFiles\Signature\{파일명}</c> — 레거시가 쓰는 바로 그 폴더),
|
||||
/// ②DB 경로 자체가 이 단말에서 열리는 경우(로컬 절대경로·UNC 공유). 둘 다 아니면 없음이다.
|
||||
///
|
||||
/// 이 판정을 순수 함수로 두는 이유는 DB·파일서버 없이 규칙을 검증하기 위해서다.
|
||||
/// </summary>
|
||||
public static class SignatureImagePath
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>레거시가 쓰는 캐시 폴더 — 설치 경로가 없으면 빈 문자열</summary>
|
||||
public static string CacheFolder(string? installPath)
|
||||
=> string.IsNullOrWhiteSpace(installPath)
|
||||
? string.Empty
|
||||
// 레거시는 설치 경로가 없어도 문자열을 그대로 이어 붙여 "\Tmp\..." 라는
|
||||
// 루트 상대경로를 만든다(clsCommonLib.vb:271-287). 그 결함은 옮기지 않는다.
|
||||
: System.IO.Path.Combine(installPath.Trim(), "Tmp", "DownLoadedFiles", "Signature");
|
||||
|
||||
/// <summary>
|
||||
/// 원격 경로 → 열 수 있는 로컬 파일.
|
||||
/// </summary>
|
||||
/// <param name="remotePath">DB 가 준 경로(M_UidMst.UidImgPth 등)</param>
|
||||
/// <param name="installPath">HIS 설치 경로(ServerInfo ini 의 Install\Path)</param>
|
||||
/// <param name="exists">파일 존재 판정 — 테스트가 갈아 끼운다</param>
|
||||
public static SignatureImageResult Resolve(string? remotePath, string? installPath,
|
||||
Func<string, bool>? exists = null)
|
||||
{
|
||||
var remote = (remotePath ?? string.Empty).Trim();
|
||||
if (remote.Length == 0)
|
||||
{
|
||||
return new SignatureImageResult(SignatureImageOrigin.None, string.Empty, string.Empty);
|
||||
}
|
||||
// 레거시는 역슬래시 기준으로 파일명을 뗀다(경로 구분자가 서버 규약이라 Path 로 자르지 않는다)
|
||||
var fileName = remote.Split('\\', '/').Last();
|
||||
var probe = exists ?? System.IO.File.Exists;
|
||||
|
||||
var folder = CacheFolder(installPath);
|
||||
if (folder.Length > 0 && fileName.Length > 0)
|
||||
{
|
||||
var cached = System.IO.Path.Combine(folder, fileName);
|
||||
if (probe(cached))
|
||||
{
|
||||
return new SignatureImageResult(SignatureImageOrigin.LegacyCache, cached, fileName);
|
||||
}
|
||||
}
|
||||
// 캐시에 없으면 원격 경로가 이 단말에서 바로 열리는지 본다 —
|
||||
// 이 병원의 저장 형태는 드라이브 절대경로다(진단 --db-filecfg 실측)
|
||||
if (probe(remote))
|
||||
{
|
||||
return new SignatureImageResult(SignatureImageOrigin.DirectPath, remote, fileName);
|
||||
}
|
||||
return new SignatureImageResult(SignatureImageOrigin.None, string.Empty, fileName);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -157,12 +157,16 @@ public static class TagPreviewCatalog
|
||||
{
|
||||
return TagPreviewKind.Table;
|
||||
}
|
||||
// 싸인·서명·직인·로고 — 값은 <b>이미지 파일 경로</b>다. 환자 해석기가 경로를 얻어
|
||||
// 이 단말에서 파일을 찾고(레거시 캐시 → 경로 자체), PictureBox 가 그린다.
|
||||
// 파일서버 전송(ActiveX)은 옮기지 않았으므로 캐시가 없는 단말에서는 사유가 된다.
|
||||
// 분류는 PatientContext 다 — Image 로 두면 "글자만 보여 준다"는 <b>틀린 사유</b>가 나간다.
|
||||
if (tag.Contains("싸인", StringComparison.Ordinal)
|
||||
|| tag.Contains("서명", StringComparison.Ordinal)
|
||||
|| tag.Contains("직인", StringComparison.Ordinal)
|
||||
|| tag.Contains("로고", StringComparison.Ordinal))
|
||||
{
|
||||
return TagPreviewKind.Image;
|
||||
return TagPreviewKind.PatientContext;
|
||||
}
|
||||
return TagPreviewKind.PatientContext;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user