Skip to content

Commit

Permalink
release: deploy to prod (#7)
Browse files Browse the repository at this point in the history
* feat: 장부 상세내역 수정 V2 api를 구현한다. (#4)

* feat: totalbalance 관련 에러코드를 추가한다.

* chore: name이 null인 경우 임시 닉네임을 기입한다.

* refactor: 소속에서 강퇴되어도 재입장이 가능하다. (#5)

* fix: 장부상세내역의 description을 300자 입력 가능토록 한다.

* feat: 카카오 계정 탈퇴 시 서비스를 unlink한다. (#6)

* feat: kakao adminKey를 프로퍼티에 추가한다.

* feat: 카카오 계정 탈퇴 시 서비스를 unlink한다.

* chore: docker-compose에 kakao Admin key를 추가한다.
  • Loading branch information
rlarltj authored Jun 20, 2024
1 parent ad3053a commit 413267e
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ services:
APPLE_TEAM_ID: ${APPLE_TEAM_ID}
APPLE_KEY_ID: ${APPLE_KEY_ID}
APPLE_CLIENT_ID: ${APPLE_CLIENT_ID}

KAKAO_ADMIN_KEY: ${KAKAO_ADMIN_KEY}
redis:
container_name: moneymong-redis
image: redis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class Agency extends BaseEntity {
@Enumerated(EnumType.STRING)
private AgencyType agencyType;

@OneToMany(mappedBy = "agency", cascade = CascadeType.PERSIST)
@OneToMany(mappedBy = "agency", cascade = CascadeType.ALL)
private List<AgencyUser> agencyUsers = new ArrayList<>();

@Column(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@ public void blockUser(Long userId, Long agencyId, BlockAgencyUserRequest request
validateStaffUserRole(staffUser);

AgencyUser targetUser = getAgencyUser(request.getUserId(), agencyId);

targetUser.updateAgencyUserRole(BLOCKED);

InvitationCodeCertification certification = getCertification(agencyId, request);

certification.revoke();

agencyUserRepository.delete(targetUser);
invitationCodeCertificationRepository.delete(certification);

Agency agency = getAgency(agencyId);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.moneymong.domain.ledger.api;

import com.moneymong.domain.ledger.api.request.*;
import com.moneymong.domain.ledger.api.response.LedgerDetailInfoView;
import com.moneymong.domain.ledger.api.response.ledger.LedgerInfoView;
import com.moneymong.domain.ledger.service.manager.LedgerDetailService;
import com.moneymong.domain.ledger.service.reader.LedgerReader;
import com.moneymong.global.security.token.dto.jwt.JwtAuthentication;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -18,6 +20,7 @@
@RequiredArgsConstructor
public class LedgerControllerV2 {
private final LedgerReader ledgerReader;
private final LedgerDetailService ledgerDetailService;

@Operation(summary = " 장부 내역 조회 API")
@GetMapping("/{id}")
Expand Down Expand Up @@ -57,4 +60,18 @@ public LedgerInfoView searchByFilter(
searchLedgerFilterRequest.getFundType()
);
}

@Operation(summary = "장부 상세 내역 수정 API")
@PutMapping("/ledger-detail/{detailId}")
public LedgerDetailInfoView updateLedger(
@AuthenticationPrincipal JwtAuthentication user,
@PathVariable("detailId") final Long ledgerDetailId,
@RequestBody @Valid final UpdateLedgerRequestV2 updateLedgerRequest
) {
return ledgerDetailService.updateLedgerDetailV2(
user.getId(),
ledgerDetailId,
updateLedgerRequest
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.moneymong.domain.ledger.api.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.ZonedDateTime;
import java.util.List;

@Getter
@NoArgsConstructor
public class UpdateLedgerRequestV2 {
@NotBlank(message = "storeInfo를 입력해주세요.")
@Size(min = 1, max = 20, message = "storeInfo 1자 - 20자 입력해주세요.")
private String storeInfo;

@NotNull(message = "amount를 입력해주세요.")
private Integer amount;

@NotBlank(message = "description를 입력해주세요.")
private String description;

@NotNull(message = "paymentDate를 입력해주세요.")
private ZonedDateTime paymentDate;

@Size(max = 12, message = "영수증 12개 이하 입력해주세요.")
private List<String> receiptImageUrls;

@Size(max = 12, message = "증빙 자료 12개 이하 입력해주세요.")
private List<String> documentImageUrls;
}
18 changes: 13 additions & 5 deletions src/main/java/com/moneymong/domain/ledger/entity/Ledger.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,23 @@ public void updateTotalBalance(int newAmount) {
.add(new BigDecimal(newAmount));

// 장부 금액 최소 초과 검증
validateTotalBalance(expectedAmount);

this.totalBalance = expectedAmount.intValue();
}

private void validateTotalBalance(BigDecimal expectedAmount) {
BigDecimal minValue = new BigDecimal("-999999999");
BigDecimal maxValue = new BigDecimal("999999999");
if (!(expectedAmount.compareTo(minValue) >= 0 &&
expectedAmount.compareTo(maxValue) <= 0)
) {
throw new BadRequestException(ErrorCode.INVALID_LEDGER_AMOUNT);

if (expectedAmount.compareTo(minValue) < 0) {
throw new BadRequestException(ErrorCode.LEDGER_AMOUNT_UNDERFLOW);
}

this.totalBalance = expectedAmount.intValue();
if (expectedAmount.compareTo(maxValue) > 0) {
throw new BadRequestException(ErrorCode.LEDGER_AMOUNT_OVERFLOW);

}
}

public static Ledger of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.moneymong.domain.agency.entity.enums.AgencyUserRole;
import com.moneymong.domain.agency.repository.AgencyUserRepository;
import com.moneymong.domain.ledger.api.request.UpdateLedgerRequest;
import com.moneymong.domain.ledger.api.request.UpdateLedgerRequestV2;
import com.moneymong.domain.ledger.api.response.LedgerDetailInfoView;
import com.moneymong.domain.ledger.entity.Ledger;
import com.moneymong.domain.ledger.entity.LedgerDetail;
Expand All @@ -13,7 +14,6 @@
import com.moneymong.domain.ledger.repository.LedgerDetailRepository;
import com.moneymong.domain.ledger.repository.LedgerDocumentRepository;
import com.moneymong.domain.ledger.repository.LedgerReceiptRepository;
import com.moneymong.domain.ledger.repository.LedgerRepository;
import com.moneymong.domain.ledger.service.mapper.LedgerAssembler;
import com.moneymong.domain.ledger.service.reader.LedgerDocumentReader;
import com.moneymong.domain.ledger.service.reader.LedgerReceiptReader;
Expand All @@ -38,7 +38,6 @@
@Slf4j
@RequiredArgsConstructor
public class LedgerDetailService {
private final LedgerRepository ledgerRepository;
private final LedgerAssembler ledgerAssembler;
private final LedgerReceiptReader ledgerReceiptReader;
private final LedgerDocumentReader ledgerDocumentReader;
Expand All @@ -47,6 +46,8 @@ public class LedgerDetailService {
private final LedgerDetailRepository ledgerDetailRepository;
private final LedgerReceiptRepository ledgerReceiptRepository;
private final LedgerDocumentRepository ledgerDocumentRepository;
private final LedgerReceiptManager ledgerReceiptManager;
private final LedgerDocumentManager ledgerDocumentManager;

@Transactional
public LedgerDetail createLedgerDetail(
Expand Down Expand Up @@ -124,6 +125,57 @@ public LedgerDetailInfoView updateLedgerDetail(
);
}

@Transactional
public LedgerDetailInfoView updateLedgerDetailV2(
Long userId,
Long ledgerDetailId,
UpdateLedgerRequestV2 updateLedgerRequest
) {

User user = getUser(userId);

LedgerDetail ledgerDetail = getLedgerDetail(ledgerDetailId);

Ledger ledger = ledgerDetail.getLedger();

AgencyUser agencyUser = getAgencyUser(user, ledgerDetail);

validateStaffUserRole(agencyUser.getAgencyUserRole());

int newAmount = AmountCalculatorByFundType.calculate(
ledgerDetail.getFundType(),
ledgerDetail.getAmount() - updateLedgerRequest.getAmount()
);

ledger.updateTotalBalance(-newAmount);

ledgerDetail.updateLedgerDetailInfo(
updateLedgerRequest.getStoreInfo(),
updateLedgerRequest.getAmount(),
updateLedgerRequest.getDescription(),
updateLedgerRequest.getPaymentDate()
);

updateBalance(ledger);

ledgerReceiptRepository.deleteByLedgerDetail(ledgerDetail);
ledgerDocumentRepository.deleteByLedgerDetail(ledgerDetail);

List<LedgerReceipt> receipts = ledgerReceiptManager.createReceipts(ledgerDetailId, updateLedgerRequest.getReceiptImageUrls());
List<LedgerDocument> ledgerDocuments = ledgerDocumentManager.createLedgerDocuments(ledgerDetailId, updateLedgerRequest.getDocumentImageUrls());

ledgerReceiptRepository.saveAll(receipts);
ledgerDocumentRepository.saveAll(ledgerDocuments);

return LedgerDetailInfoView.of(
ledgerDetail,
receipts,
ledgerDocuments,
user
);
}


@Transactional
public void removeLedgerDetail(
final Long userId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public void removeLedgerDocuments(

LedgerDocument ledgerDocument = ledgerDocumentRepository
.findById(documentId)
.orElseThrow(() -> new NotFoundException(ErrorCode.LEDGER_DOCUUMENT_NOT_FOUND));
.orElseThrow(() -> new NotFoundException(ErrorCode.LEDGER_DOCUMENT_NOT_FOUND));

ledgerDocumentRepository.delete(ledgerDocument);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public User save(User unsavedUser) {
public User registerUser(OAuthUserInfo oauthUserInfo) {
User newUser = User.of(
oauthUserInfo.getEmail(),
oauthUserInfo.getNickname(),
oauthUserInfo.getNickname() == null ? "유저" : oauthUserInfo.getNickname(),
oauthUserInfo.getProvider(),
oauthUserInfo.getOauthId()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ public enum ErrorCode {
INVALID_LEDGER_ACCESS(MoneymongConstant.FORBIDDEN, "LEDGER-002", "유효하지 않은 접근입니다."),
LEDGER_NOT_FOUND(MoneymongConstant.NOT_FOUND, "LEDGER-003", "장부가 존재하지 않습니다."),
LEDGER_DETAIL_NOT_FOUND(MoneymongConstant.NOT_FOUND, "LEDGER-004", "장부 상세 내역이 존재하지 않습니다."),
LEDGER_AMOUNT_OVERFLOW(MoneymongConstant.BAD_REQUEST,"LEDGER-005", "총 잔액은 최대 999,999,999원까지 기록이 가능합니다."),
LEDGER_RECEIPT_NOT_FOUND(MoneymongConstant.NOT_FOUND, "LEDGER-006", "장부 영수증 내역이 존재하지 않습니다."),
INVALID_LEDGER_AMOUNT(MoneymongConstant.BAD_REQUEST,"LEDGER-005", "0원 ~ 999,999,999원까지 기입 가능합니다."),
LEDGER_DOCUUMENT_NOT_FOUND(MoneymongConstant.NOT_FOUND, "LEDGER-007", "장부 증빙 자료 내역이 존재하지 않습니다."),
LEDGER_DOCUMENT_NOT_FOUND(MoneymongConstant.NOT_FOUND, "LEDGER-007", "장부 증빙 자료 내역이 존재하지 않습니다."),
LEDGER_AMOUNT_UNDERFLOW(MoneymongConstant.BAD_REQUEST,"LEDGER-008", "총 잔액은 최소 -999,999,999원까지 기록이 가능합니다."),

// ---- 이미지 ---- //
IMAGE_NOT_EXISTS(MoneymongConstant.NOT_FOUND, "IMAGE-001", "이미지를 찾을 수 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ public OAuthUserDataResponse getOAuthUserData(OAuthUserDataRequest request) {
String refreshToken = userData.getRefreshToken();
String idToken = userData.getIdToken();

log.info("[AppleService] refreshToken = {}", refreshToken);
return decodePayload(idToken, request.getName(), refreshToken);
} catch (RestClientException e) {
log.warn("[AppleService] failed to get OAuth User Data = {}", request.getAccessToken());
Expand Down Expand Up @@ -178,7 +177,7 @@ private OAuthUserDataResponse decodePayload(String idToken, String nickname, Str
Map<String, Claim> claims = decoded.getClaims();

String providerUid = decoded.getSubject();
String email = claims.get("sub").asString();
String email = claims.get("email").asString();

return OAuthUserDataResponse.builder()
.provider(getAuthProvider().toString())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.moneymong.global.security.oauth.handler;

import com.moneymong.domain.user.entity.User;
import com.moneymong.domain.user.repository.UserRepository;
import com.moneymong.global.exception.custom.NotFoundException;
import com.moneymong.global.exception.enums.ErrorCode;
import com.moneymong.global.security.oauth.dto.KakaoUserData;
import com.moneymong.global.security.oauth.dto.OAuthUserDataRequest;
Expand All @@ -11,6 +14,8 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;
Expand All @@ -21,10 +26,14 @@
public class KakaoService implements OAuthAuthenticationHandler {

private final RestTemplate restTemplate;
private final UserRepository userRepository;

@Value("${spring.security.oauth2.kakao.host}")
private String host;

@Value("${spring.security.oauth2.kakao.admin-key}")
private String adminKey;

@Override
public OAuthProvider getAuthProvider() {
return OAuthProvider.KAKAO;
Expand Down Expand Up @@ -70,6 +79,39 @@ public OAuthUserDataResponse getOAuthUserData(OAuthUserDataRequest request) {

@Override
public void unlink(Long userId) {
String oauthId = userRepository.findById(userId)
.orElseThrow(() -> new NotFoundException(ErrorCode.USER_NOT_FOUND))
.getOauthId();

String url = host + "/v1/user/unlink";

HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
httpHeaders.add("Authorization", "KakaoAK " + adminKey);

MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("target_id_type", "user_id");
body.add("target_id", oauthId);

HttpEntity<?> httpRequest = new HttpEntity<>(body, httpHeaders);

try {
ResponseEntity<KakaoUserData> response = restTemplate.exchange(
url,
HttpMethod.POST,
httpRequest,
KakaoUserData.class
);
assert response.getBody() != null;

} catch (RestClientException e) {
log.warn("[KakaoService] failed to unlink User = {}", oauthId);

if (e instanceof RestClientResponseException) {
throw new HttpClientException(ErrorCode.INVALID_OAUTH_TOKEN);
}

throw new HttpClientException(ErrorCode.HTTP_CLIENT_REQUEST_FAILED);
}
}
}
1 change: 1 addition & 0 deletions src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ spring:
oauth2:
kakao:
host: https://kapi.kakao.com
admin-key: ${KAKAO_ADMIN_KEY}
apple:
host: https://appleid.apple.com
grant-type: authorization_code
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ spring:
oauth2:
kakao:
host: https://kapi.kakao.com
admin-key: ${KAKAO_ADMIN_KEY}
apple:
host: https://appleid.apple.com
grant-type: authorization_code
Expand Down

0 comments on commit 413267e

Please sign in to comment.