Skip to content
This repository has been archived by the owner on Dec 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #292 from GSM-MSG/287-feat/attendance-status-excel…
Browse files Browse the repository at this point in the history
…-print

🔀 :: 287 출석부 엑셀 출력 API
  • Loading branch information
KimTaeO authored Mar 22, 2024
2 parents 7d49b39 + fd61907 commit 13d1b9c
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import com.msg.gcms.domain.attendance.presentation.data.dto.UserAttendanceStatus
import com.msg.gcms.domain.attendance.presentation.data.request.CreateScheduleRequestDto
import com.msg.gcms.domain.attendance.presentation.data.request.UpdateAttendanceStatusBatchRequestDto
import com.msg.gcms.domain.attendance.presentation.data.request.UpdateAttendanceStatusRequestDto
import com.msg.gcms.domain.attendance.service.CreateScheduleService
import com.msg.gcms.domain.attendance.service.QueryCurrentAttendConditionService
import com.msg.gcms.domain.attendance.service.UpdateAttendanceStatusBatchService
import com.msg.gcms.domain.attendance.service.UpdateAttendanceStatusService
import com.msg.gcms.domain.attendance.service.*
import com.msg.gcms.domain.attendance.util.AttendanceConverter
import com.msg.gcms.domain.attendance.util.ScheduleConverter
import com.msg.gcms.global.annotation.RequestController
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import java.net.URL
import java.net.URLEncoder
import java.time.LocalDate
import java.util.*
import javax.servlet.http.HttpServletResponse
import javax.validation.Valid

