From 6f371c52c2157ec2147c74373d21dca460014e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=ED=95=B4=EC=B0=AC=5BVertical=20Service=20Dev1=5D?= Date: Thu, 27 Jun 2024 18:59:25 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20QR=20Image=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 ++ .../controller/AdminAttendanceController.kt | 12 ++++++++ .../attendance/service/QrCodeService.kt | 20 +++++++++++++ .../webapp/src/apis/handlers/attendance.ts | 11 +++++-- .../types/attendance/AttendanceCodeDTO.ts | 12 ++++++++ .../src/apis/types/attendance/QRImageDTO.ts | 4 +++ .../src/pages/attendance/QRCodeList.tsx | 27 ++++++++++------- .../src/pages/attendance/QRImageComponent.tsx | 29 +++++++++++++++++++ 8 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 src/main/webapp/src/apis/types/attendance/QRImageDTO.ts create mode 100644 src/main/webapp/src/pages/attendance/QRImageComponent.tsx diff --git a/build.gradle.kts b/build.gradle.kts index cd862e0..90d6964 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,6 +38,8 @@ dependencies { implementation("mysql:mysql-connector-java:8.0.28") implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2") + implementation("com.google.zxing:javase:3.5.0") + implementation("com.google.zxing:core:3.5.0") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") diff --git a/src/main/kotlin/com/example/cmc_be/attendance/controller/AdminAttendanceController.kt b/src/main/kotlin/com/example/cmc_be/attendance/controller/AdminAttendanceController.kt index 59cfec2..01dfdea 100644 --- a/src/main/kotlin/com/example/cmc_be/attendance/controller/AdminAttendanceController.kt +++ b/src/main/kotlin/com/example/cmc_be/attendance/controller/AdminAttendanceController.kt @@ -16,6 +16,7 @@ import com.example.cmc_be.domain.user.entity.User import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.* @@ -55,6 +56,17 @@ class AdminAttendanceController( return CommonResponse.onSuccess(qrCodeService.getCodeInfo(code)) } + @GetMapping("/code/image") + @Operation(summary = "03-02-1 코드 QR이미지 조회") + fun getCodeImage( + @AuthenticationPrincipal user: User, + @Parameter(description = "코드", example = "8dFsb") + @RequestParam code: String, + @RequestParam type: String, + ): CommonResponse> { + return CommonResponse.onSuccess(qrCodeService.getCodeImage(code, type)) + } + @GetMapping("/code/all") @Operation(summary = "03-03 모든 코드 정보 조회") fun getCodeInfo( diff --git a/src/main/kotlin/com/example/cmc_be/attendance/service/QrCodeService.kt b/src/main/kotlin/com/example/cmc_be/attendance/service/QrCodeService.kt index 95d0277..7c6273a 100644 --- a/src/main/kotlin/com/example/cmc_be/attendance/service/QrCodeService.kt +++ b/src/main/kotlin/com/example/cmc_be/attendance/service/QrCodeService.kt @@ -14,10 +14,17 @@ import com.example.cmc_be.domain.attendance.repository.AttendanceRepository import com.example.cmc_be.domain.generation.entity.GenerationWeeksInfo import com.example.cmc_be.domain.user.entity.User import com.example.cmc_be.utils.RandomNumberUtil +import com.google.zxing.BarcodeFormat +import com.google.zxing.MultiFormatWriter +import com.google.zxing.client.j2se.MatrixToImageWriter import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Sort import org.springframework.data.repository.findByIdOrNull +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity import org.springframework.stereotype.Service +import java.io.ByteArrayOutputStream + @Service class QrCodeService( @@ -85,6 +92,19 @@ class QrCodeService( ) } + fun getCodeImage(code: String, type: String): ResponseEntity { + val url = if (type == "AOS") generateAndroidSchmea(code) else generateIOSSchmea(code) + val encode = MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, 300, 300) + val out = ByteArrayOutputStream() + MatrixToImageWriter.writeToStream(encode, "PNG", out) + return ResponseEntity.ok() + .contentType(MediaType.IMAGE_PNG) + .body(out.toByteArray()) + .also { + out.close() + } + } + fun validateCode(user: User, attendanceCode: AttendanceCode) { validateGeneration(user, attendanceCode.generation) validateAlreadyAttendance(user.id, attendanceCode) diff --git a/src/main/webapp/src/apis/handlers/attendance.ts b/src/main/webapp/src/apis/handlers/attendance.ts index 1151eb3..ed063a3 100644 --- a/src/main/webapp/src/apis/handlers/attendance.ts +++ b/src/main/webapp/src/apis/handlers/attendance.ts @@ -15,8 +15,13 @@ export const attendanceApi = { return await request.post(url, payload); }, - deleteAttendanceCode: async (id: any) => { - const url = setSearchParams(`/admin/attendances/code`, id); - return await request.delete(url, id); + deleteAttendanceCode: async (payload: any) => { + const url = setSearchParams(`/admin/attendances/code`, payload); + return await request.delete(url, payload); + }, + + getQRImageByCode: async (payload: { code: string, type: string }) => { + const url = setSearchParams(`/admin/attendances/code/image`, payload); + return await request.get(url); }, }; diff --git a/src/main/webapp/src/apis/types/attendance/AttendanceCodeDTO.ts b/src/main/webapp/src/apis/types/attendance/AttendanceCodeDTO.ts index a871884..497e621 100644 --- a/src/main/webapp/src/apis/types/attendance/AttendanceCodeDTO.ts +++ b/src/main/webapp/src/apis/types/attendance/AttendanceCodeDTO.ts @@ -9,3 +9,15 @@ export interface AttendanceCodeDTO { lateMinute: string; } + +export interface QRImage { + id: string; + generation: string; + week: string; + hour: number; + generationWeeksInfo: any; // TODO GenerationWeeksInfo 오브젝트인데 파싱이 안됨 + startTime: string; + endTime: string; + lateMinute: string; +} + diff --git a/src/main/webapp/src/apis/types/attendance/QRImageDTO.ts b/src/main/webapp/src/apis/types/attendance/QRImageDTO.ts new file mode 100644 index 0000000..c04a5a3 --- /dev/null +++ b/src/main/webapp/src/apis/types/attendance/QRImageDTO.ts @@ -0,0 +1,4 @@ +interface QRImageDTO { + body: string; +} + diff --git a/src/main/webapp/src/pages/attendance/QRCodeList.tsx b/src/main/webapp/src/pages/attendance/QRCodeList.tsx index 7108ff2..b6070b6 100644 --- a/src/main/webapp/src/pages/attendance/QRCodeList.tsx +++ b/src/main/webapp/src/pages/attendance/QRCodeList.tsx @@ -13,6 +13,7 @@ import {CButton, CFormInput} from "@coreui/react"; import useSelect, {Option} from "../../components/Select/useSelect"; import {FlexBox} from "../../components/FlexBox"; import ModalButton from "../../components/ModalButton"; +import {QRImageComponent} from "./QRImageComponent"; const ATTENDANCE_CODE_COLUMN: Column[] = [ {label: 'id', key: 'id'}, @@ -22,6 +23,8 @@ const ATTENDANCE_CODE_COLUMN: Column[] = [ {label: '출석 시작 시간', key: 'startTime'}, {label: '출석 종료 시간', key: 'endTime'}, {label: '지각 허용 시간(분)', key: 'lateMinute'}, + {label: 'AOS 코드', key: 'getAOSModal'}, + {label: 'IOS 코드', key: 'getIOSModal'}, {label: 'QR 삭제', key: 'modal'}, ]; @@ -214,18 +217,22 @@ const QRCodeList = () => { renderColumnData={{ modal: data => ( <> - <> - { - deleteAttendanceCode(data.id) - }}> - 삭제 - - + { + deleteAttendanceCode(data.id) + }}> + 삭제 + ), + getAOSModal: data => ( + + ), + getIOSModal: data => ( + + ), }} /> diff --git a/src/main/webapp/src/pages/attendance/QRImageComponent.tsx b/src/main/webapp/src/pages/attendance/QRImageComponent.tsx new file mode 100644 index 0000000..78f182b --- /dev/null +++ b/src/main/webapp/src/pages/attendance/QRImageComponent.tsx @@ -0,0 +1,29 @@ +import React, {useEffect, useState} from "react"; +import {attendanceApi} from "../../apis/handlers/attendance"; + +export function QRImageComponent({code, type}: { code: string, type: string }) { + const [imageSrc, setImageSrc] = useState(''); + + useEffect(() => { + console.log("-------------") + attendanceApi.getQRImageByCode({ + code: code, + type: type + }).then((r) => { + console.log(r) + setImageSrc(`data:image/jpeg;base64,${r.body}`); + }).catch((e) => { + console.log(e) + }) + }, []); + + return ( +
+ {imageSrc ? ( + Fetched from server + ) : ( +

Loading image...

+ )} +
+ ); +};