Load PoC workspace from JSON

This commit is contained in:
2026-06-09 14:21:25 +09:00
parent 0337977bff
commit c25911ca34
6 changed files with 233 additions and 7 deletions

View File

@@ -0,0 +1,108 @@
{
"cueSheets": [
{
"cueSheetId": "CJ-20260817-001",
"broadcastTime": "2026-08-17T09:00:00",
"programTitle": "최화정쇼",
"productName": "비비안 컴포트 패키지",
"priceText": "79,900원",
"compositionText": "브라 4종 + 팬티 4종",
"promotionText": "ARS 10% 할인"
},
{
"cueSheetId": "CJ-20260817-002",
"broadcastTime": "2026-08-17T11:00:00",
"programTitle": "동가게",
"productName": "프리미엄 한우 세트",
"priceText": "69,900원",
"compositionText": "1등급 등심 1.2kg",
"promotionText": "무이자 5개월"
},
{
"cueSheetId": "CJ-20260817-003",
"broadcastTime": "2026-08-17T14:00:00",
"programTitle": "스타일 라이브",
"productName": "썸머 린넨 셋업",
"priceText": "59,900원",
"compositionText": "자켓 + 팬츠",
"promotionText": "앱 주문 추가 적립"
}
],
"activeTemplate": {
"templateId": "CJ_PRICE_LOWER_01",
"templateName": "가격형 하단 자막 / Tornado Scene",
"tornadoVersion": "Tornado2 primary, Tornado3 ready",
"mappings": [
{
"cueSheetField": "programTitle",
"sceneObjectName": "TXT_PROGRAM",
"attribute": "Text",
"rule": "trim"
},
{
"cueSheetField": "productName",
"sceneObjectName": "TXT_PRODUCT",
"attribute": "Text",
"rule": "normalize-space"
},
{
"cueSheetField": "priceText",
"sceneObjectName": "TXT_PRICE",
"attribute": "Text",
"rule": "currency-text"
},
{
"cueSheetField": "compositionText",
"sceneObjectName": "TXT_COMPOSITION",
"attribute": "Text",
"rule": "line-break-safe"
},
{
"cueSheetField": "promotionText",
"sceneObjectName": "TXT_PROMOTION",
"attribute": "Text",
"rule": "special-char-exception"
}
]
},
"activeScene": {
"sceneName": "CJ_PRICE_LOWER_01_0900.tscn",
"templateId": "CJ_PRICE_LOWER_01",
"visibleTextObjects": {
"TXT_PROGRAM": "최화정쇼",
"TXT_PRODUCT": "비비안 컴포트 패키지",
"TXT_PRICE": "79,900원",
"TXT_COMPOSITION": "브라 4종 + 팬티 4종",
"TXT_PROMOTION": "ARS 10% 할인"
},
"imagePaths": [
"D:\\CJOnStyle\\Products\\VIVIEN\\main.png"
]
},
"inspectionResults": [
{
"cueSheetId": "CJ-20260817-001",
"programTitle": "최화정쇼",
"sceneName": "CJ_PRICE_LOWER_01_0900.tscn",
"matchRate": 100,
"severity": "Low",
"decision": "1차 통과"
},
{
"cueSheetId": "CJ-20260817-002",
"programTitle": "동가게",
"sceneName": "CJ_PRICE_LOWER_01_1100.tscn",
"matchRate": 96.4,
"severity": "High",
"decision": "가격 불일치 확인"
},
{
"cueSheetId": "CJ-20260817-003",
"programTitle": "스타일 라이브",
"sceneName": "CJ_PRICE_LOWER_01_1400.tscn",
"matchRate": 98.1,
"severity": "Medium",
"decision": "특수문자 예외 후보"
}
]
}

View File

@@ -1,3 +1,4 @@
using System.Text.Json;
using TornadoAce_CJOnStyle.Domain;
namespace TornadoAce_CJOnStyle.Services
@@ -25,12 +26,50 @@ namespace TornadoAce_CJOnStyle.Services
public IReadOnlyList<InspectionResult> InspectionResults { get; }
public static AutomationWorkspace CreateDemo()
{
return TryCreateFromSeedFile() ?? CreateEmbeddedDemo();
}
private static AutomationWorkspace? TryCreateFromSeedFile()
{
var seedPath = Path.Combine(AppContext.BaseDirectory, "Data", "PocWorkspace.json");
if (!File.Exists(seedPath))
{
return null;
}
var json = File.ReadAllText(seedPath);
var seed = JsonSerializer.Deserialize<WorkspaceSeed>(
json,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (seed?.CueSheets is not { Count: > 0 } cueSheets
|| seed.ActiveTemplate is null
|| seed.ActiveScene is null)
{
return null;
}
var template = seed.ActiveTemplate.ToDomain();
var scene = seed.ActiveScene.ToDomain();
var inspections = seed.InspectionResults
.Select(inspection => inspection.ToDomain())
.ToArray();
return new AutomationWorkspace(
cueSheets.Select(cueSheet => cueSheet.ToDomain()).ToArray(),
template,
scene,
inspections);
}
private static AutomationWorkspace CreateEmbeddedDemo()
{
var cueSheets = new[]
{
new CueSheetRecord("CJ-20260817-001", DateTime.Today.AddHours(9), "최화정쇼", "비비안 컴포트 패키지", "79,900원", "브라 4종 + 팬티 4종", "ARS 10% 할인"),
new CueSheetRecord("CJ-20260817-002", DateTime.Today.AddHours(11), "동가게", "프리미엄 한우 세트", "69,900원", "1등급 등심 1.2kg", "무이자 5개월"),
new CueSheetRecord("CJ-20260817-003", DateTime.Today.AddHours(14), "스타일 라이브", "썸머 린넨 셋업", "59,900원", "자켓 + 팬츠", "앱 주문 추가 적립")
new CueSheetRecord("CJ-20260817-001", new DateTime(2026, 8, 17, 9, 0, 0), "최화정쇼", "비비안 컴포트 패키지", "79,900원", "브라 4종 + 팬티 4종", "ARS 10% 할인"),
new CueSheetRecord("CJ-20260817-002", new DateTime(2026, 8, 17, 11, 0, 0), "동가게", "프리미엄 한우 세트", "69,900원", "1등급 등심 1.2kg", "무이자 5개월"),
new CueSheetRecord("CJ-20260817-003", new DateTime(2026, 8, 17, 14, 0, 0), "스타일 라이브", "썸머 린넨 셋업", "59,900원", "자켓 + 팬츠", "앱 주문 추가 적립")
};
var mappings = new[]
@@ -66,5 +105,80 @@ namespace TornadoAce_CJOnStyle.Services
return new AutomationWorkspace(cueSheets, template, scene, inspections);
}
private sealed record WorkspaceSeed(
List<CueSheetSeed> CueSheets,
SceneTemplateSeed ActiveTemplate,
SceneDataSeed ActiveScene,
List<InspectionSeed> InspectionResults);
private sealed record CueSheetSeed(
string CueSheetId,
DateTime BroadcastTime,
string ProgramTitle,
string ProductName,
string PriceText,
string CompositionText,
string PromotionText)
{
public CueSheetRecord ToDomain()
{
return new CueSheetRecord(CueSheetId, BroadcastTime, ProgramTitle, ProductName, PriceText, CompositionText, PromotionText);
}
}
private sealed record SceneTemplateSeed(
string TemplateId,
string TemplateName,
string TornadoVersion,
List<FieldMappingSeed> Mappings)
{
public SceneTemplateDefinition ToDomain()
{
return new SceneTemplateDefinition(
TemplateId,
TemplateName,
TornadoVersion,
Mappings.Select(mapping => mapping.ToDomain()).ToArray());
}
}
private sealed record FieldMappingSeed(
string CueSheetField,
string SceneObjectName,
string Attribute,
string Rule)
{
public FieldMappingDefinition ToDomain()
{
return new FieldMappingDefinition(CueSheetField, SceneObjectName, Attribute, Rule);
}
}
private sealed record SceneDataSeed(
string SceneName,
string TemplateId,
Dictionary<string, string> VisibleTextObjects,
List<string> ImagePaths)
{
public SceneDataSnapshot ToDomain()
{
return new SceneDataSnapshot(SceneName, TemplateId, VisibleTextObjects, ImagePaths);
}
}
private sealed record InspectionSeed(
string CueSheetId,
string ProgramTitle,
string SceneName,
double MatchRate,
string Severity,
string Decision)
{
public InspectionResult ToDomain()
{
return new InspectionResult(CueSheetId, ProgramTitle, SceneName, MatchRate, Severity, Decision);
}
}
}
}

