Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package devkor.com.teamcback.domain.character.controller;

import devkor.com.teamcback.domain.character.dto.request.CreateCharacterReq;
import devkor.com.teamcback.domain.character.dto.request.ModifyCharacterReq;
import devkor.com.teamcback.domain.character.dto.response.CreateCharacterRes;
import devkor.com.teamcback.domain.character.dto.response.DeleteCharacterRes;
import devkor.com.teamcback.domain.character.dto.response.GetAdminCharacterListRes;
import devkor.com.teamcback.domain.character.dto.response.GrantCharacterRes;
import devkor.com.teamcback.domain.character.dto.response.ModifyCharacterRes;
import devkor.com.teamcback.domain.character.service.AdminStoreService;
import devkor.com.teamcback.global.response.CommonResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/admin/store")
public class AdminStoreController {
private final AdminStoreService adminStoreService;

/**
* 캐릭터 목록 조회 (비활성 포함)
*/
@Operation(summary = "관리자 캐릭터 목록 조회", description = "비활성 캐릭터를 포함한 전체 캐릭터 조회")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
})
@GetMapping("")
public CommonResponse<GetAdminCharacterListRes> getCharacterList() {
return CommonResponse.success(adminStoreService.getCharacterList());
}

/**
* 캐릭터 생성
* @param req 캐릭터 정보 (이미지, 가격 포함)
*/
@Operation(summary = "캐릭터 생성", description = "캐릭터 생성 (이미지 필수, 가격은 0 이상의 포인트)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "400", description = "잘못된 입력",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@PostMapping(value = "", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public CommonResponse<CreateCharacterRes> createCharacter(
@Parameter(description = "캐릭터 정보")
@ModelAttribute CreateCharacterReq req) {
return CommonResponse.success(adminStoreService.createCharacter(req));
}

