Skip to content

Commit

Permalink
Merge pull request #1700 from innovationacademy-kr/dev
Browse files Browse the repository at this point in the history
DEV TO MAIN#1695
  • Loading branch information
saewoo1 authored Oct 25, 2024
2 parents e91ca8b + 54ef762 commit 78cab37
Show file tree
Hide file tree
Showing 21 changed files with 253 additions and 68 deletions.
4 changes: 3 additions & 1 deletion backend/database/cabi_local.sql
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,8 @@ VALUES (1, 0, 'EXTENSION_PREV', 'EXTENSION'),
(17, 200, 'ADMIN_REWARD_200', 'ADMIN_REWARD'),
(18, 500, 'ADMIN_REWARD_500', 'ADMIN_REWARD'),
(19, 1000, 'ADMIN_REWARD_1000', 'ADMIN_REWARD'),
(20, 2000, 'ADMIN_REWARD_2000', 'ADMIN_REWARD');
(20, 2000, 'ADMIN_REWARD_2000', 'ADMIN_REWARD'),
(21, 10, 'ADMIN_REWARD_COIN', 'ADMIN_REWARD');
/*!40000 ALTER TABLE `item`
ENABLE KEYS */;
UNLOCK TABLES;
Expand All @@ -758,6 +759,7 @@ CREATE TABLE `item_history`
`user_id` bigint(20) NOT NULL,
`purchase_at` datetime(6) NOT NULL,
`used_at` datetime(6) DEFAULT NULL,
`amount` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `item_history_item_id` FOREIGN KEY (`item_id`) REFERENCES `item` (`id`),
CONSTRAINT `item_history_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.ftclub.cabinet.admin.dto;

import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.ftclub.cabinet.item.domain.Sku;

@Getter
@AllArgsConstructor
public class AdminCoinAssignRequestDto {

private List<Long> userIds;
private Sku itemSku;
private Long amount;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
package org.ftclub.cabinet.admin.item.controller;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.ftclub.cabinet.admin.dto.AdminItemHistoryPaginationDto;
import org.ftclub.cabinet.admin.item.service.AdminItemFacadeService;
import org.ftclub.cabinet.admin.statistics.service.AdminStatisticsFacadeService;
import org.ftclub.cabinet.auth.domain.AuthGuard;
import org.ftclub.cabinet.auth.domain.AuthLevel;
import org.ftclub.cabinet.dto.ItemAssignRequestDto;
import org.ftclub.cabinet.dto.ItemCreateDto;
import org.ftclub.cabinet.dto.ItemDetailsDto;
import org.ftclub.cabinet.dto.ItemStoreDto;
import org.ftclub.cabinet.dto.ItemStoreResponseDto;
import org.ftclub.cabinet.item.domain.Item;
import org.ftclub.cabinet.item.domain.ItemType;
import org.ftclub.cabinet.item.service.ItemQueryService;
import org.ftclub.cabinet.log.Logging;
import org.ftclub.cabinet.mapper.ItemMapper;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -24,8 +36,8 @@
public class AdminItemController {

private final AdminItemFacadeService adminItemFacadeService;
private final AdminStatisticsFacadeService adminStatisticsFacadeService;

private final ItemQueryService itemQueryService;
private final ItemMapper itemMapper;

@PostMapping("")
@AuthGuard(level = AuthLevel.ADMIN_ONLY)
Expand All @@ -34,11 +46,29 @@ public void createItem(@RequestBody ItemCreateDto itemCreateDto) {
itemCreateDto.getSku(), itemCreateDto.getType());
}

@GetMapping("")
@AuthGuard(level = AuthLevel.ADMIN_ONLY)
public ItemStoreResponseDto getAllItems() {
List<Item> allItems = itemQueryService.getAllItems();
Map<ItemType, List<ItemDetailsDto>> itemMap = allItems.stream()
.collect(groupingBy(Item::getType,
mapping(itemMapper::toItemDetailsDto, Collectors.toList())));
List<ItemStoreDto> result = itemMap.entrySet().stream()
.map(entry -> {
ItemStoreDto itemStoreDto = itemMapper.toItemStoreDto(entry.getKey(),
entry.getValue());
itemStoreDto.sortBySkuASC();
return itemStoreDto;
})
.collect(Collectors.toList());
return new ItemStoreResponseDto(result);
}

@PostMapping("/assign")
@AuthGuard(level = AuthLevel.ADMIN_ONLY)
public void assignItem(@RequestBody ItemAssignRequestDto itemAssignRequestDto) {
adminItemFacadeService.assignItem(itemAssignRequestDto.getUserIds(),
itemAssignRequestDto.getItemSku());
itemAssignRequestDto.getItemSku(), itemAssignRequestDto.getAmount());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
import org.ftclub.cabinet.item.service.ItemCommandService;
import org.ftclub.cabinet.item.service.ItemHistoryCommandService;
import org.ftclub.cabinet.item.service.ItemHistoryQueryService;
import org.ftclub.cabinet.item.service.ItemPolicyService;
import org.ftclub.cabinet.item.service.ItemQueryService;
import org.ftclub.cabinet.item.service.ItemRedisService;
import org.ftclub.cabinet.mapper.ItemMapper;
import org.ftclub.cabinet.user.service.UserCommandService;
import org.ftclub.cabinet.user.service.UserQueryService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
Expand All @@ -33,34 +32,30 @@ public class AdminItemFacadeService {
private final ItemCommandService itemCommandService;
private final ItemHistoryQueryService itemHistoryQueryService;
private final ItemHistoryCommandService itemHistoryCommandService;
private final ItemPolicyService itemPolicyService;
private final ItemMapper itemMapper;

private final ItemRedisService itemRedisService;
private final UserCommandService userCommandService;
private final UserQueryService userQueryService;

@Transactional
public void createItem(Integer Price, Sku sku, ItemType type) {
itemCommandService.createItem(Price, sku, type);
}

@Transactional
public void assignItem(List<Long> userIds, Sku sku) {
public void assignItem(List<Long> userIds, Sku sku, Long amount) {
Item item = itemQueryService.getBySku(sku);
Long price = item.getPrice();
LocalDateTime now = null;
if (price > 0) {
Long coinAmount = item.getPrice();
if (sku.equals(Sku.ADMIN_REWARD_COIN)) {
itemPolicyService.verifyCoinAmount(amount);
coinAmount = amount;
}
if (coinAmount > 0) {
now = LocalDateTime.now();
userIds.forEach(userId -> {
long coinAmount = userQueryService.getUser(userId).getCoin();
itemRedisService.saveCoinCount(userId, coinAmount + item.getPrice());

long totalCoinSupply = itemRedisService.getTotalCoinSupply();
itemRedisService.saveTotalCoinSupply(totalCoinSupply + item.getPrice());
userCommandService.updateCoinAmount(userId, price);
});
userCommandService.addBulkCoin(userIds, coinAmount);
}
itemHistoryCommandService.createItemHistories(userIds, item.getId(), now);
itemHistoryCommandService.createItemHistories(userIds, item.getId(), now, coinAmount);
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
public class CoinHistoryDto {

private LocalDateTime date;
private Integer amount;
private Long amount;
private String history;
private String itemDetails;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ public class ItemAssignRequestDto {

private Sku itemSku;
private List<Long> userIds;
private Long amount;
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ public enum ExceptionStatus {
INVALID_JWT_TOKEN(HttpStatus.BAD_REQUEST, "토큰이 없거나, 유효하지 않은 JWT 토큰입니다."),
NOT_FOUND_SECTION(HttpStatus.BAD_REQUEST, "사물함 구역 정보를 찾을 수 없습니다."),
ITEM_NOT_OWNED(HttpStatus.BAD_REQUEST, "해당 아이템을 보유하고 있지 않습니다"),
ITEM_USE_DUPLICATED(HttpStatus.FORBIDDEN, "아이템이 중복 사용되었습니다.");
ITEM_USE_DUPLICATED(HttpStatus.FORBIDDEN, "아이템이 중복 사용되었습니다."),
INVALID_AMOUNT(HttpStatus.BAD_REQUEST, "코인 지급양은 비어있을 수 없습니다.");

final private int statusCode;
final private String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,14 @@ public class ItemHistory {
@Column(name = "USER_ID", nullable = false)
private Long userId;

@Column(name = "AMOUNT", nullable = true)
private Long amount;

protected ItemHistory(long userId, long itemId, LocalDateTime usedAt) {
protected ItemHistory(Long userId, Long itemId, LocalDateTime assignedAt, Long amount) {
this.userId = userId;
this.itemId = itemId;
this.usedAt = usedAt;
this.usedAt = assignedAt;
this.amount = amount;
}

/**
Expand All @@ -88,8 +91,8 @@ protected ItemHistory(long userId, long itemId, LocalDateTime usedAt) {
* @param usedAt 아이템 사용일자
* @return 아이템 히스토리 객체 {@link ItemHistory}
*/
public static ItemHistory of(long userId, long itemId, LocalDateTime usedAt) {
ItemHistory itemHistory = new ItemHistory(userId, itemId, usedAt);
public static ItemHistory of(Long userId, Long itemId, LocalDateTime usedAt, Long amount) {
ItemHistory itemHistory = new ItemHistory(userId, itemId, usedAt, amount);
if (!itemHistory.isValid()) {
throw ExceptionStatus.INVALID_ARGUMENT.asDomainException();
}
Expand Down
11 changes: 6 additions & 5 deletions backend/src/main/java/org/ftclub/cabinet/item/domain/Sku.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ public enum Sku {
COIN_REWARD_1000("동전 줍기 20일 보상"),
COIN_REWARD_2000("동전 줍기 20일 보상"),

ADMIN_REWARD_100("보상"),
ADMIN_REWARD_200("보상"),
ADMIN_REWARD_500("보상"),
ADMIN_REWARD_1000("보상"),
ADMIN_REWARD_2000("보상"),
ADMIN_REWARD_100("100"),
ADMIN_REWARD_200("200"),
ADMIN_REWARD_500("500"),
ADMIN_REWARD_1000("1000"),
ADMIN_REWARD_2000("2000"),
ADMIN_REWARD_COIN("지정 보상"),
;

private final String details;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ public CoinCollectionRewardResponseDto collectCoinAndIssueReward(Long userId) {
// DB에 코인 저장
Item coinCollect = itemQueryService.getBySku(Sku.COIN_COLLECT);
int reward = (int) (coinCollect.getPrice().longValue());
itemHistoryCommandService.createCoinItemHistory(userId, coinCollect.getId());
itemHistoryCommandService.createCoinItemHistory(userId, coinCollect.getId(),
coinCollect.getPrice());

// 출석 일자에 따른 랜덤 리워드 지급
Long coinCollectionCountInMonth =
Expand All @@ -206,7 +207,8 @@ public CoinCollectionRewardResponseDto collectCoinAndIssueReward(Long userId) {
Sku coinSku = itemPolicyService.getRewardSku(randomPercentage);
Item coinReward = itemQueryService.getBySku(coinSku);

itemHistoryCommandService.createCoinItemHistory(userId, coinReward.getId());
itemHistoryCommandService.createCoinItemHistory(userId, coinReward.getId(),
coinReward.getPrice());
reward += coinReward.getPrice();
}

Expand Down Expand Up @@ -310,7 +312,7 @@ public void purchaseItem(Long userId, Sku sku) {
itemPolicyService.verifyIsAffordable(userCoin, price);

// 아이템 구매 처리
itemHistoryCommandService.createItemHistory(user.getId(), item.getId());
itemHistoryCommandService.createItemHistory(user.getId(), item.getId(), price);

LockUtil.lockRedisCoin(userId, () -> {
// 코인 차감
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,39 @@ public class ItemHistoryCommandService {

private final ItemHistoryRepository itemHistoryRepository;

public void createItemHistory(Long userId, Long itemId) {
ItemHistory itemHistory = ItemHistory.of(userId, itemId, null);
public void createItemHistory(Long userId, Long itemId, Long amount) {
ItemHistory itemHistory = ItemHistory.of(userId, itemId, null, amount);
itemHistoryRepository.save(itemHistory);
}

public void createItemHistories(List<Long> userIds, Long itemId, LocalDateTime usedAt) {
public void createItemHistories(List<Long> userIds, Long itemId, LocalDateTime usedAt,
Long amount) {
List<ItemHistory> itemHistories = userIds.stream()
.map(userId -> ItemHistory.of(userId, itemId, usedAt))
.map(userId -> ItemHistory.of(userId, itemId, usedAt, amount))
.collect(Collectors.toList());
itemHistoryRepository.saveAll(itemHistories);
}

public void createCoinItemHistory(Long userId, Long itemId) {
ItemHistory coinCollectItemHistory = ItemHistory.of(userId, itemId, LocalDateTime.now());
public void createCoinItemHistory(Long userId, Long itemId, Long amount) {
ItemHistory coinCollectItemHistory = ItemHistory.of(userId, itemId, LocalDateTime.now(),
amount);
itemHistoryRepository.save(coinCollectItemHistory);
}

/**
* 관리자가 코인 지급 시 ItemHistory에 기록을 남깁니다
*
* @param userIds
* @param itemId
* @param usedAt
* @param amount
*/
public void createCoinAssignHistory(List<Long> userIds, Long itemId, LocalDateTime usedAt,
Long amount) {
List<ItemHistory> itemHistories = userIds.stream()
.map(userId -> ItemHistory.of(userId, itemId, usedAt, amount))
.collect(Collectors.toList());

itemHistoryRepository.saveAll(itemHistories);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,10 @@ public Sku getRewardSku(int randomPercentage) {
return Sku.COIN_REWARD_2000;
}
}

public void verifyCoinAmount(Long amount) {
if (amount == null) {
throw ExceptionStatus.INVALID_AMOUNT.asServiceException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ public interface ItemMapper {
ItemMapper INSTANCE = Mappers.getMapper(ItemMapper.class);

@Mapping(target = "date", source = "itemHistory.purchaseAt")
@Mapping(target = "amount", source = "item.price")
@Mapping(target = "history", source = "item.type.name")
@Mapping(target = "itemDetails", source = "item.sku.details")
@Mapping(target = "amount", source = "itemHistory.amount")
CoinHistoryDto toCoinHistoryDto(ItemHistory itemHistory, Item item);

@Mapping(target = "itemSku", source = "item.sku")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,10 @@ public interface UserRepository extends JpaRepository<User, Long> {

@Query("SELECT u FROM User u WHERE u.deletedAt IS NULL")
List<User> findAllDeletedAtIsNull();

@Modifying(clearAutomatically = true)
@Query("UPDATE User u "
+ "SET u.coin = u.coin + :amount "
+ "WHERE u.id IN :userIds")
void updateBulkUserCoin(@Param("userIds") List<Long> userIds, @Param("amount") Long amount);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public void issueLentExtension() {
users.forEach(user -> {
Long userId = user.getId();
user.addCoin(coinRewardItem.getPrice());
itemHistoryCommandService.createCoinItemHistory(userId, coinRewardItem.getId());
itemHistoryCommandService.createCoinItemHistory(userId, coinRewardItem.getId(),
coinRewardItem.getPrice());
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ftclub.cabinet.user.service;

import java.time.LocalDateTime;
import java.util.List;
import javax.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.ftclub.cabinet.auth.domain.FtProfile;
Expand Down Expand Up @@ -115,4 +116,8 @@ public void updateCoinAmount(Long userId, Long reward) {
.orElseThrow(ExceptionStatus.NOT_FOUND_USER::asServiceException);
user.addCoin(reward);
}

public void addBulkCoin(List<Long> userIds, Long amount) {
userRepository.updateBulkUserCoin(userIds, amount);
}
}
2 changes: 1 addition & 1 deletion config
Loading

0 comments on commit 78cab37

Please sign in to comment.