
오픈 소스인 zxing 을 사용하였다. 코드가 간결해서 사용하기 쉬웠다!
build.gradle 파일에 의존성을 추가해준다.
implementation group: 'com.google.zxing', name: 'javase', version: '3.5.0'
implementation group: 'com.google.zxing', name: 'core', version: '3.5.0'
public ResponseEntity<byte[]> createQr(User user, Long matchId) throws Exception {
Match match = matchRepository.findById(matchId).get();
int width = 200;
int height = 200;
long time = differenceTime(match);
if (match.getUser().getUserId() != user.getUserId()){
return ResponseEntity.badRequest()
.body("경기 리더가 아님".getBytes(StandardCharsets.UTF_8));
}
else if (time > 30 * 60000 || time < -10 * 60000) {
return ResponseEntity.badRequest().body("출석 체크 가능 시간이 아님".getBytes(StandardCharsets.UTF_8));
}
String url = "http://localhost:8080/api/attendance/"+matchId;
BitMatrix encode = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, width, height);
ByteArrayOutputStream out = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(encode, "PNG", out);
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
.body(out.toByteArray());
}
differenceTime 메서드의 결과를 time 변수에 담아두었다. 경기 시작 시간 30분 전에서 경기 시작 후 10분 사이가 아니라면 출석체크가 불가능하므로 badRequest로 처리한다.differenceTime() 은 다음과 같다. private long differenceTime(Match match) throws ParseException {
String now= LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).toString();
String matchTime= match.getStartTime().toString();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = format.parse(now);
Date d2 = format.parse(matchTime);
long sec = (d2.getTime() - d1.getTime()) / 1000;
long min = (d2.getTime() - d1.getTime()) / 60000;
long hour = (d2.getTime() - d1.getTime()) / 3600000;
long days = sec / (24*60*60);
return sec + min + hour + days;
}
url 에는 QR코드 스캔을 통해 이동할 url을 담아준다.BitMatrix 객체에서 구현할 수는 있는건지 감이 도통 안와서.. 일단 프론트쪽에서 다시 POST로 요청을 줄 url을 전달하는 방식으로 했고 프론트에선 response로 받은걸 다시 api 요청을 해주면 된다. 조금.. 번거로운 것 같다BitMatrix 객체는 width, height, 바코드 포맷 중 QR_CODE, 그리고 위에서 지정한 url 을 담아 생성한다. public ResponseEntity<String> qrToAttendance(Long matchId, String userEmail) throws Exception {
Match match = matchRepository.findById(matchId).get();
Optional<Participation> participation = participationService.findByMatchAndUser(matchId, userEmail);
long time = differenceTime(match);
if (time > 30 * 40 || time < -10 * 40) {
return ResponseEntity.badRequest().body("출석 체크 가능 시간이 아님");
}
else if (participation.get().isAttendance()) {
return ResponseEntity.badRequest().body("이미 출석 체크 완료");
} else {
return participationService.checkAttendance(matchId, userEmail);
}
}
checkAttendance 메서드를 통해 출석체크를 해준다. @Transactional
public ResponseEntity<String> checkAttendance(Long matchId, String userEmail) {
try {
Match match = matchRepository.findById(matchId).get();
User user = userRepository.findByEmail(userEmail).get();
Participation participation = participationRespository.findByMatchAndUser(match, user).get();
participation.setAttendance(true);
int attendanceCount = match.getAttendanceCnt();
match.setAttendanceCnt(attendanceCount + 1);
return ResponseEntity.ok().body("출석 체크 완료");
}catch (Exception e){
return ResponseEntity.badRequest().body("출석 체크 실패");
}
}
participation.setAttendance(true);
➡️ 유저의 participation (개인 경기 참가 정보)
의 출석 여부를 true 로 세팅해주고
int attendanceCount = match.getAttendanceCnt();
match.setAttendanceCnt(attendanceCount + 1);
➡️ match (경기 참가 공통 정보)
의 출석인원 수를 1 증가시켜준다.
걱정했던 기능이었는데 생각해보다 순탄하게 잘 끝냈다! 👍


