{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "get-products.json",
  "title": "GET /products response",
  "description": "GET /products response の response body schema。ALPSの意味、be/var/fakeの観察値、Resource境界の実形状から導いたSemantic-Ex制約。",
  "type": "object",
  "properties": {
    "filters": {
      "type": [
        "object",
        "null"
      ],
      "title": "検索条件",
      "description": "/products の一覧表示を制御するページング/検索条件。件数、開始位置、並び順、前後リンクをクライアントが再現するための値。",
      "properties": {
        "disp_number": {
          "type": [
            "string",
            "integer",
            "null"
          ],
          "maximum": 2147483647,
          "title": "表示件数指定",
          "description": "/products の一覧表示を制御するページング/検索条件。件数、開始位置、並び順、前後リンクをクライアントが再現するための値。",
          "minLength": 0,
          "maxLength": 64,
          "$comment": "EC-CUBE互換のフォーム/一覧境界で数値と文字列の両方が観察される。業務解釈はResource/Semantic層で行う。"
        },
        "nameKeyword": {
          "type": [
            "string",
            "null"
          ],
          "minLength": 0,
          "maxLength": 255,
          "title": "名前検索キーワード",
          "description": "/products の検索条件。商品名・会員名・管理者名など、この一覧画面で名前として扱う表示名を部分一致検索する。",
          "example": "鈴木"
        },
        "name": {
          "type": [
            "string",
            "null"
          ],
          "minLength": 0,
          "maxLength": 255,
          "title": "商品名",
          "description": "/products で表示する商品名。検索・一覧・詳細でユーザーに見せる販売名。",
          "example": "テスト管理者"
        },
        "pageno": {
          "type": [
            "string",
            "integer",
            "null"
          ],
          "maximum": 2147483647,
          "title": "ページ番号",
          "description": "/products の一覧表示を制御するページング/検索条件。件数、開始位置、並び順、前後リンクをクライアントが再現するための値。",
          "minLength": 0,
          "maxLength": 64,
          "$comment": "EC-CUBE互換のフォーム/一覧境界で数値と文字列の両方が観察される。業務解釈はResource/Semantic層で行う。"
        },
        "orderby": {
          "type": [
            "string",
            "null"
          ],
          "minLength": 0,
          "maxLength": 255,
          "title": "並び順",
          "description": "/products の一覧表示を制御するページング/検索条件。件数、開始位置、並び順、前後リンクをクライアントが再現するための値。"
        },
        "categoryName": {
          "type": [
            "string",
            "null"
          ],
          "minLength": 0,
          "maxLength": 32,
          "title": "カテゴリ名",
          "description": "カテゴリの表示名 Fake観察文字長 4〜6; 観察値 'Food', 'Drinks'。",
          "example": "Food"
        },
        "category_id": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^[0-9]+$",
          "minLength": 1,
          "maxLength": 10,
          "title": "選択中カテゴリID",
          "description": "/products の検索フォームに再表示する選択中カテゴリID。HTTP query由来の文字列値で、カテゴリ絞り込み条件を保持する。",
          "$comment": "Responseのfiltersは入力再表示用のtransport値なので、DB IDとしてのintegerではなくHTML queryと同じstring|nullで表す。"
        }
      },
      "additionalProperties": false,
      "required": [
        "disp_number",
        "nameKeyword",
        "name",
        "pageno",
        "orderby",
        "categoryName",
        "category_id"
      ]
    },
    "pager": {
      "type": [
        "object",
        "null"
      ],
      "title": "ページャ",
      "description": "/products の一覧表示を制御するページング/検索条件。件数、開始位置、並び順、前後リンクをクライアントが再現するための値。",
      "properties": {
        "previous": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 1,
          "maximum": 10000,
          "title": "前ページ番号",
          "description": "/products のページャで、現在ページの直前に遷移するページ番号。先頭ページでは null。"
        },
        "next": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 1,
          "maximum": 10000,
          "title": "次ページ番号",
          "description": "/products のページャで、現在ページの直後に遷移するページ番号。最終ページでは null。"
        },
        "pages": {
          "type": [
            "array",
            "null"
          ],
          "title": "ページ一覧",
          "description": "/products のレスポンスで扱うページ一覧。配列要素はALPS意味とFake観察に基づき、固定できない動的列は例外理由を台帳化する。",
          "items": {
            "type": "integer",
            "title": "ページ番号",
            "minimum": 1,
            "maximum": 10000,
            "description": "/products のレスポンスに含まれるページ。親コレクション `pages` の1行を表し、固定できる業務列はschema propertyで明示する。"
          },
          "minItems": 0
        },
        "pageCount": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 2147483647,
          "title": "件数",
          "description": "/products のレスポンスで返す件数。一覧・集計・処理結果の規模を表す非負整数。"
        },
        "current": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 2147483647,
          "title": "現在ページ",
          "description": "/products の一覧表示を制御するページング/検索条件。件数、開始位置、並び順、前後リンクをクライアントが再現するための値。"
        }
      },
      "additionalProperties": false,
      "required": [
        "pages",
        "pageCount",
        "current"
      ]
    },
    "transitionId": {
      "title": "ALPS遷移ID",
      "description": "このレスポンス/操作が対応するALPS遷移ID。クライアントの状態遷移追跡に使う。",
      "type": "string",
      "minLength": 2,
      "maxLength": 96,
      "pattern": "^(go|do)[A-Z][A-Za-z0-9]*$",
      "example": "doAddCartItem"
    },
    "csrfToken": {
      "title": "処理識別子",
      "description": "フォーム送信の偽造を防ぐために送信元画面で発行されるトークン。Fake環境では deterministic な値を使う。",
      "type": [
        "string",
        "null"
      ],
      "minLength": 8,
      "maxLength": 160,
      "pattern": "^[A-Za-z0-9_.:-]+$",
      "example": "fake-csrf-token-bemart-2026"
    },
    "totalItemCount": {
      "type": [
        "integer",
        "null"
      ],
      "minimum": 0,
      "maximum": 2147483647,
      "title": "総件数",
      "description": "/products のレスポンスで返す総件数。一覧・集計・処理結果の規模を表す非負整数。"
    },
    "products": {
      "type": [
        "array",
        "null"
      ],
      "title": "商品一覧",
      "description": "/products のレスポンスで扱う商品一覧。配列要素はALPS意味とFake観察に基づき、固定できない動的列は例外理由を台帳化する。",
      "items": {
        "type": [
          "object",
          "null"
        ],
        "title": "商品概要",
        "description": "/products のレスポンスに含まれる商品概要。親コレクション `products` の1行を表し、固定できる業務列はschema propertyで明示する。",
        "properties": {
          "mainListImage": {
            "title": "一覧メイン画像URI",
            "description": "/products の画面表示に使う一覧メイン画像URI。業務エンティティそのものではなくテンプレート/一覧表示の補助値。",
            "type": [
              "string",
              "null"
            ],
            "format": "uri-reference",
            "minLength": 1,
            "maxLength": 2048,
            "example": "/products"
          },
          "stock": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 2147483647,
            "title": "在庫数",
            "description": "物理在庫数。stockUnlimited=trueの場合は無視される。注文確定時に引き当てが行われる Fake観察数値 0〜100; 観察値 '0', '10', '20', '50', '5', '7', '100', '3'; null 9/73。",
            "example": 0
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 0,
            "maxLength": 255,
            "title": "商品名",
            "description": "/products で表示する商品名。検索・一覧・詳細でユーザーに見せる販売名。",
            "example": "テスト管理者"
          },
          "productName": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 0,
            "maxLength": 128,
            "title": "商品名",
            "description": "商品の表示名 Fake観察文字長 6〜17。",
            "example": "サンプル商品 A"
          },
          "price02": {
            "title": "販売価格",
            "description": "実際の販売価格（税抜）。税計算・小計計算のベース Fake観察数値 800〜28000。",
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999,
            "example": 3500
          },
          "tagNames": {
            "type": [
              "array",
              "null"
            ],
            "title": "タグ名一覧",
            "description": "Fake観察数値 0〜2。",
            "items": {
              "type": "string",
              "title": "タグ名",
              "minLength": 0,
              "maxLength": 128,
              "description": "/products のレスポンスに含まれる商品行。親コレクション `tagNames` の1行を表し、固定できる業務列はschema propertyで明示する。"
            },
            "minItems": 0
          },
          "categoryNames": {
            "type": [
              "array",
              "null"
            ],
            "title": "カテゴリ名一覧",
            "description": "Fake観察数値 0〜4。",
            "items": {
              "type": "string",
              "title": "カテゴリ名",
              "minLength": 0,
              "maxLength": 128,
              "description": "/products のレスポンスに含まれる商品行。親コレクション `categoryNames` の1行を表し、固定できる業務列はschema propertyで明示する。"
            },
            "minItems": 0
          },
          "productCode": {
            "title": "商品コード",
            "description": "SKU/品番。在庫管理や受注明細での識別に使用 商品を識別するSKU。Fake corpusではASCII英数・ハイフン中心で、受注明細/カート明細の結合キーになる。 Fake観察文字長 10〜26。",
            "type": "string",
            "minLength": 0,
            "maxLength": 64,
            "example": "sample-001"
          },
          "descriptionList": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 0,
            "maxLength": 255,
            "title": "一覧用説明文",
            "description": "商品一覧・検索結果に表示する短い説明文"
          },
          "stockFind": {
            "type": [
              "boolean",
              "null"
            ],
            "title": "在庫検索フラグ",
            "description": "/products の処理文脈から派生した在庫検索フラグ。ALPS基礎語だけでは単位や用途が不足するため、このResource上の意味を明示する。"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ],
            "title": "ID",
            "description": "Fake観察文字長 13〜32; 観察値 'ad000000000000000000000000000001', 'ad000000000000000000000000000003', 'fedcba9876543210fedcba9876543210', '10000000aaaa1111bbbb2222cccc3333', 'ad000000000000000000000000000002', '0123456789abcdef0123456789abcdef', 'aaaaaaaa00000000bbbbbbbb11111111', '20000000dddd2222eeee3333ffff4444'。",
            "example": "ad000000000000000000000000000001",
            "minLength": 0,
            "maxLength": 128,
            "$comment": "Fake文字列IDとEC-CUBE整数IDの両方が観察されるため、この境界だけmixedBoundaryIdとして扱う。"
          },
          "unitPrice": {
            "title": "単価（表示/計算用）",
            "description": "明細1件あたりの単価。受注/カート明細・お気に入りスナップショットでは追加時点の price02 をスナップショットして保持する（後の値引きやマスタ改定に影響されない）。BeMart 側では `int` 円整数 Fake観察数値 1200〜9800; 観察値 '1200', '9800'。",
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999,
            "example": 1200
          },
          "fileName": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 255,
            "title": "ファイル名",
            "description": "商品画像のファイル名 Fake観察文字長 12〜15; 観察値 'Mail/order.twig', 'Mail/entry.twig', 'sample-a.jpg', 'sample-b.jpg'。",
            "example": "Mail/order.twig"
          }
        },
        "additionalProperties": false,
        "$comment": "配列要素はFake/Resourceで観察された既知propertyに固定する。新しい列が必要になった場合はSemantic-Ex観察に追加してschemaを更新する。"
      },
      "minItems": 0
    }
  },
  "additionalProperties": false,
  "$defs": {
    "productCode": {
      "title": "商品コード",
      "description": "SKU/品番。在庫管理や受注明細での識別に使用 SKUとして在庫・カート・受注明細を接続する。Fake観察ではASCII英数とハイフン中心。",
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$",
      "example": "sample-001"
    },
    "email": {
      "title": "メールアドレス",
      "description": "会員のログインIDを兼ねる。有効会員間で一意 ログインID/通知先として使うためRFC準拠のemail形式。",
      "type": "string",
      "format": "email",
      "minLength": 3,
      "maxLength": 254,
      "example": "alice@example.com"
    },
    "postalCode": {
      "title": "郵便番号",
      "description": "日本の郵便番号。ハイフンなし7桁またはハイフン付き8桁 日本国内住所向け。ハイフンなし7桁またはハイフン付き8桁。",
      "type": "string",
      "pattern": "^\\d{3}-?\\d{4}$",
      "example": "1500001"
    },
    "phoneNumber": {
      "title": "電話番号",
      "description": "日本の電話番号形式（ハイフン区切り） 日本国内電話番号。Fakeはハイフンなし中心、入力ではハイフン付きも許容。",
      "type": "string",
      "pattern": "^0\\d{1,4}-?\\d{1,4}-?\\d{3,4}$",
      "minLength": 10,
      "maxLength": 13,
      "example": "0312345678"
    },
    "price": {
      "title": "金額",
      "description": "EC-CUBEの商品価格・送料・手数料・売上金額。日本円の整数金額として扱う。",
      "type": "integer",
      "minimum": 0,
      "maximum": 999999999,
      "example": 1200
    },
    "quantity": {
      "title": "数量",
      "description": "購入数量。カート明細と受注明細で共通使用 購入/調整数量。入力は1以上、調整後や集計では0以上を許容する場合がある。",
      "type": "integer",
      "minimum": 1,
      "maximum": 999,
      "example": 2
    },
    "nonNegativeInteger": {
      "title": "非負整数",
      "description": "件数、在庫、ページ番号、集計値など0以上の整数。",
      "type": "integer",
      "minimum": 0,
      "maximum": 2147483647,
      "example": 1
    },
    "opaqueId": {
      "title": "不透明ID",
      "description": "BeMart/Fake/アプリ層で外部に公開する文字列ID。DB採番値としての数値演算には使わない。",
      "type": "string",
      "minLength": 1,
      "maxLength": 128,
      "pattern": "^[A-Za-z0-9._:@/-]+$",
      "example": "customer-001"
    },
    "dbId": {
      "title": "DB採番ID",
      "description": "EC-CUBEマスタや内部行を指す非負整数ID。表示やフォーム境界では文字列化される場合があるが、意味は採番ID。",
      "type": "integer",
      "minimum": 0,
      "maximum": 2147483647,
      "example": 1
    },
    "mixedBoundaryId": {
      "title": "互換境界ID",
      "description": "同一ResourceでFake文字列IDとEC-CUBE整数IDの両方が観察される互換境界専用ID。恒久的なドメイン型ではない。",
      "type": [
        "string",
        "integer"
      ],
      "minLength": 0,
      "maxLength": 128,
      "example": "pay-cod",
      "$comment": "mixedBoundaryIdは移行互換のための例外。可能ならopaqueIdまたはdbIdへ分解する。"
    },
    "pref": {
      "title": "都道府県ID",
      "description": "日本の都道府県（1=北海道〜47=沖縄県）。住所の最上位区分として顧客・受注・配送先で使用。配送料の地域区分（DeliveryFee）や税率の地域設定（TaxRule）にも使用 確定住所は1〜47。入力フォームの未選択初期値として0を許容する。",
      "type": "integer",
      "minimum": 0,
      "maximum": 47,
      "example": 13
    },
    "productStatus": {
      "title": "商品ステータス",
      "description": "1=公開（フロント表示）, 2=非公開（フロント非表示）, 3=廃止（論理削除、管理画面でもデフォルト非表示）",
      "type": "integer",
      "enum": [
        1,
        2,
        3
      ],
      "example": 1
    },
    "customerStatus": {
      "title": "会員ステータス",
      "description": "1=仮会員（メール未認証）, 2=本会員（認証済み）, 3=退会。退会時はメールアドレスが無効化される",
      "type": "integer",
      "enum": [
        1,
        2,
        3
      ],
      "example": 2
    },
    "orderStatus": {
      "title": "注文ステータス",
      "description": "1=新規受付, 3=注文取消, 4=対応中, 5=発送済み, 6=入金済み, 7=決済処理中, 8=購入処理中, 9=返品。Symfony Workflowステートマシンで遷移を制御。許可される遷移: pay(1->6), packing(1,6->4), cancel(1,4,6->3), back_to_in_progress(3->4), ship(1,6,4->5), return(5->9), cancel_return(9->5)。7と8はPurchaseFlow内で直接セットされステートマシン遷移の対象外 EC-CUBE受注状態。Fake/管理画面で扱う状態ID。",
      "type": "integer",
      "minimum": 1,
      "maximum": 9,
      "example": 1
    },
    "cartKey": {
      "title": "カートキー",
      "description": "カート分離キー。形式: {セッションプレフィックス}_{販売種別ID}。EC-CUBEは販売種別ごとにカートを分離するため、異なる販売種別の商品は別カートになる",
      "type": "string",
      "minLength": 3,
      "maxLength": 128,
      "pattern": "^.+_[0-9]+$",
      "example": "session-prefix-1_1"
    },
    "csrfToken": {
      "title": "CSRFトークン",
      "description": "フォーム送信元を検証するトークン。Fake環境では deterministic な値を使う。",
      "type": "string",
      "minLength": 8,
      "maxLength": 160,
      "pattern": "^[A-Za-z0-9_.:-]+$",
      "example": "fake-csrf-token-bemart-2026"
    },
    "uriReference": {
      "title": "URI参照",
      "description": "画面遷移・リダイレクト・リンク先を表す相対または絶対URI。",
      "type": "string",
      "format": "uri-reference",
      "minLength": 1,
      "maxLength": 2048,
      "example": "/products"
    },
    "transitionId": {
      "title": "ALPS遷移ID",
      "description": "alps.json に定義された safe/unsafe descriptor のID。",
      "type": "string",
      "minLength": 2,
      "maxLength": 96,
      "pattern": "^(go|do)[A-Z][A-Za-z0-9]*$",
      "example": "doAddCartItem"
    },
    "date": {
      "title": "日付",
      "description": "業務上の日付。",
      "type": "string",
      "format": "date",
      "example": "2026-01-01"
    },
    "dateTime": {
      "title": "日時",
      "description": "業務イベント発生日時。",
      "type": "string",
      "format": "date-time",
      "example": "2026-01-01T00:00:00+09:00"
    },
    "link": {
      "title": "ハイパーメディアリンク",
      "description": "ALPS遷移に対応するリンク。hrefはURI参照。",
      "type": "object",
      "required": [
        "href"
      ],
      "additionalProperties": false,
      "properties": {
        "href": {
          "title": "URI参照",
          "description": "画面遷移・リダイレクト・リンク先を表す相対または絶対URI。",
          "type": "string",
          "format": "uri-reference",
          "minLength": 1,
          "maxLength": 2048,
          "example": "/products"
        },
        "rel": {
          "type": "string",
          "minLength": 1,
          "maxLength": 96
        },
        "method": {
          "type": "string",
          "enum": [
            "get",
            "post",
            "put",
            "patch",
            "delete",
            "GET",
            "POST",
            "PUT",
            "PATCH",
            "DELETE"
          ]
        }
      }
    }
  },
  "$comment": "Derived from ALPS meaning, be/var/fake observation, and Resource schema shape.",
  "required": [
    "filters",
    "pager",
    "transitionId",
    "totalItemCount",
    "products"
  ]
}
