feat: Swagger API 문서 추가 + 보스레이드/플레이어 레벨 시스템
- swaggo/swag 기반 전체 API 엔드포인트 Swagger 어노테이션 (59개) - /swagger/ 경로에 Swagger UI 제공 - 보스레이드 데디서버 관리 (등록, 하트비트, 슬롯 리셋) - 플레이어 레벨/경험치 시스템 및 스탯 성장 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,16 @@ func chainError(c *fiber.Ctx, status int, userMsg string, err error) error {
|
||||
|
||||
// ---- Query Handlers ----
|
||||
|
||||
// GetWalletInfo godoc
|
||||
// @Summary 지갑 정보 조회
|
||||
// @Description 현재 유저의 블록체인 지갑 정보를 조회합니다
|
||||
// @Tags Chain
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} docs.WalletInfoResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 404 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/wallet [get]
|
||||
func (h *Handler) GetWalletInfo(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -67,6 +77,16 @@ func (h *Handler) GetWalletInfo(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
// GetBalance godoc
|
||||
// @Summary 잔액 조회
|
||||
// @Description 현재 유저의 토큰 잔액을 조회합니다
|
||||
// @Tags Chain
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/balance [get]
|
||||
func (h *Handler) GetBalance(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -79,6 +99,18 @@ func (h *Handler) GetBalance(c *fiber.Ctx) error {
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
// GetAssets godoc
|
||||
// @Summary 에셋 목록 조회
|
||||
// @Description 현재 유저의 블록체인 에셋 목록을 조회합니다
|
||||
// @Tags Chain
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param offset query int false "시작 위치" default(0)
|
||||
// @Param limit query int false "조회 수" default(50)
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/assets [get]
|
||||
func (h *Handler) GetAssets(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -93,6 +125,18 @@ func (h *Handler) GetAssets(c *fiber.Ctx) error {
|
||||
return c.Send(result)
|
||||
}
|
||||
|
||||
// GetAsset godoc
|
||||
// @Summary 에셋 상세 조회
|
||||
// @Description 특정 에셋의 상세 정보를 조회합니다
|
||||
// @Tags Chain
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param id path string true "에셋 ID"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/asset/{id} [get]
|
||||
func (h *Handler) GetAsset(c *fiber.Ctx) error {
|
||||
assetID := c.Params("id")
|
||||
if !validID(assetID) {
|
||||
@@ -106,6 +150,16 @@ func (h *Handler) GetAsset(c *fiber.Ctx) error {
|
||||
return c.Send(result)
|
||||
}
|
||||
|
||||
// GetInventory godoc
|
||||
// @Summary 인벤토리 조회
|
||||
// @Description 현재 유저의 인벤토리를 조회합니다
|
||||
// @Tags Chain
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/inventory [get]
|
||||
func (h *Handler) GetInventory(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -119,6 +173,17 @@ func (h *Handler) GetInventory(c *fiber.Ctx) error {
|
||||
return c.Send(result)
|
||||
}
|
||||
|
||||
// GetMarketListings godoc
|
||||
// @Summary 마켓 목록 조회
|
||||
// @Description 마켓에 등록된 매물 목록을 조회합니다
|
||||
// @Tags Chain
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param offset query int false "시작 위치" default(0)
|
||||
// @Param limit query int false "조회 수" default(50)
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/market [get]
|
||||
func (h *Handler) GetMarketListings(c *fiber.Ctx) error {
|
||||
offset, limit := parsePagination(c)
|
||||
result, err := h.svc.GetMarketListings(offset, limit)
|
||||
@@ -129,6 +194,17 @@ func (h *Handler) GetMarketListings(c *fiber.Ctx) error {
|
||||
return c.Send(result)
|
||||
}
|
||||
|
||||
// GetMarketListing godoc
|
||||
// @Summary 마켓 매물 상세 조회
|
||||
// @Description 특정 마켓 매물의 상세 정보를 조회합니다
|
||||
// @Tags Chain
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param id path string true "매물 ID"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/market/{id} [get]
|
||||
func (h *Handler) GetMarketListing(c *fiber.Ctx) error {
|
||||
listingID := c.Params("id")
|
||||
if !validID(listingID) {
|
||||
@@ -144,6 +220,20 @@ func (h *Handler) GetMarketListing(c *fiber.Ctx) error {
|
||||
|
||||
// ---- User Transaction Handlers ----
|
||||
|
||||
// Transfer godoc
|
||||
// @Summary 토큰 전송
|
||||
// @Description 다른 유저에게 토큰을 전송합니다
|
||||
// @Tags Chain - Transactions
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.TransferRequest true "전송 정보"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/transfer [post]
|
||||
func (h *Handler) Transfer(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -166,6 +256,20 @@ func (h *Handler) Transfer(c *fiber.Ctx) error {
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
// TransferAsset godoc
|
||||
// @Summary 에셋 전송
|
||||
// @Description 다른 유저에게 에셋을 전송합니다
|
||||
// @Tags Chain - Transactions
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.TransferAssetRequest true "전송 정보"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/asset/transfer [post]
|
||||
func (h *Handler) TransferAsset(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -188,6 +292,20 @@ func (h *Handler) TransferAsset(c *fiber.Ctx) error {
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
// ListOnMarket godoc
|
||||
// @Summary 마켓 등록
|
||||
// @Description 에셋을 마켓에 등록합니다
|
||||
// @Tags Chain - Transactions
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.ListOnMarketRequest true "등록 정보"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/market/list [post]
|
||||
func (h *Handler) ListOnMarket(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -210,6 +328,20 @@ func (h *Handler) ListOnMarket(c *fiber.Ctx) error {
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
// BuyFromMarket godoc
|
||||
// @Summary 마켓 구매
|
||||
// @Description 마켓에서 매물을 구매합니다
|
||||
// @Tags Chain - Transactions
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.BuyFromMarketRequest true "구매 정보"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/market/buy [post]
|
||||
func (h *Handler) BuyFromMarket(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -231,6 +363,20 @@ func (h *Handler) BuyFromMarket(c *fiber.Ctx) error {
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
// CancelListing godoc
|
||||
// @Summary 마켓 등록 취소
|
||||
// @Description 마켓에 등록한 매물을 취소합니다
|
||||
// @Tags Chain - Transactions
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.CancelListingRequest true "취소 정보"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/market/cancel [post]
|
||||
func (h *Handler) CancelListing(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -252,6 +398,20 @@ func (h *Handler) CancelListing(c *fiber.Ctx) error {
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
// EquipItem godoc
|
||||
// @Summary 아이템 장착
|
||||
// @Description 에셋을 장비 슬롯에 장착합니다
|
||||
// @Tags Chain - Transactions
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.EquipItemRequest true "장착 정보"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/inventory/equip [post]
|
||||
func (h *Handler) EquipItem(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -274,6 +434,20 @@ func (h *Handler) EquipItem(c *fiber.Ctx) error {
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
// UnequipItem godoc
|
||||
// @Summary 아이템 장착 해제
|
||||
// @Description 에셋의 장비 슬롯 장착을 해제합니다
|
||||
// @Tags Chain - Transactions
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.UnequipItemRequest true "해제 정보"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/inventory/unequip [post]
|
||||
func (h *Handler) UnequipItem(c *fiber.Ctx) error {
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
@@ -297,6 +471,21 @@ func (h *Handler) UnequipItem(c *fiber.Ctx) error {
|
||||
|
||||
// ---- Operator (Admin) Transaction Handlers ----
|
||||
|
||||
// MintAsset godoc
|
||||
// @Summary 에셋 발행 (관리자)
|
||||
// @Description 새 에셋을 발행합니다
|
||||
// @Tags Chain - Admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.MintAssetRequest true "발행 정보"
|
||||
// @Success 201 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 403 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/admin/mint [post]
|
||||
func (h *Handler) MintAsset(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
TemplateID string `json:"templateId"`
|
||||
@@ -316,6 +505,21 @@ func (h *Handler) MintAsset(c *fiber.Ctx) error {
|
||||
return c.Status(fiber.StatusCreated).JSON(result)
|
||||
}
|
||||
|
||||
// GrantReward godoc
|
||||
// @Summary 보상 지급 (관리자)
|
||||
// @Description 유저에게 토큰 및 에셋 보상을 지급합니다
|
||||
// @Tags Chain - Admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.GrantRewardRequest true "보상 정보"
|
||||
// @Success 201 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 403 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/admin/reward [post]
|
||||
func (h *Handler) GrantReward(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
RecipientPubKey string `json:"recipientPubKey"`
|
||||
@@ -335,6 +539,21 @@ func (h *Handler) GrantReward(c *fiber.Ctx) error {
|
||||
return c.Status(fiber.StatusCreated).JSON(result)
|
||||
}
|
||||
|
||||
// RegisterTemplate godoc
|
||||
// @Summary 템플릿 등록 (관리자)
|
||||
// @Description 새 에셋 템플릿을 등록합니다
|
||||
// @Tags Chain - Admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.RegisterTemplateRequest true "템플릿 정보"
|
||||
// @Success 201 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 401 {object} docs.ErrorResponse
|
||||
// @Failure 403 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/chain/admin/template [post]
|
||||
func (h *Handler) RegisterTemplate(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
ID string `json:"id"`
|
||||
@@ -357,7 +576,19 @@ func (h *Handler) RegisterTemplate(c *fiber.Ctx) error {
|
||||
|
||||
// ---- Internal Handlers (game server, username-based) ----
|
||||
|
||||
// InternalGrantReward grants reward by username. For game server use.
|
||||
// InternalGrantReward godoc
|
||||
// @Summary 보상 지급 (내부 API)
|
||||
// @Description username으로 유저에게 보상을 지급합니다 (게임 서버용)
|
||||
// @Tags Internal - Chain
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.InternalGrantRewardRequest true "보상 정보"
|
||||
// @Success 201 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/internal/chain/reward [post]
|
||||
func (h *Handler) InternalGrantReward(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
Username string `json:"username"`
|
||||
@@ -377,7 +608,19 @@ func (h *Handler) InternalGrantReward(c *fiber.Ctx) error {
|
||||
return c.Status(fiber.StatusCreated).JSON(result)
|
||||
}
|
||||
|
||||
// InternalMintAsset mints an asset by username. For game server use.
|
||||
// InternalMintAsset godoc
|
||||
// @Summary 에셋 발행 (내부 API)
|
||||
// @Description username으로 에셋을 발행합니다 (게임 서버용)
|
||||
// @Tags Internal - Chain
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param Idempotency-Key header string true "멱등성 키"
|
||||
// @Param body body docs.InternalMintAssetRequest true "발행 정보"
|
||||
// @Success 201 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/internal/chain/mint [post]
|
||||
func (h *Handler) InternalMintAsset(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
TemplateID string `json:"templateId"`
|
||||
@@ -397,7 +640,17 @@ func (h *Handler) InternalMintAsset(c *fiber.Ctx) error {
|
||||
return c.Status(fiber.StatusCreated).JSON(result)
|
||||
}
|
||||
|
||||
// InternalGetBalance returns balance by username. For game server use.
|
||||
// InternalGetBalance godoc
|
||||
// @Summary 잔액 조회 (내부 API)
|
||||
// @Description username으로 잔액을 조회합니다 (게임 서버용)
|
||||
// @Tags Internal - Chain
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param username query string true "유저명"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/internal/chain/balance [get]
|
||||
func (h *Handler) InternalGetBalance(c *fiber.Ctx) error {
|
||||
username := c.Query("username")
|
||||
if !validID(username) {
|
||||
@@ -410,7 +663,19 @@ func (h *Handler) InternalGetBalance(c *fiber.Ctx) error {
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
// InternalGetAssets returns assets by username. For game server use.
|
||||
// InternalGetAssets godoc
|
||||
// @Summary 에셋 목록 조회 (내부 API)
|
||||
// @Description username으로 에셋 목록을 조회합니다 (게임 서버용)
|
||||
// @Tags Internal - Chain
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param username query string true "유저명"
|
||||
// @Param offset query int false "시작 위치" default(0)
|
||||
// @Param limit query int false "조회 수" default(50)
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/internal/chain/assets [get]
|
||||
func (h *Handler) InternalGetAssets(c *fiber.Ctx) error {
|
||||
username := c.Query("username")
|
||||
if !validID(username) {
|
||||
@@ -425,7 +690,17 @@ func (h *Handler) InternalGetAssets(c *fiber.Ctx) error {
|
||||
return c.Send(result)
|
||||
}
|
||||
|
||||
// InternalGetInventory returns inventory by username. For game server use.
|
||||
// InternalGetInventory godoc
|
||||
// @Summary 인벤토리 조회 (내부 API)
|
||||
// @Description username으로 인벤토리를 조회합니다 (게임 서버용)
|
||||
// @Tags Internal - Chain
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param username query string true "유저명"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 400 {object} docs.ErrorResponse
|
||||
// @Failure 500 {object} docs.ErrorResponse
|
||||
// @Router /api/internal/chain/inventory [get]
|
||||
func (h *Handler) InternalGetInventory(c *fiber.Ctx) error {
|
||||
username := c.Query("username")
|
||||
if !validID(username) {
|
||||
|
||||
Reference in New Issue
Block a user