📝 商品登録システム設計 - 完全ガイド
ブランド別最適化・自動バリアント生成
時の絵(作品合成)× 猫アプリ(写真プリント)統合管理システム
🎯 システム概要
商品登録アーキテクチャ
登録対象ブランド:
🎨 時の絵 (TOKINOE):
- 日本画作品 → 16プレミアム + 3木製 + 1スタンド + 1ポストカード = 21バリアント
- 合成処理: ブラウザCanvas 3555×2528px
- 管理者・作家両方が登録可能
📸 猫アプリ (NEKO):
- 写真プリント商品(89mm, 102mm等)
- テンプレート登録・バリアント設定
- 管理者のみ登録(顧客は注文時アップロード)
技術基盤:
- Cloudflare Workers BFF
- R2 Storage(画像・合成結果)
- D1 Database(非PIIメタデータ)
- Shopify API(商品・バリアント作成)
- Firebase Auth(権限管理)
🎨 時の絵 - 作品登録フロー
Step 1: 作品基本情報入力
// 時の絵作品登録フォーム
interface TokinoeArtworkForm {
// 作品情報
title: string // "紅葉"
artist: string // "山田太郎"
technique: string // "水彩画"
description: string // 作品解説
// 価格設定
premiumFrameBasePrice: number // 25000
woodFrameBasePrice: number // 18000
standPrice: number // 12000
postcardPrice: number // 500
// メタデータ
creationYear?: number // 2024
size?: string // "F6号"
tags: string[] // ["風景", "秋", "紅葉"]
// システム情報
firebase_uid: string // 作家のFirebase UID
status: 'draft' | 'published' // 公開状態
}
Step 2: 作品画像アップロード
graph TD
A[作品画像アップロード] --> B[画像検証・最適化]
B --> C[R2 Storage保存]
C --> D[メタデータ抽出]
D --> E[プレビュー生成]
E --> F[合成準備完了]
// 画像アップロード処理
interface ImageUploadResult {
original_url: string // R2保存先URL
thumbnail_url: string // サムネイルURL
width: number // 原寸サイズ
height: number
file_size: number // ファイルサイズ
format: 'jpeg' | 'png' | 'webp' // 画像形式
hash: string // SHA-256ハッシュ
}
// アップロード API
POST /api/artwork/upload
Headers: { Authorization: Bearer <firebase_token> }
Body: FormData { image: File }
Response: ImageUploadResult
Step 3: バリアント合成・生成
// 自動バリアント合成システム
interface VariantGenerationRequest {
artwork_id: string
original_image_url: string
variants: VariantConfig[]
}
interface VariantConfig {
type: 'premium' | 'wood' | 'stand' | 'postcard'
frame_color?: string // "black", "brown", "white", "natural"
mat_color?: string // "white", "cream", "gray", "navy"
dimensions: {
width: number // 3555px
height: number // 2528px
}
}
// 合成処理(ブラウザ Worker内)
async function generateVariants(request: VariantGenerationRequest) {
const canvas = new OffscreenCanvas(3555, 2528)
const ctx = canvas.getContext('2d')
const results = []
for (const variant of request.variants) {
// 1. フレーム画像読み込み
const frameImage = await loadFrameTemplate(variant.type, variant.frame_color)
const matImage = await loadMatTemplate(variant.mat_color)
const artworkImage = await loadImage(request.original_image_url)
// 2. Canvas合成
ctx.clearRect(0, 0, 3555, 2528)
// 背景(マット)
if (matImage) ctx.drawImage(matImage, 0, 0, 3555, 2528)
// 作品画像(中央配置・アスペクト比保持)
const artworkBounds = calculateArtworkBounds(variant.type)
drawImageWithinBounds(ctx, artworkImage, artworkBounds)
// フレーム重ね合わせ
ctx.drawImage(frameImage, 0, 0, 3555, 2528)
// 3. WebP変換・R2アップロード
const blob = await canvas.convertToBlob({ type: 'image/webp', quality: 0.9 })
const url = await uploadToR2(blob, `tokinoe/${request.artwork_id}/${variant.type}-${variant.frame_color}-${variant.mat_color}.webp`)
results.push({
variant_config: variant,
image_url: url,
alt_text: `${request.artwork_title}_${variant.type}_${variant.frame_color}×${variant.mat_color}`
})
}
return results
}
Step 4: Shopify商品・バリアント作成
// Shopify商品作成
interface ShopifyProductCreation {
product: {
title: string // "紅葉 / 山田太郎"
body_html: string // 作品解説(HTML)
vendor: string // "山田太郎"
product_type: "tokinoe"
tags: string // "日本画,風景,秋,紅葉"
// メタフィールド
metafields: [
{
namespace: "custom",
key: "brand",
value: "tokinoe"
},
{
namespace: "custom",
key: "firebase_uid",
value: string // 作家のUID
},
{
namespace: "custom",
key: "technique",
value: string // "水彩画"
}
]
}
variants: ShopifyVariant[]
}
interface ShopifyVariant {
title: string // "プレミアムフレーム・ブラック×ホワイト"
price: string // "25000"
inventory_management: "shopify"
inventory_quantity: 999 // 受注生産のため大きな値
requires_shipping: true
// 画像関連
image_src: string // 合成済み画像URL
image_alt: string // alt属性
// バリアント属性
option1: string // "プレミアムフレーム"
option2: string // "ブラック"
option3: string // "ホワイト"
}
// Shopify API呼び出し
async function createShopifyProduct(productData: ShopifyProductCreation) {
// BFF経由でShopify Admin API呼び出し
const response = await fetch('/api/shopify/products', {
method: 'POST',
headers: {
'Authorization': `Bearer ${await getFirebaseToken()}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(productData)
})
return response.json()
}
📸 猫アプリ - 写真プリント商品登録
商品テンプレート登録
// 猫アプリ商品テンプレート
interface NekoProductTemplate {
// 基本情報
title: string // "89mm写真プリント"
description: string // 商品説明
// サイズ・仕様
print_size: {
width_mm: number // 89
height_mm: number // 127
dpi: number // 300
pixel_width: number // 計算値
pixel_height: number // 計算値
}
// 価格設定
base_price: number // 300
quantity_discounts: { // 数量割引
[key: string]: number // "10": 250, "50": 200
}
// 生産情報
production_time: string // "1-3営業日"
shipping_method: string // "ネコポス"
// システム情報
product_type: "neko"
status: 'active' | 'inactive'
}
// 登録API
POST /api/neko/products/templates
Headers: { Authorization: Bearer <admin_token> }
Body: NekoProductTemplate
Response: { template_id: string, shopify_product_id: string }
顧客写真アップロード(注文時)
// 顧客の写真アップロード(注文時)
interface CustomerPhotoUpload {
template_id: string // 使用する商品テンプレート
photos: File[] // アップロード写真
quantity: number // 注文数量
customer_info: {
firebase_uid: string // 顧客のFirebase UID
// PII情報はShopify Checkoutで取得
}
}
// 写真処理フロー
async function processCustomerPhotos(upload: CustomerPhotoUpload) {
const processedPhotos = []
for (const photo of upload.photos) {
// 1. 画像検証・最適化
const optimized = await optimizeImage(photo)
// 2. プリントサイズにリサイズ
const resized = await resizeForPrint(optimized, upload.template_id)
// 3. R2保存(一時的・注文完了後削除)
const url = await uploadToR2(resized, `neko/temp/${upload.customer_info.firebase_uid}/${Date.now()}.webp`)
processedPhotos.push({
original_name: photo.name,
processed_url: url,
print_ready: true
})
}
// 4. 注文準備完了
return {
photos: processedPhotos,
total_price: calculatePrice(upload.template_id, upload.quantity),
ready_for_checkout: true
}
}
🖥️ 管理画面設計
権限・ロール管理
// ユーザー権限システム
interface UserRoles {
super_admin: {
permissions: ['*'] // 全権限
description: "システム全体管理"
}
tokinoe_admin: {
permissions: [
'tokinoe:create', 'tokinoe:edit', 'tokinoe:delete',
'users:manage_artists', 'analytics:view'
]
description: "時の絵ブランド管理"
}
artist: {
permissions: [
'tokinoe:create', 'tokinoe:edit_own'
]
description: "作家(自分の作品のみ管理)"
}
neko_admin: {
permissions: [
'neko:create', 'neko:edit', 'neko:delete',
'templates:manage', 'orders:manage'
]
description: "猫アプリ管理"
}
}
// 権限チェック
async function checkPermission(firebase_uid: string, permission: string): Promise<boolean> {
const userRoles = await getUserRoles(firebase_uid)
return userRoles.some(role =>
role.permissions.includes('*') ||
role.permissions.includes(permission)
)
}
管理画面UI構成
🖥️ 管理画面構成:
📊 ダッシュボード:
- 売上・注文統計
- 在庫状況
- 新着注文アラート
🎨 時の絵管理:
作品登録:
- 作品情報入力フォーム
- 画像アップロード(ドラッグ&ドロップ)
- リアルタイムプレビュー
- バリアント合成進行状況
作品一覧:
- フィルター・検索機能
- 一覧・グリッド表示切替
- 一括操作(公開・非公開・削除)
作家管理:
- 作家アカウント作成・管理
- 作品数・売上統計
- 権限設定
📸 猫アプリ管理:
商品テンプレート:
- プリントサイズ・仕様設定
- 価格・割引設定
- テンプレート有効・無効切替
注文管理:
- 写真確認・印刷指示
- 配送状況管理
- 顧客問い合わせ対応
⚙️ システム設定:
- Shopify連携設定
- R2・D1設定確認
- ログ・エラー監視
🔄 登録フロー詳細設計
時の絵作品登録フロー
graph TD
A[作品情報入力] --> B[画像アップロード]
B --> C[画像検証・最適化]
C --> D{検証OK?}
D -->|NG| E[エラー表示・再アップロード]
D -->|OK| F[プレビュー表示]
F --> G[価格設定・説明入力]
G --> H[バリアント合成開始]
H --> I[Progress表示]
I --> J[21バリアント順次合成]
J --> K[R2アップロード完了]
K --> L[Shopify商品作成]
L --> M[D1メタデータ保存]
M --> N[登録完了]
E --> B
style H fill:#e1f5fe
style J fill:#f3e5f5
style N fill:#e8f5e8
API設計 - 時の絵登録
// Step 1: 作品登録開始
POST /api/tokinoe/artworks/create
{
title: "紅葉",
artist: "山田太郎",
technique: "水彩画",
description: "...",
pricing: { premium: 25000, wood: 18000, stand: 12000, postcard: 500 }
}
Response: { artwork_id: "artwork_123", upload_token: "temp_token_456" }
// Step 2: 画像アップロード
POST /api/tokinoe/artworks/{artwork_id}/upload
Headers: { Authorization: Bearer <upload_token> }
Body: FormData { image: File }
Response: { image_url: "...", thumbnail_url: "...", ready_for_composition: true }
// Step 3: バリアント合成開始
POST /api/tokinoe/artworks/{artwork_id}/generate-variants
Response: { job_id: "job_789", estimated_time: "180s" }
// Step 4: 進行状況確認
GET /api/tokinoe/artworks/{artwork_id}/generation-status/{job_id}
Response: {
status: "processing",
completed: 12,
total: 21,
current: "premium-black-white",
eta: "60s"
}
// Step 5: Shopify公開
POST /api/tokinoe/artworks/{artwork_id}/publish
Response: { shopify_product_id: "gid://...", url: "https://..." }
📊 データ管理・バックアップ
D1データベース設計
-- 作品管理テーブル
CREATE TABLE tokinoe_artworks (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
artist TEXT NOT NULL,
technique TEXT,
description TEXT,
firebase_uid TEXT NOT NULL, -- 作家UID
-- 画像情報
original_image_url TEXT NOT NULL,
thumbnail_url TEXT,
image_hash TEXT UNIQUE,
-- 価格設定
premium_base_price INTEGER,
wood_base_price INTEGER,
stand_price INTEGER,
postcard_price INTEGER,
-- Shopify連携
shopify_product_id TEXT UNIQUE,
shopify_handle TEXT,
-- ステータス
status TEXT DEFAULT 'draft', -- draft, published, archived
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- バリアント管理テーブル
CREATE TABLE artwork_variants (
id TEXT PRIMARY KEY,
artwork_id TEXT NOT NULL,
-- バリアント情報
type TEXT NOT NULL, -- premium, wood, stand, postcard
frame_color TEXT, -- black, brown, white, natural
mat_color TEXT, -- white, cream, gray, navy
-- 画像・Shopify情報
image_url TEXT NOT NULL,
alt_text TEXT,
shopify_variant_id TEXT,
-- 価格・在庫
price INTEGER NOT NULL,
inventory_quantity INTEGER DEFAULT 999,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (artwork_id) REFERENCES tokinoe_artworks(id)
);
-- 猫アプリ商品テンプレート
CREATE TABLE neko_product_templates (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT,
-- サイズ・仕様
width_mm INTEGER,
height_mm INTEGER,
dpi INTEGER DEFAULT 300,
-- 価格設定(JSON)
pricing TEXT, -- JSON: { base: 300, discounts: {...} }
-- Shopify連携
shopify_product_id TEXT,
status TEXT DEFAULT 'active',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
バックアップ・災害復旧
バックアップ戦略:
D1 Database:
- 毎日自動バックアップ(Cloudflare機能)
- 週次手動エクスポート(CSV)
- 重要データはShopifyと同期状態維持
R2 Storage:
- オブジェクトレプリケーション設定
- 作品画像は複数リージョン配置
- 定期的な整合性チェック
Shopify Data:
- Shopify側の標準バックアップ機能
- Export API定期実行(重要商品データ)
- メタフィールドの手動バックアップ
復旧手順:
1. Cloudflare D1復旧
2. R2画像データ確認・復旧
3. Shopify商品データ整合性確認
4. 管理画面での動作確認
✅ 実装チェックリスト
Phase 1: 基盤構築(2週間)
✅ システム基盤:
- [ ] D1データベーススキーマ作成
- [ ] R2ストレージ設定・権限設定
- [ ] Firebase認証・権限管理実装
- [ ] BFF API基本構造構築
✅ 画像処理システム:
- [ ] ブラウザCanvas合成システム
- [ ] フレーム・マットテンプレート準備
- [ ] 画像最適化・リサイズ機能
- [ ] R2アップロード・CDN設定
Phase 2: 時の絵登録システム(3週間)
✅ 管理画面:
- [ ] 作品登録フォーム
- [ ] 画像アップロードUI
- [ ] バリアント合成進行表示
- [ ] プレビュー・確認画面
✅ API実装:
- [ ] 作品CRUD API
- [ ] 画像処理・合成API
- [ ] Shopify商品作成API
- [ ] 進行状況確認API
✅ バリアント合成:
- [ ] 21パターン自動合成
- [ ] エラーハンドリング
- [ ] リトライ機能
- [ ] 合成品質確認
Phase 3: 猫アプリ・統合(2週間)
✅ 猫アプリ機能:
- [ ] 商品テンプレート管理
- [ ] 顧客写真アップロード
- [ ] 注文処理フロー
- [ ] 印刷データ生成
✅ 統合・テスト:
- [ ] E2Eテスト実装
- [ ] パフォーマンステスト
- [ ] セキュリティ監査
- [ ] 本番デプロイ準備
📍 次のアクションアイテム
✅ 即座に開始可能
- D1スキーマ設計確定 - 上記SQL文の詳細検討
- フレームテンプレート準備 - デザインチームと連携
- Canvas合成プロトタイプ - 技術検証・パフォーマンステスト
📋 要件定義・承認待ち
- 作家権限範囲 - どこまで自主管理可能にするか
- 商品価格体系 - ブランド別価格戦略
- 合成品質基準 - どのレベルまで自動化するか
📍 関連ドキュメント: BFF設計 | 時の絵システム | Shopify統合
文書作成日: 2025-08-24
最終更新: 2025-08-24
バージョン: 1.0 - 商品登録システム設計版