@RequestController("/attend")
Expand All @@ -26,6 +26,7 @@ class AttendanceController(
private val queryCurrentAttendConditionService: QueryCurrentAttendConditionService,
private val updateAttendanceStatusService: UpdateAttendanceStatusService,
private val updateAttendanceStatusBatchService: UpdateAttendanceStatusBatchService,
private val clubAttendanceStatusExcelService: ClubAttendanceStatusExcelService,
private val scheduleConverter: ScheduleConverter,
private val attendanceConverter: AttendanceConverter
) {
Expand Down Expand Up @@ -64,4 +65,10 @@ class AttendanceController(
attendanceConverter.toDto(updateAttendanceStatusBatchRequestDto)
.let { updateAttendanceStatusBatchService.execute(it) }
.let { ResponseEntity.noContent().build() }

@GetMapping("/excel")
fun clubAttendanceStatusExcel(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) currentDate: LocalDate, response: HttpServletResponse): ByteArray {
response.setHeader("Content-Disposition", "attachment; filename=${URLEncoder.encode("$currentDate 출석부", "UTF-8").replace("+", "%20")}.xlsx")
return clubAttendanceStatusExcelService.execute(currentDate)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ interface AttendanceRepository : CrudRepository<Attendance, Long>, CustomAttenda
@EntityGraph(attributePaths = ["user"])
fun findAllByScheduleAndPeriod(schedule: Schedule, period: Period): List<Attendance>
fun findAllByIdIn(ids: List<Long>): List<Attendance>
@EntityGraph(attributePaths = ["user"])
fun findAllBySchedule(schedule: Schedule): List<Attendance>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ package com.msg.gcms.domain.attendance.repository

import com.msg.gcms.domain.attendance.domain.entity.Schedule
import org.springframework.data.repository.CrudRepository
import java.time.LocalDate

interface ScheduleRepository : CrudRepository<Schedule, Long>, CustomScheduleRepository
interface ScheduleRepository : CrudRepository<Schedule, Long>, CustomScheduleRepository {
fun findAllByDate(localDate: LocalDate): List<Schedule>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.msg.gcms.domain.attendance.service

import java.time.LocalDate

interface ClubAttendanceStatusExcelService {
fun execute(currentDate: LocalDate): ByteArray
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.msg.gcms.domain.attendance.service.impl

import com.msg.gcms.domain.attendance.domain.entity.Schedule
import com.msg.gcms.domain.attendance.repository.AttendanceRepository
import com.msg.gcms.domain.attendance.repository.ScheduleRepository
import com.msg.gcms.domain.attendance.service.ClubAttendanceStatusExcelService
import com.msg.gcms.domain.club.domain.entity.Club
import com.msg.gcms.global.annotation.ServiceWithReadOnlyTransaction
import com.msg.gcms.global.util.ExcelUtil
import org.apache.poi.ss.usermodel.*
import org.apache.poi.xssf.usermodel.XSSFCellStyle
import org.apache.poi.xssf.usermodel.XSSFSheet
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import java.io.ByteArrayOutputStream
import java.time.LocalDate

@ServiceWithReadOnlyTransaction
class ClubAttendanceStatusExcelServiceImpl(
private val scheduleRepository: ScheduleRepository,
private val attendanceRepository: AttendanceRepository
): ClubAttendanceStatusExcelService {
override fun execute(currentDate: LocalDate): ByteArray {
val schedule = scheduleRepository.findAllByDate(currentDate)

val workBook = XSSFWorkbook()

val font = workBook.createFont()
font.fontName = "Arial"
font.fontHeightInPoints = 11

schedule.forEach {
var rowNum = 1

val sheet = workBook.createHeaderRow(font, it.club.name)

val attendances = attendanceRepository.findAllBySchedule(it)

attendances.groupBy { attendance -> attendance.user }.forEach { (user, attendances) ->
val row = sheet.createRow(rowNum)

val sortedAttendances = attendances.sortedBy { it.period }
listOf(
"${user.grade}${user.classNum}${user.number.toString().padStart(2, '0')}",
user.nickname,
*sortedAttendances.map { sortedAttendance -> sortedAttendance.attendanceStatus.sign }.toTypedArray()
).forEachIndexed { idx, data ->
val cell = row.createCell(idx)
cell.setCellValue(data)
cell.cellStyle = workBook.createCellStyle()
.apply {
alignment = HorizontalAlignment.CENTER
verticalAlignment = VerticalAlignment.CENTER
setFont(font)
}
}
}
rowNum++
}

ByteArrayOutputStream().use { stream ->
workBook.write(stream)
return stream.toByteArray()
}
}

// 출석부의 헤더 열을 만들고 엑셀 시트를 만들어 반환하는 함수
private fun XSSFWorkbook.createHeaderRow(font: Font, name: String): XSSFSheet {
val sheet = createSheet(name)
val headerRow = sheet.createRow(0)

listOf(
"학번",
"이름",
"8교시",
"9교시",
"10교시",
"11교시"
).forEachIndexed { idx, column ->
val cell = headerRow.createCell(idx)
cell.setCellValue(column)
cell.cellStyle = createCellStyle()
.apply {
fillForegroundColor = IndexedColors.GREY_25_PERCENT.index
fillPattern = FillPatternType.SOLID_FOREGROUND
alignment = HorizontalAlignment.CENTER
verticalAlignment = VerticalAlignment.CENTER
setFont(font)
}
}

return sheet
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class SecurityConfig(
.antMatchers(HttpMethod.GET, "/attend/{club_id}").authenticated()
.antMatchers(HttpMethod.PATCH, "/attend/batch").authenticated()
.antMatchers(HttpMethod.PATCH, "/attend/{user_id}").authenticated()
.antMatchers(HttpMethod.GET, "/attend/excel").hasRole("ADMIN")

.antMatchers(HttpMethod.GET, "/club-member/{club_id}").authenticated()
.antMatchers(HttpMethod.POST, "/club-member/{club_id}").authenticated()
Expand Down
30 changes: 30 additions & 0 deletions src/main/resources/logs/access-2024-03-22.0.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
2024-03-22 13:55:02.243 INFO 9820 --- [http-nio-8080-exec-1] RequestLogFilter : client ip = 0:0:0:0:0:0:0:1
2024-03-22 13:55:02.243 INFO 9820 --- [http-nio-8080-exec-1] RequestLogFilter : request method = GET
2024-03-22 13:55:02.244 INFO 9820 --- [http-nio-8080-exec-1] RequestLogFilter : request url = /attend/excel
2024-03-22 13:55:02.244 INFO 9820 --- [http-nio-8080-exec-1] RequestLogFilter : client info = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
2024-03-22 13:55:02.698 INFO 9820 --- [http-nio-8080-exec-1] RequestLogFilter : response status = 200
2024-03-22 13:55:42.052 INFO 9895 --- [http-nio-8080-exec-2] RequestLogFilter : client ip = 0:0:0:0:0:0:0:1
2024-03-22 13:55:42.053 INFO 9895 --- [http-nio-8080-exec-2] RequestLogFilter : request method = GET
2024-03-22 13:55:42.053 INFO 9895 --- [http-nio-8080-exec-2] RequestLogFilter : request url = /attend/excel
2024-03-22 13:55:42.053 INFO 9895 --- [http-nio-8080-exec-2] RequestLogFilter : client info = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
2024-03-22 13:55:42.442 INFO 9895 --- [http-nio-8080-exec-2] RequestLogFilter : response status = 200
2024-03-22 14:03:50.076 INFO 9895 --- [http-nio-8080-exec-4] RequestLogFilter : client ip = 0:0:0:0:0:0:0:1
2024-03-22 14:03:50.078 INFO 9895 --- [http-nio-8080-exec-4] RequestLogFilter : request method = GET
2024-03-22 14:03:50.078 INFO 9895 --- [http-nio-8080-exec-4] RequestLogFilter : request url = /attend/excel
2024-03-22 14:03:50.078 INFO 9895 --- [http-nio-8080-exec-4] RequestLogFilter : client info = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
2024-03-22 14:03:50.211 INFO 9895 --- [http-nio-8080-exec-4] RequestLogFilter : response status = 200
2024-03-22 14:06:23.777 INFO 9970 --- [http-nio-8080-exec-1] RequestLogFilter : client ip = 0:0:0:0:0:0:0:1
2024-03-22 14:06:23.777 INFO 9970 --- [http-nio-8080-exec-1] RequestLogFilter : request method = GET
2024-03-22 14:06:23.777 INFO 9970 --- [http-nio-8080-exec-1] RequestLogFilter : request url = /attend/excel
2024-03-22 14:06:23.778 INFO 9970 --- [http-nio-8080-exec-1] RequestLogFilter : client info = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
2024-03-22 14:06:24.164 INFO 9970 --- [http-nio-8080-exec-1] RequestLogFilter : response status = 200
2024-03-22 14:17:19.875 INFO 10189 --- [http-nio-8080-exec-1] RequestLogFilter : client ip = 0:0:0:0:0:0:0:1
2024-03-22 14:17:19.875 INFO 10189 --- [http-nio-8080-exec-1] RequestLogFilter : request method = GET
2024-03-22 14:17:19.875 INFO 10189 --- [http-nio-8080-exec-1] RequestLogFilter : request url = /attend/excel
2024-03-22 14:17:19.876 INFO 10189 --- [http-nio-8080-exec-1] RequestLogFilter : client info = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
2024-03-22 14:17:20.264 INFO 10189 --- [http-nio-8080-exec-1] RequestLogFilter : response status = 200
2024-03-22 14:17:25.296 INFO 10189 --- [http-nio-8080-exec-2] RequestLogFilter : client ip = 0:0:0:0:0:0:0:1
2024-03-22 14:17:25.298 INFO 10189 --- [http-nio-8080-exec-2] RequestLogFilter : request method = GET
2024-03-22 14:17:25.298 INFO 10189 --- [http-nio-8080-exec-2] RequestLogFilter : request url = /attend/excel
2024-03-22 14:17:25.299 INFO 10189 --- [http-nio-8080-exec-2] RequestLogFilter : client info = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
2024-03-22 14:17:25.326 INFO 10189 --- [http-nio-8080-exec-2] RequestLogFilter : response status = 200
Empty file.

0 comments on commit 13d1b9c

Please sign in to comment.