DomainStatus Lifecycle

Status Lifecycle (HqStatus · AccountStatus · StoreStatus)

SPEC #002·#003 정합 + 후속 SPEC 예고.

Overview

도메인 entity 의 lifecycle. v1 도입 분 + 후속 SPEC 분을 모두 명시.

AccountStatus

전이트리거효과v1 구현
→ ACTIVE가입 APIdefault
ACTIVE → SUSPENDED운영사 액션즉시 세션 무효화 + 로그인 차단✅ STORE_MANAGER (#029) · OPERATOR (#032) · HQ_MANAGER 는 HqStatus 경유(#018/#024)
SUSPENDED → ACTIVE운영사 액션로그인 가능✅ STORE_MANAGER (#029) · OPERATOR (#032)
ACTIVE → WITHDRAWN운영사 회수 / 탈퇴soft-delete · 재로그인 차단 · 이메일 재사용 불가 (terminal)✅ STORE_MANAGER 회수 (#029) · OPERATOR 회수 (#032)
SUSPENDED → WITHDRAWN운영사 회수 / 탈퇴위와 동일 (terminal)✅ STORE_MANAGER 회수 (#029) · OPERATOR 회수 (#032)

STORE_MANAGER 점장 계정 라이프사이클은 SPEC #029 로 도입 — ACTIVE ↔ SUSPENDED, ACTIVE/SUSPENDED → WITHDRAWN(terminal). 운영자 백오피스 /stores 관리 다이얼로그(비밀번호 재설정[ACTIVE]·정지·복구·회수)에서 수행하며, 잘못된 전이는 409 ACCOUNT_INVALID_STATUS_TRANSITION.

OPERATOR 운영자 계정 라이프사이클은 SPEC #032 로 도입 — 점장과 동일한 ACTIVE ↔ SUSPENDED, ACTIVE/SUSPENDED → WITHDRAWN(terminal) + 초대(생성, passwordMustChange=true)·비밀번호 재설정 [ACTIVE]. 운영자 백오피스 /users 에서 수행한다. 점장과 다른 두 가지 가드: ① self-guard — 본인 계정 정지·재설정·회수 금지(403 OPERATOR_SELF_ACTION_FORBIDDEN, FE 도 isSelf 행 액션 비활성). ② 마지막 활성 운영자 보호 — 시스템에 남은 마지막 ACTIVE 운영자는 정지·회수 불가 (403 OPERATOR_LAST_ACTIVE_FORBIDDEN). 정지·재설정·회수는 대상 활성 세션을 즉시 무효화한다.

모든 전이는 공통 감사 로그(OperatorAuditLog)에 기록(#026/#029/#032 — OperatorAuditActionOPERATOR_* 5종 추가). HQ_MANAGER 계정 상태는 본사 HqStatus 전이(#018)를 통해 간접 제어된다. 일반 탈퇴(self-service)·90일 보관 정책은 후속.

StoreStatus (정지/복구 전이 도입 완료)

폐점(closedAt)은 status 와 독립된 별도 terminal 축이다 — StoreStatus enum 에 CLOSED 를 추가하지 않고, store.closed_at 타임스탬프로 표현한다(폐점 판정 = closedAt != null). status 분기·CHECK 제약·FE STATUS_META 파급을 회피하기 위함(SPEC #039 결정).

전이트리거v1 구현
→ ACTIVEINSERT (Flyway 시드 / 등록 API)
ACTIVE | INACTIVE → SUSPENDED운영사 액션✅ SPEC #037
SUSPENDED → ACTIVE운영사 복구 액션✅ SPEC #037
ACTIVE → INACTIVE90일 자동 (스케줄러)❌ 후속 (FR-11.10)
폐점 (terminal)closedAt 채움 (운영사 액션)✅ SPEC #039

정지/복구 전이 — 도입 완료 (SPEC #037):

동작endpoint허용 전이부수 효과
정지POST /api/v1/admin/stores/{storeId}/suspendACTIVE | INACTIVE → SUSPENDED소속 STORE_MANAGER active RefreshToken 즉시 revoke(세션 무효화) + 이후 로그인/refresh 차단(AUTH_STORE_SUSPENDED)
복구POST /api/v1/admin/stores/{storeId}/reactivateSUSPENDED → ACTIVE소속 점장 로그인 재허용
  • 그 외(같은 상태·역행) → 409 STORE_INVALID_STATUS_TRANSITION, 미존재 → 404 STORE_NOT_FOUND.
  • INACTIVE(휴면)도 정지 가능 — 운영 차단은 status 무관.
  • 정지 사유(reason, NotBlank·max255)는 store.suspension_reason 컬럼에 영속(복구 시 null clear)되고 감사 로그에 기록된다. 가상 본사 산하 매장도 전이 허용. OPERATOR-only.
  • 운영자 백오피스 /stores/{id} 매장 상세 헤더의 status 기반 정지/복구 2-step 확인 다이얼로그에서 수행한다.

폐점(closedAt) — 도입 완료 (SPEC #039):

동작endpoint가드부수 효과
폐점POST /api/v1/admin/stores/{storeId}/closeclosed_at IS NULL 인 매장만 (원자적 UPDATE WHERE)소속 STORE_MANAGER active RefreshToken 즉시 revoke + 이후 로그인/refresh 영구 차단(AUTH_STORE_CLOSED)
  • 폐점은 비가역(terminal) — 재폐점 → 409 STORE_ALREADY_CLOSED, 미존재 → 404 STORE_NOT_FOUND. 폐점 복구(reopen)는 비목표(후속).
  • 폐점 사유(reason, NotBlank·max255) 필수 → 감사 로그(detail)에 기록(closed_at 은 시각만 보관, 사유는 감사가 단일 보존처).
  • status 는 변경하지 않는다(closed_at 만 채움). suspend/reactivate WHERE 에 AND closed_at IS NULL 가드가 추가돼 폐점 매장 정지/복구는 DB 레벨에서 차단(affected 0 → 409)된다.
  • 운영자 백오피스 /stores/{id} 매장 상세 헤더의 폐점 2-step 확인 다이얼로그(비가역 경고 + 사유 입력 필수)에서 수행한다. 폐점 매장은 헤더에 “폐점됨” 배지를 노출하고 정지/복구/폐점 액션을 모두 숨긴다.

HqStatus (enum 도입 + 정지/복구 전이 도입 완료)

Hq entity 에 status 컬럼 도입됨 (SPEC #013, BE v0.7.0, Flyway V7) — 4 종 enum (ACTIVE / ONBOARDING / UNPAID / SUSPENDED), 신규 본사 default ONBOARDING, 가상 본사 (IndependentHqIds.SINGLETON) 는 ACTIVE 고정. API 레벨에서 HqAdminListItem.status 로 노출 + /hq 화면 탭 5종 (전체·활성·온보딩 중·미납·정지) 가 client-side 필터링.

정지/복구 전이 — 도입 완료 (SPEC #018, BE v0.11.0):

전이endpoint규칙효과
정지POST /api/v1/admin/hq/{id}/suspendACTIVE | ONBOARDING | UNPAID → SUSPENDED해당 본사 관리자(HQ_MANAGER) active RefreshToken 즉시 revoke(세션 무효화) + 이후 로그인/refresh 차단
복구POST /api/v1/admin/hq/{id}/reactivateSUSPENDED → ACTIVE로그인 재허용
  • 원자적 UPDATE(WHERE id=:id AND status IN(...)) + affected-row 검증으로 race/TOCTOU 방지. 전이 불가(이미 같은 상태 등) → 409 HQ_INVALID_STATUS_TRANSITION.
  • INDEPENDENT 가상 본사는 정지 불가403 INDEPENDENT_HQ_SUSPENSION_FORBIDDEN (시스템 단일·항시 ACTIVE).
  • OPERATOR-only. 정지 사유(reason) 저장은 v1 미포함(후속).

아직 미구현 — 후속 SPEC:

  • 전이 규칙 자동화 (ONBOARDING→ACTIVE 트리거·UNPAID 결제 실패 자동 전환)
  • WITHDRAWN(탈퇴/soft-delete) 전이·정지 사유 저장

lifecycle (handoff 10-surface §3 참고):

영향:

  • SUSPENDED 본사 — 임퍼소네이션 confirm dialog 비활성 + 사유 표시 (handoff §3). 본사 관리자 로그인 차단·진행 중 세션 무효화.
  • INDEPENDENT 가상 본사 — type 분기로만 차단 (status 무관). 정지/복구 액션 자체를 숨김.

TicketStatus (#027 — CS 티켓 v1, 전이 도입 완료)

CS 티켓의 처리 상태. 생성 직후 OPEN. 전이는 PATCH /api/v1/admin/tickets/{id}/status.

전이규칙
착수OPEN → IN_PROGRESS
해결IN_PROGRESS → RESOLVED
종결RESOLVED → CLOSED
재오픈RESOLVED → IN_PROGRESS · CLOSED → IN_PROGRESS
  • 그 외 전이는 backend 가 409 TICKET_INVALID_STATUS_TRANSITION 으로 거부(서버 가드). FE 는 현재 status 의 유효 전이만 컨트롤로 노출(ticket-meta.ts validTransitions — 클라 가드, 이중 방어).
  • 409 는 토스트/inline 에러로 처리하고 목록 새로고침을 유도한다.

우선순위(TicketPriority)는 생성 시점 고정 — v1 변경 endpoint 없음(후속 SPEC). 담당자는 PATCH /tickets/{id}/assignee 로 배정/해제(상태머신과 무관).

ContractPlan lifecycle (후속 SPEC)

DRAFT → ACTIVE (계약 발효) → EXPIRED (만료) | TERMINATED (조기 해지)

활성 협약 중복 차단 — 같은 본사·매장 활성 협약 2개 차단 (FR-20.7).

TermsDocument · PrivacyPolicy lifecycle (#033 — effectiveAt + status)

게시(즉시: effectiveAt<=now)  → status=ACTIVE      (직전 유효본 → SUPERSEDED)
게시(예약: effectiveAt>now)   → status=SCHEDULED   ──(발효 시각 도달)──▶ 읽기 계산으로 자동 유효본

LegalDocumentStatus enum: SCHEDULEDACTIVESUPERSEDED.

  • stale 주의: 스케줄러가 없어 status 는 게시 시점 스냅샷이다. 예약본(SCHEDULED)이 발효 시각을 지나도 컬럼은 SCHEDULED 로 남을 수 있다. “현재 유효본” 판정은 항상 effective_at <= now 중 최신(effectiveAt DESC)으로 한다 — status 는 이력 표시·필터 보조값. /active 응답의 status 는 항상 ACTIVE 로 보정.
  • soft-delete 안 함. 과거 버전 영구 보존(별도 history 테이블 없음). is_active 는 레거시 컬럼(점진 마이그레이션 — 완전 제거는 follow-up).
  • partial-unique(idx_*_active)는 예약본 공존을 위해 #033 에서 드롭. version 유일(uq_*_version) 유지.

Decisions

  • HqStatus 도입을 미룬 이유 — SPEC #002 는 schema 단계. status 사용처 (UI · 임퍼소네이션 차단 · 정산 차단) 가 명확해진 시점에 add. lifecycle 변경은 PR 한 번에 모든 호출부 함께 갱신해야 안전.

Roadmap

  • HqStatus 도입 (SPEC #013) ✅ · 정지/복구 전이 + 세션 무효화 (SPEC #018)
  • StoreStatus 정지/복구 전이 + 세션 무효화 + 정지 사유 영속 (SPEC #037)
  • 매장 폐점(closedAt) 액션 + 세션 무효화 + 인증 차단 (SPEC #039)
  • HqStatus 자동 전환 (ONBOARDING→ACTIVE·UNPAID 결제 실패) · 정지 사유 저장
  • StoreStatus 자동 전환 스케줄러 (90일 INACTIVE) · 폐점 복구(reopen)
  • AccountStatus.SUSPENDED 즉시 세션 무효화
  • ContractPlan · BillingKey · Invoice lifecycle

References

  • SPEC #002 §2-3·§2-9
  • SPEC #003 §2-1 (AccountStatus)
  • handoff 10-surface-ops-backoffice.md §3·§6