태그마다 값을 만들 수 있는지, 없다면 왜인지 — 판정표
'예시 값' 을 진짜로 보여 주기로 했는데, 조사해 보니 서식생성기에서 값이 나오는 태그는 384종 중 16종(4%)뿐이다. 나머지는 환자·내원 문맥이 있어야 한다. 그런데 조사에서 값보다 쓸모 있는 것이 나왔다 — <b>왜 안 되는지</b>다. 지금은 어디서도 알 수 없다: · ETC_요양기관명칭_병원명(실사용 3위, 303건)이 환자 내원을 타고 병원을 푼다는 것 · 값이 아니라 표를 돌려주는 태그 15종(콤보 채우기용이라 한 줄 값이 없다) · 파일서버 이미지 경로를 돌려주는 태그 9종(싸인·직인) · 본문이 Return "" 인 죽은 태그 1종 · 태그가 아니라 레거시 내부 함수 8종 — 우리 카탈로그가 Public Function 을 전수 추출해 섞여 들어왔다. 고르면 값이 안 나오는데 레거시는 Try 안에서 조용히 삼킨다. 그 판정을 TagPreviewCatalog 에 담았다. <b>표로 박는다</b> — 판정 근거는 레거시 함수 본문이 환자·내원 객체를 참조하는지 여부이고, 그건 태그 이름에도 접두어에도 없다. ETC_현재일자 와 ETC_요양기관명칭_병원명 은 같은 접두어인데 한쪽만 만들 수 있다. 이름 규칙으로 추정하면 절반만 맞는다. 규칙이 확실한 둘(…List·…InitialDatatable / 싸인·직인)만 접미어·낱말로 가르고 나머지는 실측을 옮겨 적었다. 값 계산도 함께 넣었다. 서버 시각 11종은 레거시 Data2Format 과 같은 형식으로 찍고 (영문은 MM-DD-YYYY 재배치), 로그인 사용자 5종은 항목을 골라 잇는다. ID·이름을 잇는 태그는 한쪽이 비면 점만 남은 값을 만들지 않는다. 단위 테스트 10건. 이 표가 틀리면 화면이 사용자에게 거짓말을 한다 — 값이 나오는 태그를 '안 된다'고 하거나, 안 나오는 태그에 빈 칸을 띄운다. 판정 근거가 레거시 함수 본문이라 코드로는 다시 확인할 수 없어 여기가 유일한 안전망이다. 아직 화면에 붙이지 않았다. 값 줄은 DB 조회(서버 시각·M_UidMst)가 필요하고 그 통로를 만드는 것이 다음 단계다. 판정과 계산은 순수 함수라 그 전에 확정해 둔다. 게이트: 테스트 277/277(신규 10), --edit-smoke 0실패, --modal-check 2/2, --dialog-shots 57파일, --db-smoke 3000 diff 0·예외 0, --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 동일. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b2bdc7fa41
commit
836a3e843a
@@ -0,0 +1,196 @@
|
||||
namespace SheetMe.Core.Catalog;
|
||||
|
||||
/// <summary>태그 값을 이 자리에서 만들 수 있는가 — 못 만든다면 왜</summary>
|
||||
public enum TagPreviewKind
|
||||
{
|
||||
/// <summary>서버 시각으로 만든다</summary>
|
||||
Clock,
|
||||
|
||||
/// <summary>로그인 사용자 정보로 만든다</summary>
|
||||
LoginUser,
|
||||
|
||||
/// <summary>환자·내원 문맥이 있어야 한다 — 서식생성기에는 없다</summary>
|
||||
PatientContext,
|
||||
|
||||
/// <summary>값이 아니라 목록(표)을 돌려준다 — 콤보를 채우는 용도</summary>
|
||||
Table,
|
||||
|
||||
/// <summary>파일서버 이미지 경로를 돌려준다 — 싸인·직인</summary>
|
||||
Image,
|
||||
|
||||
/// <summary>레거시가 항상 빈 값을 돌려준다</summary>
|
||||
Dead,
|
||||
|
||||
/// <summary>태그가 아니라 레거시 내부 함수 이름이다</summary>
|
||||
NotATag,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 태그의 값을 서식생성기에서 만들 수 있는지, 없다면 왜인지.
|
||||
///
|
||||
/// <b>왜 표로 박는가.</b> 판정 근거는 레거시 함수 본문이 환자·내원 객체를 참조하는지 여부다.
|
||||
/// 그건 태그 이름에도 접두어에도 컨트롤 타입에도 들어 있지 않다 —
|
||||
/// <c>ETC_현재일자</c> 와 <c>ETC_요양기관명칭_병원명</c> 은 같은 접두어인데 한쪽만 만들 수 있다.
|
||||
/// 이름 규칙으로 추정하면 절반만 맞는다. 그래서 실측 결과를 그대로 옮겨 적는다.
|
||||
///
|
||||
/// <b>값보다 사유가 본체다.</b> 실제로 값이 나오는 것은 384종 중 16종(4%)뿐이다.
|
||||
/// 그러나 나머지에 대해 "왜 안 나오는가"는 지금 어디서도 얻을 수 없는 정보다 —
|
||||
/// 실사용 3위인 <c>ETC_요양기관명칭_병원명</c>(303건)이 환자 내원을 타고 병원을 푼다는 것도,
|
||||
/// 표를 돌려주는 태그와 죽은 태그와 태그가 아닌 내부 함수가 목록에 섞여 있다는 것도.
|
||||
/// </summary>
|
||||
public static class TagPreviewCatalog
|
||||
{
|
||||
#region Member Fields
|
||||
/// <summary>서버 시각으로 만드는 것 — 값은 포맷 지시자</summary>
|
||||
private static readonly Dictionary<string, ClockFormat> ClockTags = new(StringComparer.Ordinal)
|
||||
{
|
||||
["ETC_현재일자"] = ClockFormat.Date,
|
||||
["ETC_현재일시"] = ClockFormat.DateTime,
|
||||
["ETC_현재일시_MaskBox"] = ClockFormat.DateTimeMask,
|
||||
["ETC_현재년도"] = ClockFormat.Year,
|
||||
["ETC_현재시간"] = ClockFormat.Time,
|
||||
["ETC_현재일자_영문"] = ClockFormat.DateEnglish,
|
||||
["ETC_현재일시_1_년"] = ClockFormat.Year,
|
||||
["ETC_현재일시_2_월"] = ClockFormat.Month,
|
||||
["ETC_현재일시_3_일"] = ClockFormat.Day,
|
||||
["ETC_현재일시_4_몇시"] = ClockFormat.Hour,
|
||||
["ETC_현재일시_5_몇분"] = ClockFormat.Minute,
|
||||
};
|
||||
|
||||
/// <summary>로그인 사용자 정보로 만드는 것 — 값은 어느 항목인지</summary>
|
||||
private static readonly Dictionary<string, UserField> UserTags = new(StringComparer.Ordinal)
|
||||
{
|
||||
["ETC_로그인_사용자명"] = UserField.Name,
|
||||
["ETC_로그인_사용자ID"] = UserField.Id,
|
||||
["ETC_로그인_사용자ID_사용자명"] = UserField.IdAndName,
|
||||
["ETC_로그인_사용자연락처"] = UserField.MobilePhone,
|
||||
["ETC_로그인_원내연락처"] = UserField.OfficePhone,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 못 만드는 것 중 <b>환자 문맥 말고 다른 이유</b>인 것들.
|
||||
/// 여기 없는 것은 전부 <see cref="TagPreviewKind.PatientContext"/> 다.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, TagPreviewKind> Exceptions = new(StringComparer.Ordinal)
|
||||
{
|
||||
// 레거시가 항상 빈 문자열을 돌려준다(본문이 Return "")
|
||||
["OCM_수술일자_Refer"] = TagPreviewKind.Dead,
|
||||
|
||||
// 태그가 아니라 내부 헬퍼 — 우리 카탈로그가 Public Function 을 전수 추출해 섞여 들어왔다.
|
||||
// 레거시 태그 목록(fmDataTag)은 PAT_/OCM_/ETC_ 접두어만 올린다.
|
||||
["퇴원약DT"] = TagPreviewKind.NotATag,
|
||||
["GetOprInfDT"] = TagPreviewKind.NotATag,
|
||||
["GetOprInfDT_OKDMain"] = TagPreviewKind.NotATag,
|
||||
["GetNurseEmrDT"] = TagPreviewKind.NotATag,
|
||||
["GetNurseShtCod"] = TagPreviewKind.NotATag,
|
||||
};
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>이 태그의 값을 어떻게 얻는가 — 못 얻으면 그 이유</summary>
|
||||
public static TagPreviewKind KindOf(string tag)
|
||||
{
|
||||
if (ClockTags.ContainsKey(tag))
|
||||
{
|
||||
return TagPreviewKind.Clock;
|
||||
}
|
||||
if (UserTags.ContainsKey(tag))
|
||||
{
|
||||
return TagPreviewKind.LoginUser;
|
||||
}
|
||||
if (Exceptions.TryGetValue(tag, out var known))
|
||||
{
|
||||
return known;
|
||||
}
|
||||
// 이름 끝으로 갈리는 두 갈래는 규칙이 확실하다 — 표에 나열하지 않는다
|
||||
if (tag.EndsWith("List", StringComparison.Ordinal)
|
||||
|| tag.EndsWith("InitialDatatable", StringComparison.Ordinal))
|
||||
{
|
||||
return TagPreviewKind.Table;
|
||||
}
|
||||
if (tag.Contains("싸인", StringComparison.Ordinal)
|
||||
|| tag.Contains("서명", StringComparison.Ordinal)
|
||||
|| tag.Contains("직인", StringComparison.Ordinal)
|
||||
|| tag.Contains("로고", StringComparison.Ordinal))
|
||||
{
|
||||
return TagPreviewKind.Image;
|
||||
}
|
||||
return TagPreviewKind.PatientContext;
|
||||
}
|
||||
|
||||
/// <summary>값을 만들 수 있는 태그인가</summary>
|
||||
public static bool CanResolve(string tag)
|
||||
=> KindOf(tag) is TagPreviewKind.Clock or TagPreviewKind.LoginUser;
|
||||
|
||||
/// <summary>못 만드는 이유를 사람 말로 — 만들 수 있는 태그면 빈 문자열</summary>
|
||||
public static string ReasonText(string tag) => KindOf(tag) switch
|
||||
{
|
||||
TagPreviewKind.PatientContext =>
|
||||
"환자·내원 정보가 있어야 값이 나오는 태그입니다. 서식생성기에는 환자 문맥이 없습니다.",
|
||||
TagPreviewKind.Table =>
|
||||
"값이 아니라 목록(표)을 돌려주는 태그입니다. 콤보를 채우는 용도라 한 줄로 보여 줄 값이 없습니다.",
|
||||
TagPreviewKind.Image =>
|
||||
"파일서버에서 내려받은 이미지 경로를 돌려주는 태그입니다. 여기서는 글자만 보여 줍니다.",
|
||||
TagPreviewKind.Dead =>
|
||||
"레거시에서 항상 빈 값을 돌려주는 태그입니다.",
|
||||
TagPreviewKind.NotATag =>
|
||||
"레거시 내부 함수 이름입니다. 태그로 쓰면 값이 나오지 않습니다.",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
/// <summary>값 옆에 붙일 근거 한 줄</summary>
|
||||
public static string SourceText(string tag) => KindOf(tag) switch
|
||||
{
|
||||
TagPreviewKind.Clock => "서버 시각 기준",
|
||||
TagPreviewKind.LoginUser => "로그인 사용자 기준",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
/// <summary>서버 시각에서 이 태그의 값을 만든다 — 시각 문자열은 yyyyMMddHHmm(12자리)</summary>
|
||||
public static string FromClock(string tag, string minute12)
|
||||
{
|
||||
if (!ClockTags.TryGetValue(tag, out var format) || minute12.Length < 12)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
var date = minute12[..8];
|
||||
return format switch
|
||||
{
|
||||
ClockFormat.Date => $"{date[..4]}-{date[4..6]}-{date[6..8]}",
|
||||
ClockFormat.DateTime => $"{date[..4]}-{date[4..6]}-{date[6..8]} {minute12[8..10]}:{minute12[10..12]}",
|
||||
ClockFormat.DateTimeMask => $"{date[..4]}-{date[4..6]}-{date[6..8]}{minute12[8..10]}:{minute12[10..12]}",
|
||||
// 레거시 Data2Format_ENG 의 MM-DD-YYYY 재배치
|
||||
ClockFormat.DateEnglish => $"{date[4..6]}-{date[6..8]}-{date[..4]}",
|
||||
ClockFormat.Year => date[..4],
|
||||
ClockFormat.Month => date[4..6],
|
||||
ClockFormat.Day => date[6..8],
|
||||
ClockFormat.Hour => minute12[8..10],
|
||||
ClockFormat.Minute => minute12[10..12],
|
||||
ClockFormat.Time => $"{minute12[8..10]}:{minute12[10..12]}",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>로그인 사용자 정보에서 이 태그의 값을 만든다</summary>
|
||||
public static string FromUser(string tag, string id, string name, string mobile, string office)
|
||||
{
|
||||
if (!UserTags.TryGetValue(tag, out var field))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
return field switch
|
||||
{
|
||||
UserField.Name => name,
|
||||
UserField.Id => id,
|
||||
UserField.IdAndName => id.Length > 0 && name.Length > 0 ? $"{id}.{name}" : string.Empty,
|
||||
UserField.MobilePhone => mobile,
|
||||
UserField.OfficePhone => office,
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
private enum ClockFormat { Date, DateTime, DateTimeMask, DateEnglish, Year, Month, Day, Hour, Minute, Time }
|
||||
|
||||
private enum UserField { Name, Id, IdAndName, MobilePhone, OfficePhone }
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user