Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
0160857
Feat: 팝업 전용 건의 종류 추가
dlwls423 Jul 1, 2026
050afa6
Merge pull request #293 from DevKor-github/feat/popup
dlwls423 Jul 1, 2026
16e372e
feat: add pharmacy enum type
JokeBear777 Jul 26, 2026
0c9d4c9
Merge pull request #294 from DevKor-github/feat/pharmacy
JokeBear777 Jul 26, 2026
c9f640c
feat: 푸시 installation 등록 및 비활성화 API 추가
JokeBear777 Jul 31, 2026
fa6bb9a
docs: restore SecurityConfig comments
JokeBear777 Jul 31, 2026
6948eaa
Merge pull request #295 from DevKor-github/feat/push-installation-api
JokeBear777 Aug 2, 2026
2cac9a2
docs: standardize push installation Swagger descriptions
JokeBear777 Aug 2, 2026
5604a6a
feat: add push dispatch and message models
JokeBear777 Aug 2, 2026
676e6d8
refactor: organize push domain entities and types
JokeBear777 Aug 2, 2026
0325204
refactor: organize push dispatch components by responsibility
JokeBear777 Aug 2, 2026
d7b8a20
fix: align push payload contract and TEST action validation
JokeBear777 Aug 2, 2026
e5f2ed6
chore: 로컬 환경 설정 파일 제외
JokeBear777 Aug 2, 2026
ea18594
Feat: 유저 테이블에 레벨 컬럼 추가
vxlmlxv Aug 2, 2026
9cec8b7
Feat: 상점에서 사용할 포인트 추가
vxlmlxv Aug 2, 2026
6f80547
Feat: 기존 유저 레벨/포인트 초기값 세팅 로직 추가
vxlmlxv Aug 2, 2026
b3f9838
Feat: 캐릭터 엔티티 추가
vxlmlxv Aug 2, 2026
daf4a27
Feat: 상점 조회 및 캐릭터 구매/장착 기능 추가
vxlmlxv Aug 2, 2026
13ae479
Feat: 관리자 캐릭터 관리 기능 추가
vxlmlxv Aug 2, 2026
1540a26
Feat: 캐릭터 초기 데이터 추가
vxlmlxv Aug 2, 2026
f9a7706
Merge pull request #296 from DevKor-github/feat/push-dispatch-model
JokeBear777 Aug 3, 2026
4610ac9
feat: add Expo push API client
JokeBear777 Aug 3, 2026
dc38a53
fix: Expo Push 응답 검증 및 예외 원인 보존
JokeBear777 Aug 3, 2026
c2f2fbf
docs: 푸시 테스트 API 설명 및 Swagger 문서화 추가
JokeBear777 Aug 3, 2026
c5a1b15
Merge pull request #298 from DevKor-github/feat/push-expo-client
JokeBear777 Aug 3, 2026
210ec00
feat: Expo push ticket 발송 worker 구현
JokeBear777 Aug 4, 2026
8a4d0bc
feat : expo push receip worker 구현
JokeBear777 Aug 4, 2026
d7091b5
test: 푸시 알림 발송 파이프라인 통합 테스트 추가
JokeBear777 Aug 4, 2026
49a1a08
test: 테스트 환경 Logback 설정 추가
JokeBear777 Aug 4, 2026
66942d0
chore: 미사용 receipt 컬럼 마이그레이션 스크립트 제거
JokeBear777 Aug 4, 2026
198316b
fix: 푸시 worker 고착 메시지 복구 처리
JokeBear777 Aug 4, 2026
99468a6
Merge pull request #300 from DevKor-github/feat/push-send-worker
JokeBear777 Aug 4, 2026
53d712a
fix: recovery worker cron YAML 설정 수정
JokeBear777 Aug 4, 2026
94a563c
Merge pull request #301 from DevKor-github/fix/push-recovery-cron-yaml
JokeBear777 Aug 4, 2026
8e97b3b
fix: recovery worker cron 프로퍼티 참조 수정
JokeBear777 Aug 4, 2026
a6b59f0
Merge pull request #302 from DevKor-github/fix/push-recovery-cron-yaml
JokeBear777 Aug 4, 2026
66312d4
fix: push 통합테스트에 test 프로필 적용
JokeBear777 Aug 4, 2026
1dbbfa0
Merge pull request #303 from DevKor-github/fix/push-integration-test-…
JokeBear777 Aug 4, 2026
f794578
feat: 관리자 푸시 발송 관리 API 추가
JokeBear777 Aug 5, 2026
d14aa09
docs: 관리자 푸시 API 설명 추가
JokeBear777 Aug 5, 2026
5551909
Merge pull request #304 from DevKor-github/feat/push-admin-api
JokeBear777 Aug 5, 2026
95a762f
Merge branch 'develop' into feat/store
JokeBear777 Aug 5, 2026
a1ec357
fix: ResultCode 병합 오류 수정
JokeBear777 Aug 5, 2026
0dd6a63
Merge pull request #297 from DevKor-github/feat/store
JokeBear777 Aug 5, 2026
457225d
feat: 도메인 이벤트 기반 자동 푸시 알림 추가
JokeBear777 Aug 5, 2026
8a0405c
refactor: 도메인 푸시 문구 팩토리 분리
JokeBear777 Aug 5, 2026
974be03
feat: 푸시 이벤트 런타임 플래그 관리 추가
JokeBear777 Aug 5, 2026
9f1d95d
Merge pull request #305 from DevKor-github/feat/push-domain-events
JokeBear777 Aug 6, 2026
2a8e0b8
feat: 설문 예약 푸시 알림 추가
JokeBear777 Aug 6, 2026
385efba
feat: 설문 예약 푸시 알림 추가
JokeBear777 Aug 6, 2026
72b4f25
Merge pull request #306 from DevKor-github/feat/push-survey-schedule
JokeBear777 Aug 6, 2026
66e2c8b
fix: remove unused push database migration
JokeBear777 Aug 6, 2026
d24e18e
Merge pull request #307 from DevKor-github/fix/remove-push-db-migration
JokeBear777 Aug 6, 2026
2f17d3a
feat: add admin social login endpoint
818jsy Aug 6, 2026
ef17774
fix: remove unused public IP action from deploy workflows
818jsy Aug 6, 2026
b1769a1
chore: trigger dev deployment
818jsy Aug 6, 2026
c6f1549
ci: allow manual dev deployment
818jsy Aug 6, 2026
d45d370
feat: add usage surveys and review author profiles
818jsy Aug 6, 2026
4351ec8
feat: configure push event app variant
818jsy Aug 6, 2026
b11080e
feat: send push events to dev and production
818jsy Aug 6, 2026
e7f084d
feat: add active-user push broadcast console
818jsy Aug 6, 2026
ab0c34e
feat: support Kakao web admin audience
818jsy Aug 6, 2026
528c4c1
fix: show zero recipients in push preview
818jsy Aug 6, 2026
fd83d59
chore: remove duplicate push admin panel
818jsy Aug 6, 2026
83aaa83
fix: derive mobile login identity from verified token
818jsy Aug 6, 2026
d1f948a
fix: route push events to isolated app variants
818jsy Aug 6, 2026
9e58110
chore: defer login callback audience changes
818jsy Aug 6, 2026
60bcd09
fix: target dev and preview from dev server
818jsy Aug 6, 2026
421afc6
fix: handle Kakao accounts without email
818jsy Aug 7, 2026
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
5 changes: 1 addition & 4 deletions .github/workflows/cd-dev.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: CI/CD using github actions & docker