/**
* 캐릭터 수정
* @param characterId 캐릭터 ID
* @param req 캐릭터 정보 (이미지 미첨부 시 기존 이미지 유지)
*/
@Operation(summary = "캐릭터 수정", description = "캐릭터 수정 (이미지 미첨부 시 기존 이미지 유지)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "404", description = "Not Found",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@PutMapping(value = "/{characterId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public CommonResponse<ModifyCharacterRes> modifyCharacter(
@Parameter(description = "캐릭터 ID", example = "1")
@PathVariable(name = "characterId") Long characterId,
@Parameter(description = "캐릭터 정보")
@ModelAttribute ModifyCharacterReq req) {
return CommonResponse.success(adminStoreService.modifyCharacter(characterId, req));
}

/**
* 캐릭터 삭제
* @param characterId 캐릭터 ID
*/
@Operation(summary = "캐릭터 삭제", description = "캐릭터 삭제 (구매한 사용자가 있으면 삭제 불가, isActive=false로 숨김 처리 권장)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "404", description = "Not Found",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
@ApiResponse(responseCode = "409", description = "구매한 사용자가 있는 캐릭터",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@DeleteMapping("/{characterId}")
public CommonResponse<DeleteCharacterRes> deleteCharacter(
@Parameter(description = "캐릭터 ID", example = "1")
@PathVariable(name = "characterId") Long characterId) {
return CommonResponse.success(adminStoreService.deleteCharacter(characterId));
}

/**
* 캐릭터 수동 지급
* @param characterId 캐릭터 ID
* @param userId 사용자 ID
*/
@Operation(summary = "캐릭터 수동 지급", description = "특정 사용자에게 캐릭터 지급 (이벤트 보상용, 포인트 차감 없음)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "404", description = "Not Found",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
@ApiResponse(responseCode = "409", description = "이미 보유한 캐릭터",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@PostMapping("/{characterId}/grant/{userId}")
public CommonResponse<GrantCharacterRes> grantCharacter(
@Parameter(description = "캐릭터 ID", example = "1")
@PathVariable(name = "characterId") Long characterId,
@Parameter(description = "사용자 ID", example = "1")
@PathVariable(name = "userId") Long userId) {
return CommonResponse.success(adminStoreService.grantCharacter(characterId, userId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package devkor.com.teamcback.domain.character.controller;

import devkor.com.teamcback.domain.character.dto.response.EquipCharacterRes;
import devkor.com.teamcback.domain.character.dto.response.GetMyCharacterListRes;
import devkor.com.teamcback.domain.character.dto.response.GetStoreRes;
import devkor.com.teamcback.domain.character.dto.response.PurchaseCharacterRes;
import devkor.com.teamcback.domain.character.dto.response.UnequipCharacterRes;
import devkor.com.teamcback.domain.character.service.StoreService;
import devkor.com.teamcback.global.response.CommonResponse;
import devkor.com.teamcback.global.security.UserDetailsImpl;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/store")
public class StoreController {
private final StoreService storeService;

/**
* 스토어 조회 (보유 포인트 + 캐릭터 목록)
* @param userDetail 사용자 정보
*/
@Operation(summary = "스토어 조회", description = "보유 포인트와 전체 캐릭터 목록(보유/구매 가능/포인트 부족) 조회")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "404", description = "Not Found",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@GetMapping("")
public CommonResponse<GetStoreRes> getStore(
@Parameter(description = "사용자 정보")
@AuthenticationPrincipal UserDetailsImpl userDetail) {
return CommonResponse.success(storeService.getStore(userDetail.getUser().getUserId()));
}

/**
* 내 보유 캐릭터 목록 조회
* @param userDetail 사용자 정보
*/
@Operation(summary = "보유 캐릭터 목록 조회", description = "보유 포인트, 구매한 캐릭터, 대표 장착 캐릭터 조회")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "404", description = "Not Found",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@GetMapping("/my")
public CommonResponse<GetMyCharacterListRes> getMyCharacters(
@Parameter(description = "사용자 정보")
@AuthenticationPrincipal UserDetailsImpl userDetail) {
return CommonResponse.success(storeService.getMyCharacters(userDetail.getUser().getUserId()));
}

/**
* 캐릭터 구매
* @param userDetail 사용자 정보
* @param characterId 캐릭터 ID
*/
@Operation(summary = "캐릭터 구매", description = "보유 포인트를 차감하여 캐릭터 구매")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "400", description = "포인트 부족 또는 비활성 캐릭터",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
@ApiResponse(responseCode = "404", description = "Not Found",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
@ApiResponse(responseCode = "409", description = "이미 보유한 캐릭터",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@PostMapping("/{characterId}/purchase")
public CommonResponse<PurchaseCharacterRes> purchaseCharacter(
@Parameter(description = "사용자 정보")
@AuthenticationPrincipal UserDetailsImpl userDetail,
@Parameter(description = "캐릭터 ID", example = "1")
@PathVariable(name = "characterId") Long characterId) {
return CommonResponse.success(storeService.purchaseCharacter(userDetail.getUser().getUserId(), characterId));
}

/**
* 대표 캐릭터 장착
* @param userDetail 사용자 정보
* @param characterId 캐릭터 ID
*/
@Operation(summary = "대표 캐릭터 장착", description = "구매한 캐릭터를 대표 캐릭터로 장착")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "400", description = "보유하지 않은 캐릭터",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
@ApiResponse(responseCode = "404", description = "Not Found",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@PutMapping("/{characterId}/equip")
public CommonResponse<EquipCharacterRes> equipCharacter(
@Parameter(description = "사용자 정보")
@AuthenticationPrincipal UserDetailsImpl userDetail,
@Parameter(description = "캐릭터 ID", example = "1")
@PathVariable(name = "characterId") Long characterId) {
return CommonResponse.success(storeService.equipCharacter(userDetail.getUser().getUserId(), characterId));
}

/**
* 대표 캐릭터 장착 해제
* @param userDetail 사용자 정보
*/
@Operation(summary = "대표 캐릭터 장착 해제", description = "대표 캐릭터 장착 해제")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "404", description = "Not Found",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@DeleteMapping("/equip")
public CommonResponse<UnequipCharacterRes> unequipCharacter(
@Parameter(description = "사용자 정보")
@AuthenticationPrincipal UserDetailsImpl userDetail) {
return CommonResponse.success(storeService.unequipCharacter(userDetail.getUser().getUserId()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package devkor.com.teamcback.domain.character.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;

@Schema(description = "저장할 캐릭터 정보")
@Getter
@Setter
public class CreateCharacterReq {
@Schema(description = "캐릭터 이름", example = "아기 호랑이")
private String name;

@Schema(description = "캐릭터 설명", example = "10 포인트로 구매할 수 있는 캐릭터")
private String description;

@Schema(description = "캐릭터 대사", example = "같이 캠퍼스를 누벼볼까?")
private String quote;

@Schema(description = "구매 가격 (포인트)", example = "10")
private Integer price;

@Schema(description = "해금 레벨 (1~5, 1이면 제한 없음)", example = "2")
private Integer requiredLevel = 1;

@Schema(description = "정렬 순서", example = "1")
private Integer displayOrder = 0;

@Schema(description = "노출 여부", example = "true")
private boolean isActive = true;

@Schema(description = "캐릭터 이미지")
private MultipartFile image;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package devkor.com.teamcback.domain.character.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;

@Schema(description = "수정할 캐릭터 정보")
@Getter
@Setter
public class ModifyCharacterReq {
@Schema(description = "캐릭터 이름", example = "아기 호랑이")
private String name;

@Schema(description = "캐릭터 설명", example = "10 포인트로 구매할 수 있는 캐릭터")
private String description;

@Schema(description = "캐릭터 대사", example = "같이 캠퍼스를 누벼볼까?")
private String quote;

@Schema(description = "구매 가격 (포인트)", example = "10")
private Integer price;

@Schema(description = "해금 레벨 (1~5, 1이면 제한 없음)", example = "2")
private Integer requiredLevel = 1;

@Schema(description = "정렬 순서", example = "1")
private Integer displayOrder = 0;

@Schema(description = "노출 여부", example = "true")
private boolean isActive = true;

@Schema(description = "캐릭터 이미지 (미첨부 시 기존 이미지 유지)")
private MultipartFile image;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package devkor.com.teamcback.domain.character.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Schema(description = "캐릭터 생성 결과")
@Getter
public class CreateCharacterRes {
@Schema(description = "생성된 캐릭터 ID", example = "1")
private Long characterId;

public CreateCharacterRes(Long characterId) {
this.characterId = characterId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package devkor.com.teamcback.domain.character.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "캐릭터 삭제 결과")
public class DeleteCharacterRes {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package devkor.com.teamcback.domain.character.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Schema(description = "대표 캐릭터 장착 결과")
@Getter
public class EquipCharacterRes {
@Schema(description = "장착한 캐릭터 ID", example = "1")
private Long characterId;

public EquipCharacterRes(Long characterId) {
this.characterId = characterId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package devkor.com.teamcback.domain.character.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.Getter;

@Schema(description = "관리자용 캐릭터 목록")
@Getter
public class GetAdminCharacterListRes {
@Schema(description = "캐릭터 목록")
private List<GetAdminCharacterRes> characterList;

public GetAdminCharacterListRes(List<GetAdminCharacterRes> characterList) {
this.characterList = characterList;
}
}
Loading
Loading