View File

@@ -45,6 +45,9 @@
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
<Content Include="Data\PocWorkspace.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View File

@@ -250,7 +250,7 @@ namespace TornadoAce_CJOnStyle.ViewModels
private void LoadRequirementCoverage()
{
RequirementCoverage.Add(new PocRequirementViewModel("RFP-01", "큐시트 API 연동", "PoC", "샘플 큐시트 선택", Brush(0xFF45D6D1)));
RequirementCoverage.Add(new PocRequirementViewModel("RFP-01", "큐시트 API 연동", "PoC", "JSON Mock 수신", Brush(0xFF45D6D1)));
RequirementCoverage.Add(new PocRequirementViewModel("RFP-02", "ERP 편성 연동", "대기", "엔드포인트 자리", Brush(0xFFFFB547)));
RequirementCoverage.Add(new PocRequirementViewModel("RFP-03", "Data Field Mapping", "PoC", "Scene 스냅샷 생성", Brush(0xFF37D67A)));
RequirementCoverage.Add(new PocRequirementViewModel("RFP-04", "Tornado Scene API", "PoC", "Text/Image 추출 샘플", Brush(0xFF37D67A)));

View File

@@ -11,7 +11,8 @@
## 현재 초기 빌드 범위
- `Domain`: 큐시트, Scene 템플릿, Scene 데이터 스냅샷, 검수 결과 모델.
- `Services`: 실제 API/Tornado SDK 연결을 받을 인터페이스와 목 워크스페이스.
- `Data`: 큐시트, Scene 템플릿, Scene 추출값을 담은 JSON 기반 PoC 샘플.
- `Services`: 실제 API/Tornado SDK 연결을 받을 인터페이스, JSON 로더, 목 워크스페이스.
- `ViewModels`: 운영 콘솔에 표시할 상태 카드, 파이프라인, 매핑표, 검수 큐.
- `Views`: WinUI 3 기반 첫 화면. 예제 프로젝트와 같은 데스크톱 앱 방향으로 유지.

View File

@@ -13,7 +13,7 @@
| ID | RFP 요구사항 | 초기 PoC 반영 상태 | 현재 구현 근거 |
| --- | --- | --- | --- |
| RFP-01 | 신규 큐시트 플랫폼 API 연동 | Mock 데이터 수신 흐름 구현 | `AutomationWorkspace.CueSheets`, `RefreshCueSheetCommand` |
| RFP-01 | 신규 큐시트 플랫폼 API 연동 | JSON Mock 데이터 수신 흐름 구현 | `Data/PocWorkspace.json`, `AutomationWorkspace.CueSheets`, `RefreshCueSheetCommand` |
| RFP-02 | ERP 편성데이터 연동 | 연동 엔드포인트 자리 표시 | `IntegrationEndpointViewModel` |
| RFP-03 | CG 포맷 제작 자동화, Data Field Mapping | 큐시트 필드와 Scene 객체 매핑 구현 | `SceneTemplateDefinition`, `PocAutomationEngine.GenerateScene` |
| RFP-04 | Tornado Scene API | Scene visible Text/Image 스냅샷 샘플 구현 | `SceneDataSnapshot.ToPreviewJson` |
@@ -32,7 +32,7 @@
## 다음 구현 후보
- 큐시트/ERP Mock 데이터를 JSON 파일로 분리하여 실제 API 응답 구조와 맞춘다.
- ERP Mock 데이터를 JSON 파일로 분리하여 실제 편성 API 응답 구조와 맞춘다.
- Tornado2/Tornado3 어댑터를 동일 인터페이스로 유지하고, 버전별 구현체를 분리한다.
- Scene 내부 비노출 객체를 구분할 메타데이터 규칙을 정의한다.
- 1차 검수 실패 사유를 필드 단위로 누적하고 LLM 요청 후보로 전달한다.