Enums (도메인 enum 카탈로그)
Kotlin enum (backend) + TypeScript const enum / union (frontend packages/api-client/src/generated/).
모두 @Enumerated(EnumType.STRING) 저장 — 가독성 + migration 안전.
HqType
| 값 | 의미 |
|---|---|
FRANCHISE | 프랜차이즈 본부. 산하에 DIRECT/FRANCHISE 매장. plan, billingAnchorDay NOT NULL. |
INDEPENDENT | 가상 본사. 시스템 전체 1개 (partial unique). 개인 매장 (INDEPENDENT type) 산하. plan = NULL, billing 모두 NULL. |
HqStatus
현재 도입 완료 — BE v0.7.0 (SPEC #013) Flyway V7. 4 종 enum. generated 스키마
HqAdminListItemStatus. 정지/복구 전이는 SPEC #018 (BE v0.11.0) 도입 완료 — suspend/reactivate endpoint + hq-list 액션. 자동 전환·사유 저장은 후속.
| 값 | 의미 | 비고 |
|---|---|---|
ACTIVE | 정상 운영 | 임퍼소네이션 가능 (FRANCHISE 한정) · 가상 본사는 이 값 고정 · 정지 대상 |
ONBOARDING | 초기 등록 후 활성화 전 | 신규 본사 default · 정지 대상 |
UNPAID | 결제 미납 | UI 경고 (배경 색 변경) · 정지 대상 |
SUSPENDED | 협약 위반 / 운영사 정지 | 임퍼소네이션 confirm 비활성 · 관리자 세션 무효화 + 로그인 차단 · 복구(reactivate)로만 ACTIVE 복귀 |
전이: ACTIVE \| ONBOARDING \| UNPAID → SUSPENDED (suspend) · SUSPENDED → ACTIVE (reactivate). INDEPENDENT 가상 본사는 전이 불가. 상세는 Status Lifecycle.
StoreType
| 값 | 의미 | 결제 단위 |
|---|---|---|
DIRECT | 직영 매장 | HQ |
FRANCHISE | 가맹 매장 | HQ |
INDEPENDENT | 개인 매장 | Store |
StoreStatus
| 값 | 의미 | 자동 전환 |
|---|---|---|
ACTIVE | 정상 | default |
SUSPENDED | 정지 (운영사 액션, #037) | 수동 — ACTIVE|INACTIVE → SUSPENDED |
INACTIVE | 90일 미사용 | 스케줄러 (FR-11.10, 후속) |
SPEC #019 매장 목록(
/stores) 의StoreAdminListItemStatus(generated) 와 정합:ACTIVE·INACTIVE·SUSPENDED.StoreAdminListItemType도INDEPENDENT·DIRECT·FRANCHISE로 위 StoreType 과 일치. SPEC #037 정지/복구 전이 도입 — suspend(ACTIVE\|INACTIVE→SUSPENDED)·reactivate(SUSPENDED→ACTIVE). 상세는 Status Lifecycle 참고.
PlanType
| 값 | 의미 | 라이브러리 |
|---|---|---|
AI | 자체 음원 only | AI 라이브러리 |
TRUST | AI + 신탁음원 | AI + TRUST 라이브러리 |
Role
| 값 | 의미 | hqId | storeId |
|---|---|---|---|
OPERATOR | 운영사 | NULL | NULL |
HQ_MANAGER | 본사 관리자 | NOT NULL | NULL |
STORE_MANAGER | 점장 | NULL | NOT NULL |
도메인 invariant — entity 생성자 require(...).
HQ_MANAGER는 본사 온보딩(POST /api/v1/admin/hq, #010)에서 발급된다.STORE_MANAGER는 매장 생성과 분리해POST /api/v1/admin/stores/{storeId}/managers(#021)로 사후 발급한다 —storeIdtenancy,hqId=null,passwordMustChange=true. 매장당 복수 발급 가능(하드 블록 없음). 운영사 매장 목록(/stores)이StoreAdminListItem.hasManagerAccount로 발급 여부를 표시한다.
AccountStatus
| 값 | 의미 |
|---|---|
ACTIVE | 정상 |
SUSPENDED | 로그인 차단 (정지 — 즉시 세션 무효화) |
WITHDRAWN | 회수/탈퇴 (soft-delete, terminal — 이메일 재사용 불가) |
STORE_MANAGER 계정은 SPEC #029 로 전이 액션이 도입됐다(ACTIVE ↔ SUSPENDED,
ACTIVE/SUSPENDED → WITHDRAWN). generated 노출 enum StoreManagerListItemStatus =
같은 3값. 상세 전이·효과는 Status Lifecycle 참고.
HqAffiliatedStoreType (현재 미도입, 후속)
| 값 (예정) | 의미 |
|---|---|
DIRECT_ONLY | 직영만 |
FRANCHISE_ONLY | 가맹만 |
MIXED | 직영 + 가맹 혼합 |
본사의 매장 구성 형태. 현재는 type 별 store row count 로 유추.
ImpersonationTokenStatus (현재 entity 컬럼 없음, 상태는 used_at 으로 유추)
| 의미 |
|---|
활성 (60초 이내, used_at = NULL) |
만료 (expires_at < now()) |
사용됨 (used_at != NULL) |
TicketStatus (#027 — CS 티켓)
| 값 | 의미 | tone(FE) |
|---|---|---|
OPEN | 열림 (생성 직후) | info |
IN_PROGRESS | 처리 중 | warn |
RESOLVED | 해결됨 | success |
CLOSED | 닫힘 | muted |
전이 규칙은 status-lifecycle 참고 (OPEN → IN_PROGRESS → RESOLVED → CLOSED
- 재오픈
RESOLVED/CLOSED → IN_PROGRESS). 잘못된 전이는 backend 409TICKET_INVALID_STATUS_TRANSITION.
TicketPriority (#027 — CS 티켓)
| 값 | 의미 | 색점 tone(FE, 임시) |
|---|---|---|
URGENT | 긴급 | danger |
HIGH | 높음 | warn |
NORMAL | 보통 | info |
LOW | 낮음 | muted |
v1 — 우선순위는 생성 시점에만 설정(변경 endpoint 없음). 색점은 전용 시안 부재로 기존 StatusPill tone 만 재사용한 임시 레이아웃.
CommentKind (#027 — CS 티켓 코멘트)
| 값 | 의미 |
|---|---|
REPLY | 고객 응답 (외부 노출 톤) |
INTERNAL | 내부 메모 (내부 통제용 톤) |
LegalDocumentStatus (#033 — 약관·개인정보 게시본 상태)
| 값 | 의미 | FE StatusPill 톤 |
|---|---|---|
SCHEDULED | 예약 — effectiveAt 가 미래(아직 발효 전) | info |
ACTIVE | 게시 시점 유효 (즉시 게시·발효됨) | success |
SUPERSEDED | 대체됨 (이후 버전에 의해) | muted |
generated 노출 enum
LegalDocumentResponseStatus·LegalDocumentHistoryItemStatus= 같은 3값. stale 주의: 스케줄러가 없어status는 게시 시점 스냅샷이다. 예약본이 발효 시각을 지나도SCHEDULED로 남을 수 있으므로, “현재 유효본” 판정은effectiveAt(<= now중 최신)으로 한다./active응답은status를 항상ACTIVE로 보정. 상세는 Status Lifecycle.
OperatorAuditAction (#026 — 운영자 액션 감사 로그 액션 종류)
운영자 핵심 변경 액션의 append-only 감사 로그 액션 enum. generated 노출 enum
OperatorAuditItemAction(/audit/actions). 신규 액션은 VARCHAR 영속이라 마이그레이션 무관.
| 값 | 의미 | FE StatusPill 톤 | 도입 |
|---|---|---|---|
HQ_SUSPENDED / HQ_REACTIVATED | 본사 정지 / 복구 | danger / success | #018·#024 |
HQ_UPDATED | 본사명 편집 (운영사 전용) | info | #123 |
STORE_SUSPENDED / STORE_REACTIVATED | 매장 정지 / 복구 | danger / success | #037 |
STORE_CLOSED | 매장 폐점 (비가역 terminal) | danger | #039 |
STORE_MANAGER_ISSUED | 점장 발급 | info | #021 |
STORE_MANAGER_PASSWORD_RESET | 점장 비밀번호 재설정 | muted | #029 |
STORE_MANAGER_SUSPENDED / STORE_MANAGER_REACTIVATED / STORE_MANAGER_REVOKED | 점장 정지 / 복구 / 회수 | warn / success / danger | #029 |
OPERATOR_ISSUED … OPERATOR_REVOKED (5종) | 운영자 초대·재설정·정지·복구·회수 | info·muted·warn·success·danger | #032 |
TERMS_PUBLISHED / PRIVACY_POLICY_PUBLISHED | 약관 / 개인정보 게시 | playing | #033 |
TERMS_SCHEDULE_CANCELED / PRIVACY_POLICY_SCHEDULE_CANCELED | 약관 / 개인정보 예약 취소 | warn | #038 |
MUSIC_UPLOADED | 음원 업로드 | info | #041 |
MUSIC_FILE_REPLACED | 음원 파일 교체 | warn | #041 |
MUSIC_DELETED | 음원 소프트삭제 (deleted_at 채움, blob 유지) | danger | #042 |
TICKET_STATUS_CHANGED | 티켓 상태 변경 | info | #047 |
TICKET_ASSIGNED | 티켓 담당자 배정 / 해제 (단일 액션, detail 분기) | info | #047 |
TICKET_PRIORITY_CHANGED | 티켓 우선순위 변경 | warn | #047 |
HQ_ONBOARDED | 본사 온보딩 | playing | — |
대상 타입(
OperatorAuditItemTargetType):HQ·STORE·OPERATOR·TERMS·PRIVACY_POLICY·MUSIC(#041) ·TICKET(#047). 매장 정지/복구/폐점의 targetType =STORE, targetId = storeId, targetLabel = 매장명, detail = 사유(suspend/close). 음원 업로드/교체의 targetType =MUSIC, targetId = 음원 PK, targetLabel = 곡 제목 스냅샷, detail = 원본 파일명·크기(#041). 음원 소프트삭제(MUSIC_DELETED)의 targetType =MUSIC(재사용), targetId = 음원 PK, targetLabel = 곡 제목 스냅샷(#042). 티켓 상태/우선순위/담당자 변경의 targetType =TICKET, targetId = 티켓 PK, targetLabel = 티켓 title 스냅샷. detail 은 상태/우선순위이전→이후(예OPEN→IN_PROGRESS·HIGH→URGENT), 담당자배정: {email}(이메일) /해제(#047).
MusicSource (#059 — 음원 타입)
음원의 출처 타입. 업로드 시 지정·불변. 라이브러리 타입(libraryType)과 동일 도메인으로,
음원을 라이브러리에 담을 때 musicSource == libraryType 이어야 한다(불일치 시 400
LIBRARY_TYPE_MISMATCH). generated 노출 enum MusicListItemMusicSource / MusicResponseMusicSource
/ UploadMusicMusicSource / ListMusicMusicSource. FE 는 MUSIC_SOURCE_META 단일 소스로 라벨/톤을
매핑하며 라이브러리 타입 배지와 동일 표현(색만으로 구분하지 않고 라벨 병기).
| 값 | 의미 | FE StatusPill 톤 |
|---|---|---|
AI | AI 생성 음원 | info |
TRUST | 신탁 음원 | playing |
플랜(
PlanType)의 AI/TRUST 는 본사 구독 등급, 음원의musicSource는 개별 음원의 출처 타입으로 의미 레이어가 다르다(라벨만 공유). 라이브러리·플랜·음원의 AI/TRUST 표현은 톤/라벨을 일관 유지한다.
MusicTagOptionType (#132 — 음원 태그 옵션 타입)
음원 분류 태그 옵션(music_tag_option)의 타입. 생성 후 불변. 같은 타입 내 value 는 unique
(중복 시 409 MUSIC_TAG_OPTION_DUPLICATE). generated 노출 enum MusicTagOptionResponseType /
CreateMusicTagOptionRequestType / ListMusicTagOptionsType(모두 동일 2값). FE /settings/music-options
는 타입별로 2 섹션(장르·무드)을 분리해 렌더한다.
| 값 | 의미 |
|---|---|
GENRE | 장르 (예: 재즈·힙합·클래식) |
MOOD | 무드 (예: 차분한·신나는·잔잔한) |
라이브러리·플레이리스트 분류에 쓰일 태그 옵션의 마스터 데이터. 운영사가 CRUD 로 관리하며 삭제는 soft delete(
active=false). 음원 업로드 폼이 이 옵션을 소비(드롭다운)하는 연동은 후속.
PlaylistStatus (#060 — 플레이리스트 파생 상태)
플레이리스트의 파생(계산) 상태. DB 컬럼·마이그레이션 없는 응답 계산값으로, BE 단일 함수
derivePlaylistStatus(libraryCount, appliedStoreCount, isDefault)(운영사·본사 서비스 공유)가 집계로 판정한다.
한 PL 은 정확히 1개 상태이며 배타적 우선순위로 결정한다. generated 노출 enum PlaylistResponseStatus /
PlaylistListItemStatus / HqPlaylistListItemStatus / HqPlaylistDetailResponseStatus(모두 동일 4값).
FE 는 표시만 하며(서버 파생값 신뢰) playlist-status-meta.ts 단일 소스로 라벨/톤을 매핑한다(운영사·본사 일치·라벨 항상 병기).
| 우선순위 | 값 | 판정 조건 | FE 라벨 | FE StatusPill 톤 |
|---|---|---|---|---|
| 1 | EMPTY | libraryCount == 0 (라이브러리 미보유, 최우선) | 비어있음 | warn |
| (2) | (MISMATCH) | 플랜 불일치 — PL 상태 배지는 아직 미구현(후속). #122 는 점장 큐 게이팅만 도입 | — | — |
| 3 | FALLBACK | isDefault == true (본사 기본 PL·점장 큐 fallback 대상) | 기본 | info |
| 4 | ACTIVE | appliedStoreCount >= 1 (적용 매장 있음) | 사용 중 | success |
| 5 | UNUSED | 그 외 (libraryCount>0 && !isDefault && appliedStoreCount==0) | 미사용 | muted |
상태 모델은 5종 설계(EMPTY/MISMATCH/FALLBACK/ACTIVE/UNUSED)이나 PL 상태 배지는 비게이트 4종만 구현한다. SPEC #122(#060 F1)는 plan↔type 게이팅의 첫 구현이나, 운영사/본사 PL 화면의 MISMATCH 배지가 아니라 점장 재생 큐에서 plan 위반 음원을 서버가 제외하는 게이팅만 도입한다(effective plan=
store.plan ?? hq.planvslibrary.libraryType, plan null=무제약, 전부 위반→EMPTY_PLAYLIST— Store Player §“플랜 위반 음원 큐 제외”). PL 상태 배지로서의 MISMATCH(운영사/본사 편집 화면 경고)는 여전히 후속 — 도입 시 우선순위에서 EMPTY 다음·FALLBACK 앞에 들어갈 예정. FALLBACK status 는 기존isDefault“기본” 배지와 라벨이 겹치므로 FE 는 status 배지로 통합하고 별도 기본 배지를 숨긴다(status≠FALLBACK인 기본 PL 은 병치 — 상세는 Playlist · HQ Mode 플레이리스트).
TtsVoice (#061 — TTS 안내방송 voice)
본사 TTS 안내방송 합성에 쓰는 voice. 5종. BE TtsVoice enum 이 actor_id·model_version·표시명을
내부 보유하고, API 에는 enum name + 한글 표시명만 노출(actor_id 비노출). generated 노출 enum
CreateTtsAnnouncementRequestVoice / TtsAnnouncementListItemVoice / TtsAnnouncementDetailResponseVoice
(모두 동일 5값). 목록·상세는 BE voiceDisplayName(한글)을 단일 소스로 표시한다. 생성 폼만 합성
전이라 BE 표시명을 받을 수 없으므로 FE tts-voice-meta.ts 가 value→한글 라벨을 보유(라벨 어긋나도
합성은 enum value 로 진행 — 안전).
| 값 | FE 생성 폼 라벨(예시) |
|---|---|
SHEAN | 시안 (차분한 여성) |
WOOSUNG | 우성 (안정된 남성) |
CYRUS | 사이러스 (밝은 남성) |
AERAN | 애란 (따뜻한 여성) |
SEUNGA | 승아 (또렷한 여성) |
✅ voice별 tone preset 선택 — #063 도착(아래
TtsTonePreset). FE 생성 폼 라벨은 BETtsVoice.displayName과 정합을 유지한다(BE 가 응답voiceDisplayName의 단일 소스).
TtsTonePreset (#063 — TTS 안내방송 톤 프리셋)
본사 TTS 안내방송 합성의 감정 톤(Typecast emotion). 6종. voice별 지원 집합이 다르며 모든 voice 가
NORMAL(기본) 을 지원한다. BE TtsTonePreset enum 이 Typecast emotion 파라미터·표시명을 내부
보유하고, API 에는 enum name + 한글 표시명(*DisplayName)만 노출. generated 노출 enum
CreateTtsAnnouncementRequestTonePreset(@nullable) / UpdateTtsAnnouncementRequestTonePreset(@nullable) /
TtsAnnouncementListItemTonePreset / TtsAnnouncementDetailResponseTonePreset / TtsTonePresetOptionPreset
(모두 동일 6값). voice별 허용 매핑은 GET /api/v1/hq/tts-voices(TtsVoiceListResponse)가 단일 소스.
| 값 | FE 폴백 라벨(예시) |
|---|---|
NORMAL | 기본 |
HAPPY | 밝게 |
TONEUP | 톤 업 |
TONEDOWN | 톤 다운 |
ANGRY | 강하게 |
SAD | 차분하게 |
톤 표시명은 BE
tonePresetDisplayName(voices 응답·목록·상세)이 단일 소스다. FE 는 voices 응답이 아직/실패일 때의 폴백 라벨만tts-voice-meta.ts에 보유(NORMAL 만 노출). 생성·수정 폼은 선택 voice 의 허용 톤만 옵션으로 보이고, voice 미지원 톤은 UI 가 차단(혹시 도달 시 BE 400TTS_INVALID_TONE_PRESET).
DispatchRequestTarget (송출 슬라이스 — 안내방송 송출 대상)
본사 안내방송 송출(POST /api/v1/hq/announcements/{id}/dispatch) + 반복 예약
(POST /api/v1/hq/dispatch-schedules)의 대상 구분. 3종 (SPEC #144 REGION 추가). generated
DispatchRequestTarget(DispatchRequest.target) / CreateDispatchScheduleRequestTarget /
DispatchScheduleResponseTarget.
| 값 | 의미 |
|---|---|
ALL | 산하 매장 전체. storeIds·region 무시. (매장 0건이면 dispatchedCount=0) |
STORES | 지정 매장. storeIds 필수(≤1000, 모두 본인 본사 산하). 비어있거나 타 본사 → 400 DISPATCH_INVALID_TARGET |
REGION | (SPEC #144) 지정 지역(Region 시/도). region 필수(누락 시 400 DISPATCH_REGION_REQUIRED). service 가 hqId AND region=:region 매장으로 fan-out(해당 region 매장 0건이면 dispatchedCount=0) |
ALL/STORES/REGION 분기는 즉시 송출·반복 예약 양쪽에서 동일. FE 는 송출/예약 다이얼로그 target 라디오에 “지역” 옵션 + region select(시/도) 를 노출하고, target=REGION 인데 미선택이면 제출 disabled(BE 400 선검증). 상세는 HQ TTS 안내방송 송출.
Region (SPEC #144 — 매장 지역 시/도)
매장(Store.region, nullable)의 시/도 enum. REGION 모드 송출의 그룹핑 축. 17종. 본사가
매장 상세에서 태그하면 target=REGION 으로 같은 시/도 매장군에 일괄 송출한다. BE Region enum 이
displayName(한글)을 내부 보유하고, OpenAPI 에는 enum name 만 노출 → FE 는 한글 라벨 맵을
apps/space/src/lib/region.ts 에 단일 소스로 보유(generated 의 per-field region enum 17값을 value
소스로 재사용). generated 노출 enum: UpdateStoreRegionRequestRegion / HqStoreDetailResponseRegion /
DispatchRequestRegion / CreateDispatchScheduleRequestRegion / DispatchScheduleResponseRegion(모두
동일 17값, nullable).
| enum name | 라벨 | enum name | 라벨 | |
|---|---|---|---|---|
SEOUL | 서울 | GANGWON | 강원 | |
BUSAN | 부산 | CHUNGBUK | 충북 | |
DAEGU | 대구 | CHUNGNAM | 충남 | |
INCHEON | 인천 | JEONBUK | 전북 | |
GWANGJU | 광주 | JEONNAM | 전남 | |
DAEJEON | 대전 | GYEONGBUK | 경북 | |
ULSAN | 울산 | GYEONGNAM | 경남 | |
SEJONG | 세종 | JEJU | 제주 | |
GYEONGGI | 경기 |
Store.region이null(미지정)이면 지역 송출에 포함되지 않는다. 편집은 본사 매장 상세 매장 지역 편집 참고. 자동 주소 파싱 채움은 범위 밖(수동 선택).
DispatchStatus (송출 슬라이스 — 안내방송 송출 상태)
송출 row(announcement_dispatch.status)의 생명주기. 5종 (SPEC #077 CANCELED 추가 + SPEC #078 SCHEDULED 추가 + SPEC #143 MISSED 추가).
전이는 단방향 — SCHEDULED → PENDING → PLAYED(정상 경로) · SCHEDULED → CANCELED(예약 직접 취소) ·
PENDING → CANCELED(즉시/예약-전이된 PENDING 취소) · PENDING → MISSED(도래+grace 10분 초과 자동 만료, SPEC #143).
PLAYED·CANCELED·MISSED 는 종착 상태(이후 전이 없음 — 시간 되감기 X · 중복 취소 가드).
SCHEDULED ──┬──> PENDING ──┬──> PLAYED
│ ├──> MISSED (도래+grace 10분 초과·미재생)
└──> CANCELED <─┘| 값 | 의미 |
|---|---|
SCHEDULED | (SPEC #078) 예약 송출 row 의 초기 상태. 본사가 미래 scheduledAt 으로 송출 요청 시 적재됨. 백그라운드 디스패처(HqDispatchScheduler, @Scheduled(cron='0 * * * * *'))가 도래 시 원자 UPDATE(WHERE status='SCHEDULED' AND scheduled_at <= :now) 로 PENDING 전이. 점장 player 는 WHERE status='PENDING' 만 폴링하므로 SCHEDULED 는 자연 제외 |
PENDING | 송출 직후 기본값(즉시 송출) 또는 디스패처가 SCHEDULED 에서 전이한 상태. 점장 player 의 pending 폴링 대상(listStorePendingAnnouncements) |
PLAYED | 점장이 재생 완료 ack(ackStoreAnnouncement). BE 는 원자 조건부 UPDATE(WHERE status='PENDING') — 첫 ack 만 204, 중복 ack·이미 PLAYED·타 매장·미존재는 모두 404 DISPATCH_NOT_FOUND(상태·존재 은닉). 효과는 멱등(상태 불변)이나 응답은 404 |
CANCELED | 본사가 PENDING 또는 SCHEDULED(SPEC #078) dispatch 를 취소(cancelHqDispatch, SPEC #077·#078). 원자 조건부 UPDATE(WHERE id AND hq_id AND (status='PENDING' OR status='SCHEDULED')) — 1행 영향 시 204, 0행(이미 PLAYED·CANCELED·미존재·타 본사)은 모두 404 DISPATCH_NOT_FOUND(상태·존재 은닉). 점장 pending 조회는 WHERE status='PENDING' 만 매치하므로 CANCELED 자동 제외(별도 cleanup 0). 같은 트랜잭션에 본사 audit 1건(HQ_ANNOUNCEMENT_DISPATCH_CANCELED) 기록. 원격 즉시중단(revoke)도 CANCELED 로 전이(revokeHqDispatch, SPEC #077 확장 — PENDING-only + revoked_at set + audit HQ_DISPATCH_REVOKED, 점장 player 가 revokedDispatchIds 신호로 재생 중 멘트 즉시 중단) |
MISSED | (SPEC #143) PENDING 이 도래 시각 + grace(10분) 을 초과해도 재생 ack 가 없으면 디스패처가 자동 만료시킨 종착 상태. 백그라운드 디스패처가 원자 UPDATE(WHERE status='PENDING' AND created_at <= :now - grace)로 MISSED 전이(점장이 늦게 ack 하면 race 로 PLAYED 가 이기거나 404). 점장 pending 조회는 WHERE status='PENDING' 만 매치하므로 MISSED 자동 제외 — 만료 후엔 player 에 더 노출되지 않는다. 통계에선 송출 상태 분포에 집계되나 도달률 분모(PENDING+PLAYED)에선 제외(재생 시도분이 아닌 미재생 만료라). 별도 audit 없음(시스템 자동 만료) |
상세는 AnnouncementDispatch 테이블 · Store Player 삽입 재생 · 본사 송출 취소.
DispatchCalendarEventKind / DispatchCalendarEventStatus (송출 캘린더)
송출 캘린더(GET /api/v1/hq|store/dispatch-calendar, DispatchCalendarEvent)의 이벤트 분류 enum. 본사·점장 공용.
DispatchCalendarEventKind — is_emergency·is_hq_origin 에서 파생.
| 값 | 의미 |
|---|---|
EMERGENCY | 긴급 안내(최우선). FE 캘린더는 status 무관 danger 톤으로 강조 |
HQ_ANNOUNCEMENT | 본사 안내방송 송출 |
STORE_BROADCAST | 점장 매장 방송 송출 |
DispatchCalendarEventStatus — DispatchStatus 와 동일 4종(SCHEDULED → PENDING → {PLAYED, CANCELED} 단방향).
FE 캘린더 톤: SCHEDULED/PENDING=info, PLAYED=success, CANCELED=muted(취소선, “MISSED=muted” 의도 대응).
상세는 DispatchCalendarResponse DTO · 본사 캘린더 · 점장 캘린더.
DispatchScheduleFrequency (반복 송출 예약 — 빈도, #139 확장)
본사 반복 송출 예약(dispatch_schedule.frequency)의 빈도. 5종 (#139 — 시각형 HOURLY·EVEN_HOURS·
ODD_HOURS 추가, 기존 DAILY·WEEKLY 보존). generated CreateDispatchScheduleRequestFrequency /
DispatchScheduleResponseFrequency(동일 값). FE radiogroup·라벨(dispatch-schedule-meta.ts
FREQUENCY_LABELS)·타임테이블 전개(expandOperatingHours)가 이 enum 단일 소스를 재사용한다.
| 값 | 의미 | 연관 필드 |
|---|---|---|
DAILY | 매일 1회 | slotTime(HH:MM). byWeekday·startHour·endHour 무시 |
WEEKLY | 지정 요일마다 1회 | byWeekday(요일 CSV, 필수) + slotTime(HH:MM) |
HOURLY | 운영시간 매 정시마다 | startHour·endHour(0-23, start≤end, 필수) + slotTime 의 분(MM)만 매시 :MM 오프셋(시 무시) |
EVEN_HOURS | 운영시간 내 짝수 hour마다 | 위와 동일 |
ODD_HOURS | 운영시간 내 홀수 hour마다 | 위와 동일 |
시각형(HOURLY/EVEN_HOURS/ODD_HOURS)은
startHour·endHour(운영시간)가 필수이며slotTime의 시(HH)는 무시된다(분 MM만 매시 오프셋). 빈도↔요일·빈도↔운영시간 불일치·범위 위반은 400DISPATCH_SCHEDULE_INVALID. 백그라운드 디스패처가 시각형이면 하루 N건(운영시간 hour마다)으로 전개한다. 상세는 반복 송출 예약.
TtsAnnouncementSource (즉시방송 슬라이스 — 안내방송 출처)
tts_announcement.source(V24)의 출처 구분. 2종. 같은 테이블을 본사 안내방송과 점장 즉시방송이
공유하되 source 로 격리한다.
| 값 | 의미 |
|---|---|
HQ | 본사(HQ_MANAGER)가 만든 안내방송. 기존 행 backfill 기본값. 본사 화면(목록·상세·수정·삭제·dispatch)이 다루는 유일한 source |
STORE_BROADCAST | 점장(STORE_MANAGER)이 즉시방송 미리듣기(previewStoreBroadcast)로 만든 draft. created_by_store_id 채워짐. send 로 본인 매장에만 dispatch |
보안 불변식 — 본사-facing 쿼리는 모두
source='HQ'로 격리해 STORE_BROADCAST row 가 본사 화면·계약에 노출되지 않는다. 점장 즉시방송 흐름은 점장 즉시방송 · TtsAnnouncement 테이블.
HqAuditAction (#067 도입 · #068 확장 — 본사 감사 액션)
hq_audit_log.action(V27)의 본사(HQ_MANAGER) 액션 enum. 현재 4종(SPEC #068 에서 라이프사이클
3종 추가). generated FE 도메인은 HqAuditItemAction·ListHqAuditDispatchesAction(value 동일).
| 값 | 의미 |
|---|---|
HQ_ANNOUNCEMENT_DISPATCHED | 본사 안내방송 송출. HqAnnouncementDispatchService.dispatch 트랜잭션 내 1행 기록 — actor·시각·target=ALL|STORES·count·storeIds(앞 10개) 스냅샷 |
HQ_ANNOUNCEMENT_CREATED | 본사 안내방송 생성. HqTtsAnnouncementService.create 트랜잭션 내 1행 — title·voice·tonePreset·durationSeconds(null = “null” 명시) 스냅샷 |
HQ_ANNOUNCEMENT_UPDATED | 본사 안내방송 수정. HqTtsAnnouncementService.update — title·voice·tonePreset·resynthesized(text/voice/tonePreset 변경 시 true) 스냅샷. no-op(모든 필드 동일) 시 audit 미기록(early return) |
HQ_ANNOUNCEMENT_DELETED | 본사 안내방송 soft-delete. HqTtsAnnouncementQueryService.delete — title 스냅샷(삭제된 안내방송 식별) |
HQ_ANNOUNCEMENT_DISPATCH_CANCELED | 본사 송출 취소(SPEC #077). cancelHqDispatch 트랜잭션 내 1행 — PENDING/SCHEDULED dispatch CANCELED 전이 |
HQ_DISPATCH_REVOKED | 본사 송출 원격 즉시중단(SPEC #077 확장). revokeHqDispatch 트랜잭션 내 1행 — PENDING dispatch CANCELED 전이 + revoked_at set. cancel(재생 전 무효화)과 별도 audit 액션으로 운영 의도 구분(revoke=재생 중/직전 best-effort 중단, 점장 player 가 revokedDispatchIds 신호로 즉시 멈춤) |
HQ_STORE_REGION_UPDATED | 본사 매장 지역(시/도) 변경(SPEC #144). updateStoreRegion 트랜잭션 내 1행 — targetType=STORE, before/after region 스냅샷. FE audit 뷰 라벨 “매장 지역 변경”(info 톤) |
generated 도메인(
HqAuditItemAction·ListHqAuditDispatchesAction)에는 추가 액션(ticket·store·반복 예약 등)도 포함된다 — 위는 안내방송 송출 라이프사이클 핵심 액션 + #144 지역 변경. Followups: 점장 즉시방송 audit 은 별도 enum/테이블(F3 —store_audit_log). 상세는 HqAuditLog · HQ Mode 감사.
HqAuditActorRole (#067 — 본사 감사 행위자 역할)
hq_audit_log.actor_role(V27). 2종. impersonate 컨텍스트 구분 — impersonated_by_operator_id
컬럼과 정합성을 CHECK 제약으로 강제한다((actor_role='OPERATOR_IMPERSONATING') = (impersonated_by_operator_id IS NOT NULL)).
generated FE 도메인은 HqAuditItemActorRole(value 동일).
| 값 | 의미 |
|---|---|
HQ_MANAGER | 본사 매니저 직접 송출(임퍼소네이션 아님). impersonated_by_* 컬럼 null |
OPERATOR_IMPERSONATING | 운영자(principal.impersonatedBy != null)가 본사로 위장해 송출. impersonated_by_operator_id/email 동시 기록 — 원본 운영자와 위장 본사 매니저 둘 다 추적 |
impersonate 미러 —
principal.accountId= HQ_MANAGER 계정(actor_account_id),principal.impersonatedBy= 원본 운영자(impersonated_by_operator_id). 둘 다 lookup 실패 시 IllegalStateException → 액션 롤백. 상세는 HqAuditLog.
HqAuditTargetType (#067 — 본사 감사 대상 유형)
hq_audit_log.target_type(V27)의 대상 유형 enum. 현재 1종(후속 SPEC 으로 확장). generated FE
도메인은 HqAuditItemTargetType(value 동일).
| 값 | 의미 |
|---|---|
TTS_ANNOUNCEMENT | 본사 TTS 안내방송(tts_announcement.id). target_id=안내방송 id, target_label=제목 스냅샷 |
Followups: 후속 액션 확장에 따라 대상 유형도 추가 가능(예: announcement create/update/delete 도 같은 유형). 상세는 HqAuditLog · HQ Mode 감사.
StoreAuditAction (#114 — 매장 감사 액션)
store_audit_log.action(V36)의 점장(STORE_MANAGER) 액션 enum. 현재 8종(HqAudit 처럼 점진 확장 —
새 점장 액션은 VARCHAR(48) 영속이라 마이그레이션 무관). HqAuditAction·
OperatorAuditAction 과 의미가 분리된 별도 enum
(actor·테넌트 스코프·UI 권한 모델이 다름). v1 은 기록만이라 generated FE 도메인 노출은 조회 view 후속에서.
| 값 | 의미 |
|---|---|
STORE_TICKET_CREATED | 점장 CS 티켓 생성 (#112 audit tail 마감). StoreTicketService.createStoreTicket — target_id=ticket.id·target_label=ticket.title·detail=title=...·category=NAME·bodyLength=N 스냅샷 |
STORE_TICKET_COMMENT_ADDED | 점장 CS 티켓 댓글 추가 (#112). StoreTicketService.addStoreTicketComment — target_id=ticket.id(댓글 id 아님 — audit 는 ticket scope)·detail=bodyLength=N |
STORE_TICKET_CLOSED | 점장 CS 티켓 확인 종료 RESOLVED→CLOSED (#121). StoreTicketService.closeStoreTicket — target_type=SUPPORT_TICKET·target_id=ticket.id·target_label=ticket.title 스냅샷. store_id 격리 원자 UPDATE affected=1 확정 시에만 기록(409/404 분기 후) |
STORE_PROFILE_UPDATED | 점장 본인 매니저 계정 name 편집 (#087 F4 마감). StoreMeService.updateMe — target_type=STORE_MANAGER·detail=changedFields=name·name:before->after. 변경 0(멱등 no-op) 시 audit 미기록 |
STORE_PASSWORD_CHANGED | 점장 본인 비밀번호 변경 (#100 F2 마감). 공용 AuthService.changePassword role=STORE_MANAGER 분기 — target_type=STORE_MANAGER·detail 없음(보안 — 비밀번호·길이 미기록)·임퍼소네이션 차단되어 actor 항상 본인 |
STORE_DISPATCH_CANCELED | 점장 본인 매장 예약 송출 취소 (#092 F2 마감). StoreBroadcastService — SCHEDULED dispatch 를 CANCELED 로 단방향 전이·target_type=DISPATCH·detail 없음(원자 UPDATE affected=1 확정 시에만 기록) |
STORE_ACTIVE_PLAYLIST_CHANGED | 점장 본인 매장 활성 PL 선택·해제 (#129). 활성 PL 지정(playlistId) 또는 본사 기본 fallback(null) 로 실제 변경 시·target_type=PLAYLIST. 기존 운영사/본사 활성 PL 지정 로직 재사용 |
STORE_DISPATCH_BROADCAST_NOW | 점장 예약 송출 즉시 방송 (#142). StoreBroadcastService — SCHEDULED dispatch 의 안내방송으로 새 IMMEDIATE dispatch 1건 생성(원본 SCHEDULED 무변경)·target_type=DISPATCH·target_id=새 IMMEDIATE dispatch.id·detail = 원본 SCHEDULED dispatch.id 스냅샷(sourceScheduledDispatchId) — 즉시 방송이 어느 예약본에서 비롯됐는지 역추적용 |
Followups: 점장 액션 도착 시 enum 확장. v1 은 기록만 — 조회 view(운영자/본사) 는 후속(D5). 상세는 StoreAuditLog · Store 감사.
StoreAuditActorRole (#114 — 매장 감사 행위자 역할)
store_audit_log.actor_role(V36). 2종. HqAuditActorRole
미러 — impersonate 컨텍스트를 구분하고 impersonated_by_operator_id 컬럼과 정합성을 CHECK 제약으로 강제한다
((actor_role='OPERATOR_IMPERSONATING') = (impersonated_by_operator_id IS NOT NULL)).
| 값 | 의미 |
|---|---|
STORE_MANAGER | 점장 본인이 직접 액션(임퍼소네이션 아님). impersonated_by_* 컬럼 null |
OPERATOR_IMPERSONATING | 운영자(principal.impersonatedBy != null)가 점장 모드로 위장해 액션. impersonated_by_operator_id/email 동시 기록 — 원본 운영자와 위장 점장 둘 다 추적 |
impersonate 미러 —
principal.accountId= 점장 계정(actor_account_id),principal.impersonatedBy= 원본 운영자(impersonated_by_operator_id). 둘 다 lookup 실패 시 IllegalStateException → 액션 롤백. 상세는 StoreAuditLog.
StoreAuditTargetType (#114 — 매장 감사 대상 유형)
store_audit_log.target_type(V36)의 대상 유형 enum. 현재 4종(ASCII 사전순). 후속 액션 확장에 따라 추가 가능.
| 값 | 의미 |
|---|---|
DISPATCH | 예약 송출(announcement_dispatch.id). STORE_DISPATCH_CANCELED·STORE_DISPATCH_BROADCAST_NOW(#142) 의 대상 |
PLAYLIST | 플레이리스트(playlist.id). STORE_ACTIVE_PLAYLIST_CHANGED(#129) 의 대상 |
STORE_MANAGER | 점장 본인 매니저 계정(self, 점장 계정 id). STORE_PROFILE_UPDATED·STORE_PASSWORD_CHANGED 의 대상 |
SUPPORT_TICKET | CS 티켓(ticket.id·target_label=title 스냅샷). STORE_TICKET_CREATED·STORE_TICKET_COMMENT_ADDED 의 대상 |
상세는 StoreAuditLog · Store 감사.
OpenAPI 동기화
backend enum 변경 시 pnpm sync-api 로 packages/api-client/src/generated/ 의 TypeScript union /
const enum 자동 갱신. 수기 정의 금지 (frontend [[feedback-15]] 정합).
References
- SPEC #002 §2-3 (HqType · StoreType · PlanType · StoreStatus)
- SPEC #003 §2-1 (Role · AccountStatus)
- SPEC #005 (ImpersonationToken)
linkmusic-msa-space-was/src/main/kotlin/.../domain/enum/linkmusic-frontend-space/packages/api-client/src/generated/schemas/