docs: sam_stat Swagger API 문서 추가 (Phase 6)

- StatApi.php: Stats 태그, 4개 엔드포인트 Swagger 정의
  - GET /stats/summary - 대시보드 통계 요약
  - GET /stats/daily - 도메인별 일간 통계
  - GET /stats/monthly - 도메인별 월간 통계
  - GET /stats/alerts - 통계 알림 목록
- 스키마: StatSalesDaily, StatFinanceDaily, StatDashboardSummary, StatAlert

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-30 09:23:51 +09:00
parent ca2dd44567
commit 0fbd080875

273
app/Swagger/v1/StatApi.php Normal file
View File

@@ -0,0 +1,273 @@
<?php
namespace App\Swagger\v1;
/**
* @OA\Tag(name="Stats", description="통계 (sam_stat DB 기반)")
*
* @OA\Schema(
* schema="StatSalesDaily",
* type="object",
* description="일간 매출 통계",
*
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="tenant_id", type="integer", example=287),
* @OA\Property(property="stat_date", type="string", format="date", example="2026-01-28"),
* @OA\Property(property="order_count", type="integer", example=5, description="수주 건수"),
* @OA\Property(property="order_amount", type="number", format="float", example=15000000, description="수주 금액"),
* @OA\Property(property="sales_count", type="integer", example=3, description="매출 건수"),
* @OA\Property(property="sales_amount", type="number", format="float", example=12000000, description="매출 금액"),
* @OA\Property(property="sales_tax_amount", type="number", format="float", example=1200000, description="매출 세액"),
* @OA\Property(property="new_client_count", type="integer", example=1, description="신규 고객 수"),
* @OA\Property(property="active_client_count", type="integer", example=4, description="활성 고객 수"),
* @OA\Property(property="shipment_count", type="integer", example=2, description="출하 건수"),
* @OA\Property(property="shipment_amount", type="number", format="float", example=500000, description="출하 금액")
* )
*
* @OA\Schema(
* schema="StatFinanceDaily",
* type="object",
* description="일간 재무 통계",
*
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="tenant_id", type="integer", example=287),
* @OA\Property(property="stat_date", type="string", format="date", example="2026-01-28"),
* @OA\Property(property="deposit_count", type="integer", example=3, description="입금 건수"),
* @OA\Property(property="deposit_amount", type="number", format="float", example=5000000, description="입금 금액"),
* @OA\Property(property="withdrawal_count", type="integer", example=5, description="출금 건수"),
* @OA\Property(property="withdrawal_amount", type="number", format="float", example=3000000, description="출금 금액"),
* @OA\Property(property="net_cashflow", type="number", format="float", example=2000000, description="순 현금 흐름"),
* @OA\Property(property="purchase_count", type="integer", example=2, description="매입 건수"),
* @OA\Property(property="purchase_amount", type="number", format="float", example=8000000, description="매입 금액"),
* @OA\Property(property="bank_balance_total", type="number", format="float", example=150000000, description="은행 잔액 합계")
* )
*
* @OA\Schema(
* schema="StatDashboardSummary",
* type="object",
* description="대시보드 통계 요약 (sam_stat 기반)",
*
* @OA\Property(property="sales_today", ref="#/components/schemas/StatSalesDaily", nullable=true, description="오늘 매출 통계"),
* @OA\Property(property="finance_today", ref="#/components/schemas/StatFinanceDaily", nullable=true, description="오늘 재무 통계"),
* @OA\Property(property="production_today", type="object", nullable=true, description="오늘 생산 통계"),
* @OA\Property(property="sales_monthly", type="object", nullable=true, description="이번 달 매출 요약"),
* @OA\Property(property="alerts", type="array", description="미읽은/미해결 알림 (최대 10건)",
*
* @OA\Items(ref="#/components/schemas/StatAlert")
* )
* )
*
* @OA\Schema(
* schema="StatAlert",
* type="object",
* description="통계 알림",
*
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="tenant_id", type="integer", example=287),
* @OA\Property(property="domain", type="string", example="sales", description="도메인"),
* @OA\Property(property="alert_type", type="string", example="aggregation_failure", description="알림 유형"),
* @OA\Property(property="severity", type="string", enum={"info","warning","critical"}, example="critical", description="심각도"),
* @OA\Property(property="title", type="string", example="[sales_daily] 집계 실패", description="제목"),
* @OA\Property(property="message", type="string", example="오류 상세 메시지", description="상세 메시지"),
* @OA\Property(property="current_value", type="number", format="float", nullable=true, example=0, description="현재 값"),
* @OA\Property(property="threshold_value", type="number", format="float", nullable=true, example=1, description="기준 값"),
* @OA\Property(property="is_read", type="boolean", example=false, description="읽음 여부"),
* @OA\Property(property="is_resolved", type="boolean", example=false, description="해결 여부"),
* @OA\Property(property="resolved_at", type="string", format="date-time", nullable=true, description="해결 일시"),
* @OA\Property(property="created_at", type="string", format="date-time", example="2026-01-28T10:00:00Z", description="생성 일시")
* )
*/
class StatApi
{
/**
* @OA\Get(
* path="/api/v1/stats/summary",
* tags={"Stats"},
* summary="대시보드 통계 요약",
* description="sam_stat DB 기반 대시보드 요약 통계를 반환합니다. 오늘의 매출/재무/생산 통계, 이번 달 매출 요약, 미해결 알림을 포함합니다. Redis 캐싱 적용 (TTL 5분).",
* operationId="getStatSummary",
* security={{"ApiKeyAuth":{}}, {"BearerAuth":{}}},
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="데이터를 조회했습니다."),
* @OA\Property(property="data", ref="#/components/schemas/StatDashboardSummary")
* )
* ),
*
* @OA\Response(response=401, description="인증 실패")
* )
*/
public function summary() {}
/**
* @OA\Get(
* path="/api/v1/stats/daily",
* tags={"Stats"},
* summary="도메인별 일간 통계 조회",
* description="지정한 도메인의 일간 통계를 기간별로 조회합니다. 7개 도메인 지원: sales, finance, production, inventory, quote, hr, system. Redis 캐싱 적용 (TTL 5분).",
* operationId="getStatDaily",
* security={{"ApiKeyAuth":{}}, {"BearerAuth":{}}},
*
* @OA\Parameter(
* name="domain",
* in="query",
* required=true,
* description="통계 도메인",
*
* @OA\Schema(type="string", enum={"sales","finance","production","inventory","quote","hr","system"})
* ),
*
* @OA\Parameter(
* name="start_date",
* in="query",
* required=true,
* description="시작 날짜",
*
* @OA\Schema(type="string", format="date", example="2026-01-01")
* ),
*
* @OA\Parameter(
* name="end_date",
* in="query",
* required=true,
* description="종료 날짜 (start_date 이후)",
*
* @OA\Schema(type="string", format="date", example="2026-01-31")
* ),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="데이터를 조회했습니다."),
* @OA\Property(property="data", type="array",
*
* @OA\Items(type="object", description="도메인별 일간 통계 레코드 (스키마는 도메인에 따라 다름)")
* )
* )
* ),
*
* @OA\Response(response=401, description="인증 실패"),
* @OA\Response(response=422, description="유효성 검증 실패")
* )
*/
public function daily() {}
/**
* @OA\Get(
* path="/api/v1/stats/monthly",
* tags={"Stats"},
* summary="도메인별 월간 통계 조회",
* description="지정한 도메인의 월간 통계를 조회합니다. 4개 도메인 지원: sales, finance, production, project. Redis 캐싱 적용 (TTL 5분).",
* operationId="getStatMonthly",
* security={{"ApiKeyAuth":{}}, {"BearerAuth":{}}},
*
* @OA\Parameter(
* name="domain",
* in="query",
* required=true,
* description="통계 도메인",
*
* @OA\Schema(type="string", enum={"sales","finance","production","project"})
* ),
*
* @OA\Parameter(
* name="year",
* in="query",
* required=true,
* description="조회 연도",
*
* @OA\Schema(type="integer", minimum=2020, maximum=2099, example=2026)
* ),
*
* @OA\Parameter(
* name="month",
* in="query",
* required=false,
* description="조회 월 (미지정 시 연간 전체)",
*
* @OA\Schema(type="integer", minimum=1, maximum=12, example=1)
* ),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="데이터를 조회했습니다."),
* @OA\Property(property="data", type="array",
*
* @OA\Items(type="object", description="도메인별 월간 통계 레코드 (스키마는 도메인에 따라 다름)")
* )
* )
* ),
*
* @OA\Response(response=401, description="인증 실패"),
* @OA\Response(response=422, description="유효성 검증 실패")
* )
*/
public function monthly() {}
/**
* @OA\Get(
* path="/api/v1/stats/alerts",
* tags={"Stats"},
* summary="통계 알림 목록 조회",
* description="통계 시스템에서 발생한 알림을 조회합니다. 집계 실패, 데이터 누락, 정합성 불일치 등의 알림이 포함됩니다.",
* operationId="getStatAlerts",
* security={{"ApiKeyAuth":{}}, {"BearerAuth":{}}},
*
* @OA\Parameter(
* name="limit",
* in="query",
* required=false,
* description="조회 건수 (기본 20)",
*
* @OA\Schema(type="integer", minimum=1, maximum=100, example=20)
* ),
*
* @OA\Parameter(
* name="unread_only",
* in="query",
* required=false,
* description="미읽은 알림만 조회",
*
* @OA\Schema(type="boolean", example=true)
* ),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="데이터를 조회했습니다."),
* @OA\Property(property="data", type="array",
*
* @OA\Items(ref="#/components/schemas/StatAlert")
* )
* )
* ),
*
* @OA\Response(response=401, description="인증 실패"),
* @OA\Response(response=422, description="유효성 검증 실패")
* )
*/
public function alerts() {}
}