From 91684e420252843ea6b9c7daed47bb48151ef879 Mon Sep 17 00:00:00 2001 From: "B.E.N.S.O.N" Date: Tue, 13 Jan 2026 15:00:52 +0800 Subject: [PATCH] Supporting Function: Warehouse QR Code Printing --- .../java/com/ffii/fpsms/config/WebConfig.java | 4 +- .../master/service/WarehouseQrCodeService.kt | 86 +++++++++++++++++++ .../web/ExportWarehouseQrCodeRequest.kt | 5 ++ .../modules/master/web/WarehouseController.kt | 27 +++++- .../qrCodeHandle/warehouse_QrHandle.jrxml | 43 ++++++++++ 5 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/ffii/fpsms/modules/master/service/WarehouseQrCodeService.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/master/web/ExportWarehouseQrCodeRequest.kt create mode 100644 src/main/resources/qrCodeHandle/warehouse_QrHandle.jrxml diff --git a/src/main/java/com/ffii/fpsms/config/WebConfig.java b/src/main/java/com/ffii/fpsms/config/WebConfig.java index 1b0ec0e..dcdf839 100644 --- a/src/main/java/com/ffii/fpsms/config/WebConfig.java +++ b/src/main/java/com/ffii/fpsms/config/WebConfig.java @@ -16,8 +16,8 @@ public class WebConfig implements WebMvcConfigurer { registry.addMapping("/**") .allowedHeaders("*") .allowedOrigins("*") - .exposedHeaders("filename") - .allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD"); + .exposedHeaders("filename", "Content-Disposition") + .allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"); } diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/WarehouseQrCodeService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/WarehouseQrCodeService.kt new file mode 100644 index 0000000..f804496 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/service/WarehouseQrCodeService.kt @@ -0,0 +1,86 @@ +package com.ffii.fpsms.modules.master.service + +import com.ffii.core.utils.PdfUtils +import com.ffii.core.utils.QrCodeUtil +import com.ffii.fpsms.modules.master.entity.WarehouseRepository +import com.ffii.fpsms.modules.master.web.ExportWarehouseQrCodeRequest +import net.sf.jasperreports.engine.JasperCompileManager +import net.sf.jasperreports.engine.JasperPrint +import org.springframework.core.io.ClassPathResource +import org.springframework.stereotype.Service +import java.io.FileNotFoundException +import java.awt.GraphicsEnvironment +import kotlinx.serialization.json.Json +import kotlinx.serialization.encodeToString + +@Service +class WarehouseQrCodeService( + private val warehouseRepository: WarehouseRepository +) { + + fun exportWarehouseQrCode(request: ExportWarehouseQrCodeRequest): Map { + val QRCODE_HANDLE_PDF = "qrCodeHandle/warehouse_QrHandle.jrxml" + val resource = ClassPathResource(QRCODE_HANDLE_PDF) + if (!resource.exists()) { + throw FileNotFoundException("Report file not found: $QRCODE_HANDLE_PDF") + } + + val inputStream = resource.inputStream + val qrCodeHandleReport = JasperCompileManager.compileReport(inputStream) + + val warehouses = warehouseRepository.findAllById(request.warehouseIds) + if (warehouses.isEmpty()) { + throw IllegalArgumentException("No warehouses found for the provided warehouse IDs: ${request.warehouseIds}") + } + + val fields = mutableListOf>() + + for (warehouse in warehouses) { + val field = mutableMapOf() + + val code = warehouse.code ?: "" + + if (code.isBlank()) { + continue + } + + val qrContentMap = mapOf("warehouseCode" to code) + val qrCodeContent = Json.encodeToString(qrContentMap) + + val qrCodeImage = QrCodeUtil.generateQRCodeImage(qrCodeContent) + + field["username"] = "" + field["staffNo"] = "" + field["code"] = code + field["equipmentCode"] = "" + field["qrCode"] = qrCodeImage + + fields.add(field) + } + + if (fields.isEmpty()) { + throw IllegalArgumentException("No valid warehouses found for the provided warehouse IDs: ${request.warehouseIds}") + } + + val params: MutableMap = mutableMapOf() + + val availableFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().availableFontFamilyNames + val chineseFont = availableFonts.find { + it.contains("SimSun", ignoreCase = true) || + it.contains("Microsoft YaHei", ignoreCase = true) || + it.contains("STSong", ignoreCase = true) || + it.contains("SimHei", ignoreCase = true) + } ?: "Arial Unicode MS" + + params["net.sf.jasperreports.default.pdf.encoding"] = "Identity-H" + params["net.sf.jasperreports.default.pdf.embedded"] = true + params["net.sf.jasperreports.default.pdf.font.name"] = chineseFont + + val firstWarehouse = warehouses.firstOrNull() + + return mapOf( + "report" to PdfUtils.fillReport(qrCodeHandleReport, fields, params), + "fileName" to (firstWarehouse?.code ?: "warehouse_qrcode") + ) + } +} diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/ExportWarehouseQrCodeRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/ExportWarehouseQrCodeRequest.kt new file mode 100644 index 0000000..3bf56f0 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/web/ExportWarehouseQrCodeRequest.kt @@ -0,0 +1,5 @@ +package com.ffii.fpsms.modules.master.web + +data class ExportWarehouseQrCodeRequest( + val warehouseIds: List +) diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/WarehouseController.kt b/src/main/java/com/ffii/fpsms/modules/master/web/WarehouseController.kt index e01edad..190a387 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/WarehouseController.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/WarehouseController.kt @@ -3,7 +3,9 @@ package com.ffii.fpsms.modules.master.web import com.ffii.fpsms.modules.master.entity.Warehouse import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo import com.ffii.fpsms.modules.master.service.WarehouseService +import com.ffii.fpsms.modules.master.service.WarehouseQrCodeService import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.apache.poi.ss.usermodel.Workbook import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.springframework.http.ResponseEntity @@ -17,12 +19,18 @@ import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.RequestMethod +import org.springframework.web.bind.annotation.CrossOrigin import org.springframework.web.multipart.MultipartHttpServletRequest +import net.sf.jasperreports.engine.JasperExportManager +import net.sf.jasperreports.engine.JasperPrint +import java.io.OutputStream @RestController @RequestMapping("/warehouse") class WarehouseController( - private val warehouseService: WarehouseService + private val warehouseService: WarehouseService, + private val warehouseQrCodeService: WarehouseQrCodeService ) { @GetMapping fun getWarehouses(): List { @@ -72,4 +80,21 @@ class WarehouseController( return ResponseEntity.ok(warehouseService.importNewExcel(workbook)) } + + @PostMapping("/export-qrcode") + @CrossOrigin(origins = ["*"], allowedHeaders = ["*"]) + fun exportQrCode(@Valid @RequestBody request: ExportWarehouseQrCodeRequest, response: HttpServletResponse) { + response.setHeader("Access-Control-Allow-Origin", "*") + response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + response.setHeader("Access-Control-Allow-Headers", "*") + response.characterEncoding = "utf-8" + response.contentType = "application/pdf" + val out: OutputStream = response.outputStream + val pdf = warehouseQrCodeService.exportWarehouseQrCode(request) + val jasperPrint = pdf["report"] as JasperPrint + val fileName = pdf["fileName"] as String + response.addHeader("Content-Disposition", "attachment; filename=\"${fileName}_qrcode.pdf\"") + out.write(JasperExportManager.exportReportToPdf(jasperPrint)) + out.flush() + } } \ No newline at end of file diff --git a/src/main/resources/qrCodeHandle/warehouse_QrHandle.jrxml b/src/main/resources/qrCodeHandle/warehouse_QrHandle.jrxml new file mode 100644 index 0000000..f89f0e2 --- /dev/null +++ b/src/main/resources/qrCodeHandle/warehouse_QrHandle.jrxml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +