Skip to content

Commit d064985

Browse files
committed
Merge branch 'feat/#85' into develop
2 parents b835e46 + 42cbcc3 commit d064985

File tree

6 files changed

+131
-3
lines changed

6 files changed

+131
-3
lines changed

src/main/kotlin/site/billilge/api/backend/domain/payer/controller/AdminPayerController.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package site.billilge.api.backend.domain.payer.controller
22

3+
import org.springframework.core.io.InputStreamResource
4+
import org.springframework.http.ContentDisposition
5+
import org.springframework.http.HttpHeaders
36
import org.springframework.http.ResponseEntity
47
import org.springframework.web.bind.annotation.*
58
import site.billilge.api.backend.domain.payer.dto.request.PayerDeleteRequest
@@ -9,6 +12,8 @@ import site.billilge.api.backend.domain.payer.service.PayerService
912
import site.billilge.api.backend.global.annotation.OnlyAdmin
1013
import site.billilge.api.backend.global.dto.PageableCondition
1114
import site.billilge.api.backend.global.dto.SearchCondition
15+
import java.time.LocalDate
16+
import java.time.format.DateTimeFormatter
1217

1318
@RestController
1419
@RequestMapping("/admin/members/payers")
@@ -35,4 +40,20 @@ class AdminPayerController(
3540
payerService.deletePayers(request)
3641
return ResponseEntity.noContent().build()
3742
}
43+
44+
@GetMapping("/excel")
45+
fun createPayerExcel(): ResponseEntity<InputStreamResource> {
46+
val excel = payerService.createPayerExcel()
47+
val currentDate = LocalDate.now()
48+
val dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd")
49+
val headers = HttpHeaders().apply {
50+
contentDisposition = ContentDisposition.builder("attachment")
51+
.filename("kmusw_payers_${dateFormatter.format(currentDate)}.xlsx")
52+
.build()
53+
}
54+
55+
return ResponseEntity.ok()
56+
.headers(headers)
57+
.body(InputStreamResource((excel)))
58+
}
3859
}

src/main/kotlin/site/billilge/api/backend/domain/payer/repository/PayerRepository.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ interface PayerRepository : JpaRepository<Payer, Long?> {
1515

1616
@Query("SELECT p FROM Payer p WHERE p.name LIKE CONCAT('%', :name, '%')")
1717
fun findAllByNameContaining(@Param("name") name: String, pageable: Pageable): Page<Payer>
18+
19+
fun findAllByEnrollmentYear(enrollmentYear: String): List<Payer>
1820
}

src/main/kotlin/site/billilge/api/backend/domain/payer/service/PayerService.kt

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,19 @@ import site.billilge.api.backend.domain.payer.entity.Payer
1414
import site.billilge.api.backend.domain.payer.repository.PayerRepository
1515
import site.billilge.api.backend.global.dto.PageableCondition
1616
import site.billilge.api.backend.global.dto.SearchCondition
17+
import site.billilge.api.backend.global.utils.ExcelGenerator
18+
import site.billilge.api.backend.global.utils.ExcelRow
19+
import java.io.ByteArrayInputStream
20+
import java.time.Year
1721

1822
@Service
1923
@Transactional(readOnly = true)
2024
class PayerService(
2125
private val payerRepository: PayerRepository,
22-
private val memberRepository: MemberRepository
26+
27+
private val memberRepository: MemberRepository,
28+
29+
private val excelGenerator: ExcelGenerator
2330
) {
2431
fun isPayer(name: String, studentId: String): Boolean {
2532
val enrollmentYear = studentId.substring(0, 4)
@@ -71,6 +78,7 @@ class PayerService(
7178

7279
@Transactional
7380
fun addPayers(request: PayerRequest) {
81+
val newPayers = mutableListOf<Payer>()
7482
request.payers.forEach { payerItem ->
7583
val name = payerItem.name
7684
val studentId = payerItem.studentId
@@ -87,18 +95,19 @@ class PayerService(
8795
this.registered = registered
8896
}
8997

90-
payerRepository.save(payer)
98+
newPayers.add(payer)
9199
}
92100

93101
registeredMember?.isFeePaid = true
94102
}
103+
104+
payerRepository.saveAll(newPayers)
95105
}
96106

97107
@Transactional
98108
fun deletePayers(request: PayerDeleteRequest) {
99109
val payerStudentIds = payerRepository.findAllByIds(request.payerIds)
100110
.mapNotNull { it.studentId }
101-
.toList()
102111

103112
memberRepository.findAllByStudentIds(payerStudentIds)
104113
.forEach { member ->
@@ -107,4 +116,21 @@ class PayerService(
107116

108117
payerRepository.deleteAllById(request.payerIds)
109118
}
119+
120+
fun createPayerExcel(): ByteArrayInputStream {
121+
val startYear = 2015
122+
val currentYear = Year.now().value
123+
val headerTitles = arrayOf("이름", "학번")
124+
val sheetData = mutableMapOf<String, Pair<Array<String>, List<ExcelRow>>>()
125+
126+
for (year in startYear..currentYear) {
127+
val yearText = "$year"
128+
val payersByYearExcelRow = payerRepository.findAllByEnrollmentYear(yearText)
129+
.map { payer -> ExcelRow(payer.name, payer.studentId ?: "${yearText}XXXX") }
130+
131+
sheetData.put(yearText, headerTitles to payersByYearExcelRow)
132+
}
133+
134+
return excelGenerator.generateByMultipleSheets(sheetData)
135+
}
110136
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package site.billilge.api.backend.global.config
2+
3+
import org.springframework.context.annotation.Configuration
4+
import org.springframework.scheduling.annotation.EnableAsync
5+
6+
@EnableAsync
7+
@Configuration
8+
class AsyncConfig {
9+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package site.billilge.api.backend.global.utils
2+
3+
import org.apache.poi.ss.usermodel.IndexedColors
4+
import org.apache.poi.xssf.streaming.SXSSFSheet
5+
import org.apache.poi.xssf.streaming.SXSSFWorkbook
6+
import org.springframework.stereotype.Component
7+
import java.io.ByteArrayInputStream
8+
import java.io.ByteArrayOutputStream
9+
10+
private const val HEADER_ROW = 0
11+
12+
@Component
13+
class ExcelGenerator {
14+
15+
fun generateByMultipleSheets(
16+
sheetData: Map<String, Pair<Array<String>, List<ExcelRow>>>
17+
): ByteArrayInputStream {
18+
val workbook = SXSSFWorkbook()
19+
20+
sheetData.forEach { (sheetName, sheetContent) ->
21+
val (headerTitles, rows) = sheetContent
22+
val sheet = workbook.createSheet(sheetName)
23+
styleHeaders(workbook, sheet, headerTitles)
24+
fillData(sheet, rows, headerTitles.size)
25+
}
26+
27+
val out = ByteArrayOutputStream()
28+
workbook.write(out)
29+
workbook.close()
30+
31+
return ByteArrayInputStream(out.toByteArray())
32+
}
33+
34+
private fun styleHeaders(workbook: SXSSFWorkbook, sheet: SXSSFSheet, headerTitles: Array<String>) {
35+
val headerFont = workbook.createFont()
36+
headerFont.bold = true
37+
38+
val headerCellStyle = workbook.createCellStyle()
39+
headerCellStyle.setFont(headerFont)
40+
headerCellStyle.fillForegroundColor = IndexedColors.GREY_25_PERCENT.index
41+
headerCellStyle.fillPattern = org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND
42+
43+
val headerRow = sheet.createRow(HEADER_ROW)
44+
headerTitles.forEachIndexed { col, title ->
45+
val cell = headerRow.createCell(col)
46+
cell.setCellValue(title)
47+
cell.cellStyle = headerCellStyle
48+
}
49+
}
50+
51+
private fun fillData(sheet: SXSSFSheet, rows: List<ExcelRow>, columnSize: Int) {
52+
sheet.trackAllColumnsForAutoSizing()
53+
54+
rows.forEachIndexed { index, excelRow ->
55+
val row = sheet.createRow(index + 1)
56+
excelRow.data.forEachIndexed { propertyIndex, property ->
57+
row.createCell(propertyIndex).setCellValue(property)
58+
}
59+
}
60+
61+
repeat(columnSize) { col -> sheet.autoSizeColumn(col) }
62+
}
63+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package site.billilge.api.backend.global.utils
2+
3+
data class ExcelRow(
4+
val data: List<String>
5+
) {
6+
constructor(vararg data: String) : this(data.toList())
7+
}

0 commit comments

Comments
 (0)