AGENT SKILL

11-shop-product

ShopTable row를 table-centric `ShopProductBase` 계층(`Daily/Event/Gold/Chest/Purchase`)으로 변환하고 가격, 할인, 구매 제한 runtime 모델을 정리할 때 사용한다.

适用职业产品经理
License 许可
MIT
所属仓库
devwinsoft/devian
最近更新
2026-04-12
Stars
★ 0
Forks
0

01 前言 Front Matter

name
11-shop-product
description
ShopTable row를 table-centric `ShopProductBase` 계층(`Daily/Event/Gold/Chest/Purchase`)으로 변환하고 가격, 할인, 구매 제한 runtime 모델을 정리할 때 사용한다.

02 资源文件 3

03 SKILL.md 正文

11-shop-product

Status: ACTIVE AppliesTo: v10

ShopProduct 모델은 SHOP_ITEM_* row를 1:1 concrete product class로 감싼다. row 원본은 Table 프로퍼티를 통해 노출하고, runtime 상태만 product에 둔다.


1. Source Tables

  • table file: input/Domains/Game/ShopTable.xlsx
  • SHOP_ITEM_DAILY
  • SHOP_ITEM_EVENT
  • SHOP_ITEM_CHEST
  • SHOP_ITEM_PURCHASE
  • SHOP_ITEM_GOLD

기존 SHOP_PRODUCT/productId 개념은 제거하고 shop_item_id를 사용한다.


2. Class Contract

public interface IShopTableProduct
{
    public object TableObject { get; }
}

public interface IShopTableProduct<out TRow> : IShopTableProduct
{
    public TRow Table { get; }
}

public abstract class ShopProductBase
{
    public string shop_id { get; }
    public string name_id { get; }
    public SHOP_CATALOG_TYPE catalog_type { get; }
    public SHOP_PRODUCT_TYPE ProductType { get; }
    public SHOP_DISCOUNT_TYPE DiscountType { get; }
    public virtual int PriceWithoutDiscount { get; }
    public int Price { get; } // PriceWithoutDiscount에 DiscountType 반영
}

public abstract class ShopProductBase<TRow> : ShopProductBase, IShopTableProduct<TRow>
{
    public TRow Table { get; }
}

public abstract class ShopLimitedProductBase : ShopProductBase
{
    public int max_count { get; }
    public int RemainCount { get; }
    public bool HasPurchaseLimit { get; }
}

public abstract class ShopRewardProductBase : ShopLimitedProductBase
{
    public CURRENCY_TYPE currency_type { get; }
    public string reward_group_id { get; }
    public int amount { get; }
}

public sealed class ShopProductDaily : ShopRewardProductBase<SHOP_ITEM_DAILY> {}
public sealed class ShopProductEvent : ShopRewardProductBase<SHOP_ITEM_EVENT> {}
public sealed class ShopProductGold : ShopRewardProductBase<SHOP_ITEM_GOLD> {}
public sealed class ShopProductChest : ShopLimitedProductBase<SHOP_ITEM_CHEST> {}
public sealed class ShopProductPurchase : ShopProductBase<SHOP_ITEM_PURCHASE> {}
  • concrete class는 source table 기준으로만 나눈다.
  • FREE/ADS/CURRENCY는 더 이상 구현 클래스 분기가 아니라 ProductType/currency_type 해석 결과다.
  • SHOP_ITEM_GOLD와 SHOP_ITEM_EVENT는 amount column이 없으므로 amount=1 고정이다.
  • SHOP_ITEM_CHEST.amount는 reward 반복 지급 횟수다.

3. Hard Rules

  • shop_item_id가 유일 키다.
  • Table이 row 원본 SSOT다. name_id, price, reward_group_id, internal_product_id 같은 table 값은 product에 중복 저장하지 않는다.
  • runtime 상태(DiscountType, RemainCount)만 product에 둔다.
  • PURCHASE 타입은 Internal_product_id가 필수다.
  • DiscountType은 SHOP_ITEM_DAILY 초기화/refresh 시에만 랜덤 배정되며, 나머지 카탈로그는 SHOP_DISCOUNT_TYPE.NONE을 사용한다.
  • ShopManager.GetProducts(...) API는 사용하지 않고, GetCatalog(catalog_type).GetProducts()/GetCatalog(catalog_type).GetProduct(shop_item_id)를 사용한다.
  • 제한 상품은 ShopLimitedProductBase만 표현한다. max_count=-1은 무제한이다.
  • ShopLimitedProductBase 생성 시 기본 RemainCount = max_count로 초기화한다.
  • ShopProductBase.Price는 SHOP_DISCOUNT_TYPE을 적용한 최종 가격이다.
  • ShopProductBase.PriceWithoutDiscount는 "할인 전 실제 결제 기준 가격"이다.
  • SHOP_ITEM_DAILY row는 정적 amount를 가지지 않고 amount_min/amount_max를 가진다.
  • ShopRewardProductBase의 기본 amount는 1이다. ShopProductDaily만 daily snapshot에서 선택된 runtime amount를 주입한다.
  • SHOP_ITEM_DAILY.unit_price는 단가다. 따라서 ShopProductDaily.PriceWithoutDiscount = Table.unit_price * amount이며, 할인은 그 결과에 적용한다.
  • SHOP_ITEM_EVENT.price, SHOP_ITEM_GOLD.price, SHOP_ITEM_CHEST.price는 이미 최종 row 가격으로 해석한다. 특히 chest는 amount가 reward 배수일 뿐 가격에는 곱해지지 않는다.
  • daily runtime amount는 reward 반복 횟수이면서 가격 배수에도 반영된다.
  • SHOP_ITEM_CHEST.amount는 reward 반복 지급 횟수일 뿐 가격에는 곱하지 않는다.
  • SHOP_ITEM_CHEST는 reward_group_id를 직접 가지지 않는다. chest 구매 reward는 현재 ShopCatalogChest.Level의 SHOP_CATALOG_CHEST.reward_ads/reward_paid01/reward_paid10에서 동적으로 결정한다.
  • ShopProductChest.ProductType은 Chest_type=ADS면 SHOP_PRODUCT_TYPE.ADS, Chest_type=ONE/TEN이면 SHOP_PRODUCT_TYPE.CURRENCY로 해석한다.
  • ShopProductDaily/Event/Gold의 ProductType은 각 row의 currency_type에서 계산한다.
  • 카탈로그 클래스(ShopCatalogBase, ShopCatalogDaily/Chest/Purchase/Gold)는 ShopCatalog.cs에 분리되어 관리한다.
  • row -> ShopProductBase 변환은 ShopProductFactory(15)에서 처리한다.

4. Implementation Location (3-path mirror)

클래스별 파일 분리 — Product/ 폴더 (8개 파일):

  • ShopProductBase.cs

  • ShopLimitedProductBase.cs

  • ShopRewardProductBase.cs

  • ShopProductDaily.cs

  • ShopProductEvent.cs

  • ShopProductGold.cs

  • ShopProductChest.cs

  • ShopProductPurchase.cs

  • UPM (정본): framework-cs/upm/com.devian.foundation/Samples~/MobilePackage/Runtime/Shop/Product/

  • Packages (sync): framework-cs/apps/UnityExample/Packages/com.devian.foundation/Samples~/MobilePackage/Runtime/Shop/Product/

  • Assets/Samples (import): framework-cs/apps/UnityExample/Assets/Samples/Devian Foundation/0.1.0/MobilePackage/Runtime/Shop/Product/


5. Related

04 同仓库更多 Skills