on:
workflow_dispatch:
push:
branches:
- develop
Expand Down Expand Up @@ -53,10 +54,6 @@ jobs:
needs: push_to_registry
runs-on: ubuntu-latest
steps:
- name: Get Github action IP
id: ip
uses: haythem/public-ip@v1.2

- name: Deploy to prod
if: contains(github.ref, 'develop') || contains(github.ref, 'main')
uses: appleboy/ssh-action@master
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ jobs:
needs: push_to_registry
runs-on: ubuntu-latest
steps:
- name: Get Github action IP
id: ip
uses: haythem/public-ip@v1.2

- name: Deploy to prod
if: contains(github.ref, 'develop') || contains(github.ref, 'main')
uses: appleboy/ssh-action@master
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,8 @@ out/
###claude###
.claude
.serena

# Local-only config
/src/main/resources/application-local.yml
/.env.local
/docker-compose.local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package devkor.com.teamcback.domain.ble.event;

import java.time.LocalDateTime;

public record PlaceBecameVacantEvent(
Long placeId,
Long bleDataId,
LocalDateTime occurredAt
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@

public interface BLEDataRepository extends JpaRepository<BLEData, Long> {
Optional<BLEData> findTopByDeviceOrderByLastTimeDesc(BLEDevice device);
Optional<BLEData> findTopByDeviceOrderByLastTimeDescIdDesc(BLEDevice device);
List<BLEData> findAllByDeviceAndLastTimeBetweenOrderByLastTimeAsc(BLEDevice device, LocalDateTime start, LocalDateTime end);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import devkor.com.teamcback.domain.ble.entity.BLEData;
import devkor.com.teamcback.domain.ble.entity.BLEDevice;
import devkor.com.teamcback.domain.ble.entity.BLEstatus;
import devkor.com.teamcback.domain.ble.event.PlaceBecameVacantEvent;
import devkor.com.teamcback.domain.ble.repository.BLEDataRepository;
import devkor.com.teamcback.domain.ble.repository.BLEDeviceRepository;
import devkor.com.teamcback.domain.place.entity.Place;
Expand All @@ -17,6 +18,7 @@
import devkor.com.teamcback.global.response.ResultCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -34,6 +36,7 @@ public class BLEService {
private final BLEDeviceRepository bledeviceRepository;
private final BLEDataRepository bleDataRepository;
private final PlaceRepository placeRepository;
private final ApplicationEventPublisher eventPublisher;

// 평균 구해올 시간대 라벨
private static final int[] TIME_SLOTS = {7, 10, 13, 16, 19, 22};
Expand All @@ -46,6 +49,11 @@ public class BLEService {
@Transactional
public UpdateBLERes updateBLE(UpdateBLEReq updateBLEReq) {
BLEDevice bleDevice = bledeviceRepository.findByDeviceName(updateBLEReq.getDeviceName());
if (bleDevice == null) {
throw new GlobalException(ResultCode.NOT_FOUND_DEVICE_NAME);
}
BLEData previousData = bleDataRepository.findTopByDeviceOrderByLastTimeDescIdDesc(bleDevice)
.orElse(null);
int capacity = bleDevice.getCapacity();
int people = getBlEPeople(updateBLEReq.getLastCount(), bleDevice);
double final_ratio = (double) people / capacity;
Expand All @@ -61,9 +69,29 @@ public UpdateBLERes updateBLE(UpdateBLEReq updateBLEReq) {
bleData.setLastTime(updateBLEReq.getLastTime());
bleDataRepository.save(bleData);

if (becameVacant(previousData, status) && bleDevice.getPlace() != null) {
eventPublisher.publishEvent(new PlaceBecameVacantEvent(
bleDevice.getPlace().getId(),
bleData.getId(),
bleData.getLastTime()
));
}

return new UpdateBLERes(bleData);
}

private boolean becameVacant(
BLEData previousData,
BLEstatus newStatus
) {
if (!BLEstatus.VACANT.equals(newStatus) || previousData == null) {
return false;
}

return BLEstatus.AVAILABLE.equals(previousData.getLastStatus())
|| BLEstatus.CROWDED.equals(previousData.getLastStatus());
}

private int getBlEPeople(int lastCount, BLEDevice bleDevice) {
if (bleDevice == null) throw new GlobalException(ResultCode.NOT_FOUND_DEVICE_NAME);
double ratio = bleDevice.getRatio();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,15 @@ List<Category> findCategoriesByUserAndLocationTypeAndLocationId(

@Query("SELECT c FROM Category c LEFT JOIN FETCH c.categoryBookmarkList WHERE c.user = :user")
List<Category> findByUser(@Param("user") User user);

@Query("""
SELECT DISTINCT c.user.userId FROM CategoryBookmark cb
JOIN cb.category c
JOIN cb.bookmark b
WHERE c.user IS NOT NULL AND b.locationType = :locationType AND b.locationId = :locationId
""")
List<Long> findDistinctUserIdsByLocationTypeAndLocationId(
@Param("locationType") LocationType locationType,
@Param("locationId") Long locationId
);
}
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()));
}
}
Loading
Loading