
@Service
@RequiredArgsConstructor
public class MemberManagementService {
private final MemberRepository memberRepository;
// 회원 전체 조회
public List<Member> findAllMembers() {
return memberRepository.findAll();
}
// 회원 단일 조회 (id)
public Member findMember(String mId) {
Member member = memberRepository.findBymId(mId);
if (member != null) {
return member;
} else {
throw new IllegalArgumentException("존재하지 않는 회원입니다.");
}
}
// 회원 권한 변경
@Transactional
public void changeMemberRole(String mId, Role newRole) {
Member member = memberRepository.findBymId(mId);
if (member != null) { // ADMIN 계정 추가 이후에 수정 예정
member.setRole(newRole); //
memberRepository.save(member); // 변경된 정보 저장
} else {
throw new IllegalArgumentException("존재하지 않는 회원입니다.");
}
}
// 회원 삭제
@Transactional
public void deleteMember(String mId) {
Member member = memberRepository.findBymId(mId);
if (member != null) {
memberRepository.delete(member);
} else {
throw new IllegalArgumentException("존재하지 않는 회원입니다.");
}
}
// 회원 전체 조회(페이징 처리)
public Page<Member> getsAdminMemberWithPages(AdminSearchInfo adminSearchInfo) {
int page = adminSearchInfo.getPage();
int pageSize = adminSearchInfo.getPageSize();
page = Math.max(page, 1);
pageSize = pageSize < 1 ? 15 : pageSize;
// 권한 오름차순, 생성일자 내림차순으로 정렬
Pageable pageable = PageRequest.of(page - 1, pageSize,
Sort.by(Sort.Order.asc("role"), Sort.Order.desc("createdAt")));
Page<Member> adminMemberList = memberRepository.findAll(pageable);
return adminMemberList;
}
// 검색(필터링) 페이징 처리
public Page<Member> searchMembers(AdminSearchInfo adminSearchInfo) {
int page = adminSearchInfo.getPage();
int pageSize = adminSearchInfo.getPageSize();
page = Math.max(page, 1);
pageSize = pageSize < 1 ? 15 : pageSize;
String sOpt = adminSearchInfo.getSOpt();
String sKey = adminSearchInfo.getSKey();
// 권한(Admin / Member) 오름차순, 생성일 내림차순
Pageable pageable = PageRequest.of(page - 1, pageSize,
Sort.by(Sort.Order.asc("role"), Sort.Order.desc("createdAt")));
if (sOpt != null && sKey != null) {
switch (sOpt) {
case "mNo":
Long mNo = Long.parseLong(sKey);
return memberRepository.findBymNo(mNo,pageable);
case "mId":
return memberRepository.findBymId(sKey, pageable);
case "mName":
return memberRepository.findBymName(sKey, pageable);
case "mNickName":
return memberRepository.findBymNickName(sKey, pageable);
case "grade":
Grade grade = Grade.valueOf(sKey);
return memberRepository.findByGrade(grade, pageable);
case "mLevel":
Integer mLevel = Integer.parseInt(sKey);
return memberRepository.findBymLevel(mLevel, pageable);
case "role":
Role role = Role.valueOf(sKey);
return memberRepository.findByRole(role, pageable);
}
}
return memberRepository.findAll(pageable);
}
}
MemberManagementService에서는 회원에 관한 정보를 변경, 삭제 , 조회 등과 같은 기능들을 구현해 놓은 클래스이다. 회원 권한 변경하는 코드에서는 Role이라는 타입으로 매개변수를 받는데
public enum Role {
MEMBER,
ADMIN
}
위와 같이 enum타입으로 MEMBER와 ADMIN을 만들어 놓았다.
<!-- 권한 변경 -->
<form th:action="@{'/admin/member/' + ${member.mId} + '/role'}" method="post">
<input type="hidden" name="_method" value="put" />
<!-- ADMIN / MEMBER 둘 중 하나만 선택 하도록 구현 -->
<select name="newRole">
<option value="MEMBER">MEMBER</option>
<option value="ADMIN">ADMIN</option>
</select>
<button type="submit">Change Role</button>
</form>
</td>
</tr>
memberManagement.html에 thymeleaf로 권한 변경하는 코드를 작성해놓았다. 위 폼은 사용자가 버튼을 클릭하면 newRole에 정보를 담아서
@PutMapping("/{mId}/role")
public RedirectView changeMemberRole(@PathVariable String mId, @RequestParam("newRole") Role newRole) {
try {
memberManagementService.changeMemberRole(mId, newRole);
return new RedirectView("/admin/member/memberList");
} catch (IllegalArgumentException e) {
// 예외가 발생시 해당 예외 메시지와 함께 HTTP 상태 코드 400(Bad Request)를 반환
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e);
}
}
위 컨트롤러에서 받아서 서비스에서 처리한 내용을 바탕으로 다시 /admin/member/memberList로 넘겨준다.
회원 전체 조회나 검색은 페이징 처리를 해서 코드를 작성했다.
@Data
public class AdminSearchInfo {
private int page = 1;
private int pageSize = 10;
private String sOpt; // 검색 조건
private String sKey; // 검색 키워드(유저 입력)
// private String category; // 검색 조건
// private String query; // 검색 키워드 (유저 입력)
}
AdminSearchInfo 클래스에 페이징 처리에 관련된 정보를 넣어놓고 Spring Data JPA에서 제공하는 페이지네이션 기능을 활용하였다.
package com.springboot.shootformoney.bet.service;
import com.springboot.shootformoney.bet.entity.Bet;
import com.springboot.shootformoney.bet.entity.EuroPool;
import com.springboot.shootformoney.bet.repository.BetRepository;
import com.springboot.shootformoney.bet.repository.EuroPoolRepository;
import com.springboot.shootformoney.game.dto.data.Result;
import com.springboot.shootformoney.game.entity.Game;
import com.springboot.shootformoney.game.repository.GameRepository;
import com.springboot.shootformoney.member.entity.Euro;
import com.springboot.shootformoney.member.entity.Member;
import com.springboot.shootformoney.member.repository.EuroRepository;
import com.springboot.shootformoney.member.utils.MemberUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
/*
* 배팅 관련 서비스(배팅 실행, 배팅 취소, 정산 후 지급금 지급) 구현 클래스
* Author: Hyedokal(https://www.github.com/Hyedokal)
*/
@Service
public class BetService {
@Autowired
private BetRepository betRepository;
@Autowired
private EuroPoolRepository euroPoolRepository;
@Autowired
private GameRepository gameRepository;
@Autowired
private MemberUtil memberUtil;
@Autowired
private EuroRepository euroRepository;
//고객이 배팅하면, Bet 테이블에 기록을 저장하는 메서드
@Transactional
public Bet bet(Long gNo, String expect, Integer betAmount) {
Game game = gameRepository.findById(gNo)
.orElseThrow(() -> new RuntimeException("해당 경기 정보가 없습니다.")); //gNo로 경기 데이터를 찾아 온다.
Bet bet = new Bet();
bet.setGame(game);
bet.setExpect(Result.valueOf(expect));
bet.setBtMoney(betAmount);
bet.setBtTime(LocalDateTime.now());
bet.setMember(memberUtil.getEntity());
//배팅 정보 저장 후 가져옴.
return betRepository.save(bet);
}
//취소할 배팅을 불러오는 메서드
@Transactional
public Bet findToCancel(Long btNo){
Optional<Bet> bet = betRepository.findByBtNo(btNo);
if(bet.isPresent()){
return bet.get();
}
return null;
}
//배팅 취소 메서드
@Transactional
public Bet betCancel(Long btNo){
Bet bet = betRepository.findByBtNo(btNo) //btId로 배팅 데이터를 찾아 온다.
.orElseThrow(() -> new RuntimeException("배팅 정보가 잘못되었습니다."));
betRepository.delete(bet); // 찾아온 배팅 데이터 삭제
return bet;
}
//경기결과가 나오면 배당률을 계산해서 모든 배팅률이 업데이트되지 않은 배팅내역에 배당률을 집어넣는다.
@Transactional
public void calcBtRatio(){
try{
List<Game> finishedMatches = gameRepository.findAllFinishedMatches();
for(Game game : finishedMatches) {
EuroPool targetEuroPool = euroPoolRepository.findByGame_gNo(game.getGNo());
//걸린 유로의 총합.
double euroSum = (double) targetEuroPool.getWinEuro() + targetEuroPool.getDrawEuro()
+ targetEuroPool.getLoseEuro();
//각 결과별 배당률.
double winBtRatio = Math.round((euroSum / targetEuroPool.getWinEuro()) * 100.0) / 100.0;
double drawBtRatio = Math.round((euroSum / targetEuroPool.getDrawEuro()) * 100.0) / 100.0;
double loseBtRatio = Math.round((euroSum / targetEuroPool.getLoseEuro()) * 100.0) / 100.0;
List<Bet> bets = betRepository.findAllByGame_gNo(game.getGNo());
for (Bet bet : bets) {
if (bet.getExpect() == Result.WIN) {
bet.setBtRatio(winBtRatio);
} else if (bet.getExpect() == Result.DRAW) {
bet.setBtRatio(drawBtRatio);
} else if(bet.getExpect() == Result.LOSE) {
bet.setBtRatio(loseBtRatio);
}
}
}
} catch (RuntimeException e){
System.err.println("calcBtRatio()에서 오류 발생");
}
}
//경기결과가 나오면 지급금을 지급하는 메서드.
@Transactional
public void dividend(){
try {
//끝난 경기들 가져오기.
List<Game> finishedGames = gameRepository.findAllFinishedMatches();
for (Game game : finishedGames) {
List<Bet> bets = betRepository.findByGameAndEndPaid(game);
for (Bet bet : bets) {
//배팅 적중 시 지급금을 지금하는 if문.
if(bet.getExpect().equals(game.getResult())){
Member member = bet.getMember();
double fee = member.getGrade().getFee();
//배당금 = 배팅금 * (배당률 - 1) (만 단위, 배팅금 보전)
double prizeValue = bet.getBtMoney() * (bet.getBtRatio() - 1);
//지급금 = 배당금 * (1 - 수수료) + 배팅금
Integer addValue = (int) ((prizeValue * (1 - fee) + bet.getBtMoney()) * 10000);
//현재 사용자의 유로 보유량 조회.
Integer originalValue = euroRepository.findBymNo(member.getMNo()).getValue();
//Euro 보유량 업데이트.
Euro euroAccount = euroRepository.findBymNo(member.getMNo());
euroAccount.setValue(originalValue + addValue);
bet.setCorrect((byte) 1);
}
//Bet엔터티의 중복검사부분 업데이트
bet.setEndPaid((byte) 1);
}
}
} catch (Exception e){
System.out.println("오류가 발생했습니다.");
}
}
}
위 코드에서 expect는 회원의 예상 결과이고 result형으로 형변환 해준 것을 볼 수 있다.
package com.springboot.shootformoney.game.dto.data;
public enum Result {
WIN,
DRAW,
LOSE,
//경기 종료 전 디폴트값.
NN;
}
result는 위와 같이 승, 무, 패로 이루어져 있다.