feat: Item Master 하이브리드 구조 전환 및 독립 API 추가
- CASCADE FK → 독립 엔티티 + entity_relationships 링크 테이블 - 독립 API 10개 추가 (섹션/필드/BOM CRUD, clone, usage) - SectionTemplate 모델 제거 → ItemSection.is_template 통합 - 페이지-섹션, 섹션-필드, 섹션-BOM 링크/언링크 API 14개 추가 - Swagger 문서 업데이트
This commit is contained in:
@@ -35,10 +35,14 @@
|
||||
*
|
||||
* @OA\Property(property="id", type="integer", example=1),
|
||||
* @OA\Property(property="tenant_id", type="integer", example=1),
|
||||
* @OA\Property(property="page_id", type="integer", example=1),
|
||||
* @OA\Property(property="group_id", type="integer", nullable=true, example=1),
|
||||
* @OA\Property(property="page_id", type="integer", nullable=true, example=1),
|
||||
* @OA\Property(property="title", type="string", example="제품 상세"),
|
||||
* @OA\Property(property="type", type="string", enum={"fields","bom"}, example="fields"),
|
||||
* @OA\Property(property="order_no", type="integer", example=0),
|
||||
* @OA\Property(property="is_template", type="boolean", example=false, description="템플릿 여부"),
|
||||
* @OA\Property(property="is_default", type="boolean", example=false, description="기본 템플릿 여부"),
|
||||
* @OA\Property(property="description", type="string", nullable=true, example="섹션 설명"),
|
||||
* @OA\Property(property="created_at", type="string", example="2025-11-20 10:00:00"),
|
||||
* @OA\Property(property="updated_at", type="string", example="2025-11-20 10:00:00"),
|
||||
* @OA\Property(
|
||||
@@ -188,6 +192,73 @@
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="IndependentSectionStoreRequest",
|
||||
* type="object",
|
||||
* required={"title","type"},
|
||||
*
|
||||
* @OA\Property(property="group_id", type="integer", nullable=true, example=1),
|
||||
* @OA\Property(property="title", type="string", maxLength=255, example="독립 섹션"),
|
||||
* @OA\Property(property="type", type="string", enum={"fields","bom"}, example="fields"),
|
||||
* @OA\Property(property="is_template", type="boolean", example=false),
|
||||
* @OA\Property(property="is_default", type="boolean", example=false),
|
||||
* @OA\Property(property="description", type="string", nullable=true, example="섹션 설명")
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="IndependentFieldStoreRequest",
|
||||
* type="object",
|
||||
* required={"field_name","field_type"},
|
||||
*
|
||||
* @OA\Property(property="group_id", type="integer", nullable=true, example=1),
|
||||
* @OA\Property(property="field_name", type="string", maxLength=255, example="제품명"),
|
||||
* @OA\Property(property="field_type", type="string", enum={"textbox","number","dropdown","checkbox","date","textarea"}, example="textbox"),
|
||||
* @OA\Property(property="is_required", type="boolean", example=false),
|
||||
* @OA\Property(property="default_value", type="string", nullable=true, example=null),
|
||||
* @OA\Property(property="placeholder", type="string", nullable=true, maxLength=255, example="입력하세요"),
|
||||
* @OA\Property(property="display_condition", type="object", nullable=true, example=null),
|
||||
* @OA\Property(property="validation_rules", type="object", nullable=true, example=null),
|
||||
* @OA\Property(property="options", type="object", nullable=true, example=null),
|
||||
* @OA\Property(property="properties", type="object", nullable=true, example=null)
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="IndependentBomItemStoreRequest",
|
||||
* type="object",
|
||||
* required={"item_name"},
|
||||
*
|
||||
* @OA\Property(property="group_id", type="integer", nullable=true, example=1),
|
||||
* @OA\Property(property="item_code", type="string", nullable=true, maxLength=100, example="ITEM001"),
|
||||
* @OA\Property(property="item_name", type="string", maxLength=255, example="부품 A"),
|
||||
* @OA\Property(property="quantity", type="number", format="float", example=1),
|
||||
* @OA\Property(property="unit", type="string", nullable=true, maxLength=50, example="EA"),
|
||||
* @OA\Property(property="unit_price", type="number", format="float", nullable=true, example=10000),
|
||||
* @OA\Property(property="total_price", type="number", format="float", nullable=true, example=10000),
|
||||
* @OA\Property(property="spec", type="string", nullable=true, example="규격"),
|
||||
* @OA\Property(property="note", type="string", nullable=true, example="비고")
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="SectionUsageResponse",
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="section_id", type="integer", example=1),
|
||||
* @OA\Property(property="direct_page", type="object", nullable=true),
|
||||
* @OA\Property(property="linked_pages", type="array", @OA\Items(type="object")),
|
||||
* @OA\Property(property="total_usage_count", type="integer", example=2)
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="FieldUsageResponse",
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="field_id", type="integer", example=1),
|
||||
* @OA\Property(property="direct_section", type="object", nullable=true),
|
||||
* @OA\Property(property="linked_sections", type="array", @OA\Items(type="object")),
|
||||
* @OA\Property(property="linked_pages", type="array", @OA\Items(type="object")),
|
||||
* @OA\Property(property="total_usage_count", type="integer", example=3)
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="ItemSectionUpdateRequest",
|
||||
* type="object",
|
||||
*
|
||||
@@ -507,11 +578,247 @@ public function updatePages() {}
|
||||
*/
|
||||
public function destroyPages() {}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/item-master/sections",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="독립 섹션 목록 조회",
|
||||
* description="페이지와 연결되지 않은 독립 섹션 목록을 조회합니다. is_template 파라미터로 템플릿 필터링이 가능합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\Parameter(name="is_template", in="query", description="템플릿 여부 필터", @OA\Schema(type="boolean")),
|
||||
*
|
||||
* @OA\Response(response=200, description="조회 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/ItemSection")))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function indexSections() {}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/item-master/sections",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="독립 섹션 생성",
|
||||
* description="페이지와 연결되지 않은 독립 섹션을 생성합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/IndependentSectionStoreRequest")),
|
||||
*
|
||||
* @OA\Response(response=200, description="생성 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/ItemSection"))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=422, description="검증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function storeIndependentSection() {}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/item-master/sections/{id}/clone",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="섹션 복제",
|
||||
* description="기존 섹션을 복제하여 새 독립 섹션을 생성합니다. 하위 필드와 BOM 항목도 함께 복제됩니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\Parameter(name="id", in="path", required=true, description="복제할 섹션 ID", @OA\Schema(type="integer")),
|
||||
*
|
||||
* @OA\Response(response=200, description="복제 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/ItemSection"))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=404, description="데이터 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function cloneSection() {}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/item-master/sections/{id}/usage",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="섹션 사용처 조회",
|
||||
* description="섹션이 어떤 페이지에 연결되어 있는지 조회합니다. FK 기반 연결과 entity_relationships 기반 연결 모두 조회됩니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\Parameter(name="id", in="path", required=true, description="섹션 ID", @OA\Schema(type="integer")),
|
||||
*
|
||||
* @OA\Response(response=200, description="조회 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/SectionUsageResponse"))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=404, description="데이터 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function getSectionUsage() {}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/item-master/fields",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="독립 필드 목록 조회",
|
||||
* description="섹션과 연결되지 않은 독립 필드 목록을 조회합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\Response(response=200, description="조회 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/ItemField")))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function indexFields() {}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/item-master/fields",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="독립 필드 생성",
|
||||
* description="섹션과 연결되지 않은 독립 필드를 생성합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/IndependentFieldStoreRequest")),
|
||||
*
|
||||
* @OA\Response(response=200, description="생성 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/ItemField"))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=422, description="검증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function storeIndependentField() {}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/item-master/fields/{id}/clone",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="필드 복제",
|
||||
* description="기존 필드를 복제하여 새 독립 필드를 생성합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\Parameter(name="id", in="path", required=true, description="복제할 필드 ID", @OA\Schema(type="integer")),
|
||||
*
|
||||
* @OA\Response(response=200, description="복제 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/ItemField"))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=404, description="데이터 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function cloneField() {}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/item-master/fields/{id}/usage",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="필드 사용처 조회",
|
||||
* description="필드가 어떤 섹션/페이지에 연결되어 있는지 조회합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\Parameter(name="id", in="path", required=true, description="필드 ID", @OA\Schema(type="integer")),
|
||||
*
|
||||
* @OA\Response(response=200, description="조회 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/FieldUsageResponse"))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=404, description="데이터 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function getFieldUsage() {}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/item-master/bom-items",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="독립 BOM 목록 조회",
|
||||
* description="섹션과 연결되지 않은 독립 BOM 항목 목록을 조회합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\Response(response=200, description="조회 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/ItemBomItem")))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function indexBomItems() {}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/item-master/bom-items",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="독립 BOM 생성",
|
||||
* description="섹션과 연결되지 않은 독립 BOM 항목을 생성합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/IndependentBomItemStoreRequest")),
|
||||
*
|
||||
* @OA\Response(response=200, description="생성 성공",
|
||||
*
|
||||
* @OA\JsonContent(allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/ItemBomItem"))
|
||||
* })
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=422, description="검증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function storeIndependentBomItem() {}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/item-master/pages/{pageId}/sections",
|
||||
* tags={"ItemMaster"},
|
||||
* summary="섹션 생성",
|
||||
* summary="섹션 생성 (페이지 연결)",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\Parameter(name="pageId", in="path", required=true, @OA\Schema(type="integer")),
|
||||
|
||||
Reference in New Issue
Block a user