| Autor | SHA1 | Mensaje | Fecha |
|---|---|---|---|
|
|
0a992c381d | Merge remote-tracking branch 'origin/production' into production | hace 1 semana |
|
|
8002b6d621 | QR Code Printing Update | hace 1 semana |
| @@ -4,30 +4,52 @@ import com.ffii.core.utils.PdfUtils | |||
| import com.ffii.core.utils.QrCodeUtil | |||
| import com.ffii.fpsms.modules.master.entity.EquipmentDetailRepository | |||
| import com.ffii.fpsms.modules.master.web.ExportEquipmentQrCodeRequest | |||
| import com.ffii.fpsms.modules.master.web.PrintEquipmentQrCodeRequest | |||
| import com.ffii.fpsms.modules.master.print.A4PrintDriverRegistry | |||
| import net.sf.jasperreports.engine.JasperCompileManager | |||
| import net.sf.jasperreports.engine.JasperExportManager | |||
| import net.sf.jasperreports.engine.JasperReport | |||
| import net.sf.jasperreports.engine.JasperPrint | |||
| import org.springframework.core.io.ClassPathResource | |||
| import org.springframework.stereotype.Service | |||
| import java.io.FileNotFoundException | |||
| import java.io.File | |||
| import java.awt.GraphicsEnvironment | |||
| import kotlinx.serialization.json.Json | |||
| import kotlinx.serialization.encodeToString | |||
| @Service | |||
| class EquipmentQrCodeService( | |||
| private val equipmentDetailRepository: EquipmentDetailRepository | |||
| private val equipmentDetailRepository: EquipmentDetailRepository, | |||
| private val printerService: PrinterService, | |||
| ) { | |||
| private val qrCodeHandleJrxmlPath = "qrCodeHandle/equipment_QrHandle.jrxml" | |||
| fun exportEquipmentQrCode(request: ExportEquipmentQrCodeRequest): Map<String, Any> { | |||
| val QRCODE_HANDLE_PDF = "qrCodeHandle/equipment_QrHandle.jrxml" | |||
| val resource = ClassPathResource(QRCODE_HANDLE_PDF) | |||
| /** | |||
| * Compile the Jasper template once; compiling per request is expensive. | |||
| */ | |||
| private val qrCodeHandleReport: JasperReport by lazy { | |||
| val resource = ClassPathResource(qrCodeHandleJrxmlPath) | |||
| if (!resource.exists()) { | |||
| throw FileNotFoundException("Report file not found: $QRCODE_HANDLE_PDF") | |||
| throw FileNotFoundException("Report file not found: $qrCodeHandleJrxmlPath") | |||
| } | |||
| val inputStream = resource.inputStream | |||
| val qrCodeHandleReport = JasperCompileManager.compileReport(inputStream) | |||
| resource.inputStream.use { JasperCompileManager.compileReport(it) } | |||
| } | |||
| /** | |||
| * Cache the chosen Chinese font family name (font scanning is expensive). | |||
| */ | |||
| private val chineseFontFamily: String by lazy { | |||
| val availableFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().availableFontFamilyNames | |||
| availableFonts.find { family -> | |||
| family.contains("SimSun", ignoreCase = true) || | |||
| family.contains("Microsoft YaHei", ignoreCase = true) || | |||
| family.contains("STSong", ignoreCase = true) || | |||
| family.contains("SimHei", ignoreCase = true) | |||
| } ?: "Arial Unicode MS" | |||
| } | |||
| fun exportEquipmentQrCode(request: ExportEquipmentQrCodeRequest): Map<String, Any> { | |||
| val equipmentDetails = equipmentDetailRepository.findAllById(request.equipmentDetailIds) | |||
| if (equipmentDetails.isEmpty()) { | |||
| throw IllegalArgumentException("No equipment details found for the provided equipment detail IDs: ${request.equipmentDetailIds}") | |||
| @@ -63,18 +85,10 @@ class EquipmentQrCodeService( | |||
| } | |||
| val params: MutableMap<String, Any> = 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 | |||
| params["net.sf.jasperreports.default.pdf.font.name"] = chineseFontFamily | |||
| val firstEquipmentDetail = equipmentDetails.firstOrNull() | |||
| @@ -83,4 +97,23 @@ class EquipmentQrCodeService( | |||
| "fileName" to (firstEquipmentDetail?.code ?: "equipment_qrcode") | |||
| ) | |||
| } | |||
| fun printEquipmentQrCode(request: PrintEquipmentQrCodeRequest) { | |||
| val printer = printerService.findById(request.printerId) ?: throw NoSuchElementException("No such printer") | |||
| val pdf = exportEquipmentQrCode(ExportEquipmentQrCodeRequest(request.equipmentDetailIds)) | |||
| val jasperPrint = pdf["report"] as JasperPrint | |||
| val tempPdfFile = File.createTempFile("print_job_", ".pdf") | |||
| try { | |||
| JasperExportManager.exportReportToPdfFile(jasperPrint, tempPdfFile.absolutePath) | |||
| val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty | |||
| printer.ip?.let { ip -> | |||
| val port = printer.port ?: 9100 | |||
| val driver = A4PrintDriverRegistry.getDriver(printer.brand) | |||
| driver.print(tempPdfFile, ip, port, printQty) | |||
| } | |||
| } finally { | |||
| tempPdfFile.delete() | |||
| } | |||
| } | |||
| } | |||
| @@ -2,12 +2,17 @@ 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.print.A4PrintDriverRegistry | |||
| import com.ffii.fpsms.modules.master.entity.WarehouseRepository | |||
| import com.ffii.fpsms.modules.master.web.ExportWarehouseQrCodeRequest | |||
| import com.ffii.fpsms.modules.master.web.PrintWarehouseQrCodeRequest | |||
| import net.sf.jasperreports.engine.JasperCompileManager | |||
| import net.sf.jasperreports.engine.JasperExportManager | |||
| import net.sf.jasperreports.engine.JasperReport | |||
| import net.sf.jasperreports.engine.JasperPrint | |||
| import org.springframework.core.io.ClassPathResource | |||
| import org.springframework.stereotype.Service | |||
| import java.io.File | |||
| import java.io.FileNotFoundException | |||
| import java.awt.GraphicsEnvironment | |||
| import kotlinx.serialization.json.Json | |||
| @@ -15,19 +20,32 @@ import kotlinx.serialization.encodeToString | |||
| @Service | |||
| class WarehouseQrCodeService( | |||
| private val warehouseRepository: WarehouseRepository | |||
| private val warehouseRepository: WarehouseRepository, | |||
| private val printerService: PrinterService, | |||
| ) { | |||
| private val qrCodeHandleJrxmlPath = "qrCodeHandle/warehouse_QrHandle.jrxml" | |||
| fun exportWarehouseQrCode(request: ExportWarehouseQrCodeRequest): Map<String, Any> { | |||
| val QRCODE_HANDLE_PDF = "qrCodeHandle/warehouse_QrHandle.jrxml" | |||
| val resource = ClassPathResource(QRCODE_HANDLE_PDF) | |||
| /** Compile the Jasper template once; compiling per request is expensive. */ | |||
| private val qrCodeHandleReport: JasperReport by lazy { | |||
| val resource = ClassPathResource(qrCodeHandleJrxmlPath) | |||
| if (!resource.exists()) { | |||
| throw FileNotFoundException("Report file not found: $QRCODE_HANDLE_PDF") | |||
| throw FileNotFoundException("Report file not found: $qrCodeHandleJrxmlPath") | |||
| } | |||
| val inputStream = resource.inputStream | |||
| val qrCodeHandleReport = JasperCompileManager.compileReport(inputStream) | |||
| resource.inputStream.use { JasperCompileManager.compileReport(it) } | |||
| } | |||
| /** Cache the chosen Chinese font family name (font scanning is expensive). */ | |||
| private val chineseFontFamily: String by lazy { | |||
| val availableFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().availableFontFamilyNames | |||
| availableFonts.find { family -> | |||
| family.contains("SimSun", ignoreCase = true) || | |||
| family.contains("Microsoft YaHei", ignoreCase = true) || | |||
| family.contains("STSong", ignoreCase = true) || | |||
| family.contains("SimHei", ignoreCase = true) | |||
| } ?: "Arial Unicode MS" | |||
| } | |||
| fun exportWarehouseQrCode(request: ExportWarehouseQrCodeRequest): Map<String, Any> { | |||
| val warehouses = warehouseRepository.findAllById(request.warehouseIds) | |||
| if (warehouses.isEmpty()) { | |||
| throw IllegalArgumentException("No warehouses found for the provided warehouse IDs: ${request.warehouseIds}") | |||
| @@ -68,18 +86,10 @@ class WarehouseQrCodeService( | |||
| } | |||
| val params: MutableMap<String, Any> = 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 | |||
| params["net.sf.jasperreports.default.pdf.font.name"] = chineseFontFamily | |||
| val firstWarehouse = warehouses.firstOrNull() | |||
| @@ -88,4 +98,23 @@ class WarehouseQrCodeService( | |||
| "fileName" to (firstWarehouse?.code ?: "warehouse_qrcode") | |||
| ) | |||
| } | |||
| fun printWarehouseQrCode(request: PrintWarehouseQrCodeRequest) { | |||
| val printer = printerService.findById(request.printerId) ?: throw NoSuchElementException("No such printer") | |||
| val pdf = exportWarehouseQrCode(ExportWarehouseQrCodeRequest(request.warehouseIds)) | |||
| val jasperPrint = pdf["report"] as JasperPrint | |||
| val tempPdfFile = File.createTempFile("print_job_", ".pdf") | |||
| try { | |||
| JasperExportManager.exportReportToPdfFile(jasperPrint, tempPdfFile.absolutePath) | |||
| val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty | |||
| printer.ip?.let { ip -> | |||
| val port = printer.port ?: 9100 | |||
| val driver = A4PrintDriverRegistry.getDriver(printer.brand) | |||
| driver.print(tempPdfFile, ip, port, printQty) | |||
| } | |||
| } finally { | |||
| tempPdfFile.delete() | |||
| } | |||
| } | |||
| } | |||
| @@ -93,4 +93,9 @@ fun getAllEquipmentByPage( | |||
| out.flush() | |||
| } | |||
| @PostMapping("/print-qrcode") | |||
| fun printQrCode(@Valid @RequestBody request: PrintEquipmentQrCodeRequest) { | |||
| equipmentQrCodeService.printEquipmentQrCode(request) | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| package com.ffii.fpsms.modules.master.web | |||
| data class PrintEquipmentQrCodeRequest( | |||
| val equipmentDetailIds: List<Long>, | |||
| val printerId: Long, | |||
| val printQty: Int? = 1, | |||
| ) | |||
| @@ -0,0 +1,7 @@ | |||
| package com.ffii.fpsms.modules.master.web | |||
| data class PrintWarehouseQrCodeRequest( | |||
| val warehouseIds: List<Long>, | |||
| val printerId: Long, | |||
| val printQty: Int? = 1, | |||
| ) | |||
| @@ -98,6 +98,10 @@ class WarehouseController( | |||
| out.write(JasperExportManager.exportReportToPdf(jasperPrint)) | |||
| out.flush() | |||
| } | |||
| @PostMapping("/print-qrcode") | |||
| fun printQrCode(@Valid @RequestBody request: PrintWarehouseQrCodeRequest) { | |||
| warehouseQrCodeService.printWarehouseQrCode(request) | |||
| } | |||
| @GetMapping("/stockTakeSections") | |||
| fun getStockTakeSections(): List<StockTakeSectionInfo> { | |||
| return warehouseService.getStockTakeSections() | |||
| @@ -2,15 +2,18 @@ package com.ffii.fpsms.modules.user.service | |||
| import com.ffii.core.utils.PdfUtils | |||
| import com.ffii.core.utils.QrCodeUtil | |||
| import com.ffii.fpsms.modules.master.print.A4PrintDriverRegistry | |||
| import com.ffii.fpsms.modules.master.service.PrinterService | |||
| import com.ffii.fpsms.modules.user.entity.UserRepository | |||
| import com.ffii.fpsms.modules.user.web.ExportUserQrCodeRequest | |||
| import com.ffii.fpsms.modules.user.web.PrintUserQrCodeRequest | |||
| import net.sf.jasperreports.engine.JasperCompileManager | |||
| import net.sf.jasperreports.engine.JasperExportManager | |||
| import net.sf.jasperreports.engine.JasperReport | |||
| import net.sf.jasperreports.engine.JasperPrint | |||
| import net.sf.jasperreports.engine.export.JRPdfExporter | |||
| import net.sf.jasperreports.export.SimpleExporterInput | |||
| import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput | |||
| import org.springframework.core.io.ClassPathResource | |||
| import org.springframework.stereotype.Service | |||
| import java.io.File | |||
| import java.io.FileNotFoundException | |||
| import java.awt.GraphicsEnvironment | |||
| import kotlinx.serialization.json.Json | |||
| @@ -18,19 +21,34 @@ import kotlinx.serialization.encodeToString | |||
| @Service | |||
| class UserQrCodeService( | |||
| private val userRepository: UserRepository | |||
| private val userRepository: UserRepository, | |||
| private val printerService: PrinterService, | |||
| ) { | |||
| private val qrCodeHandleJrxmlPath = "qrCodeHandle/qrCodeHandle.jrxml" | |||
| fun exportUserQrCode(request: ExportUserQrCodeRequest): Map<String, Any> { | |||
| val QRCODE_HANDLE_PDF = "qrCodeHandle/qrCodeHandle.jrxml" | |||
| val resource = ClassPathResource(QRCODE_HANDLE_PDF) | |||
| /** | |||
| * Compile the Jasper template once; compiling per request is expensive. | |||
| */ | |||
| private val qrCodeHandleReport: JasperReport by lazy { | |||
| val resource = ClassPathResource(qrCodeHandleJrxmlPath) | |||
| if (!resource.exists()) { | |||
| throw FileNotFoundException("Report file not found: $QRCODE_HANDLE_PDF") | |||
| throw FileNotFoundException("Report file not found: $qrCodeHandleJrxmlPath") | |||
| } | |||
| val inputStream = resource.inputStream | |||
| val qrCodeHandleReport = JasperCompileManager.compileReport(inputStream) | |||
| resource.inputStream.use { JasperCompileManager.compileReport(it) } | |||
| } | |||
| /** Cache the chosen Chinese font family name (font scanning is expensive). */ | |||
| private val chineseFontFamily: String by lazy { | |||
| val availableFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().availableFontFamilyNames | |||
| availableFonts.find { family -> | |||
| family.contains("SimSun", ignoreCase = true) || | |||
| family.contains("Microsoft YaHei", ignoreCase = true) || | |||
| family.contains("STSong", ignoreCase = true) || | |||
| family.contains("SimHei", ignoreCase = true) | |||
| } ?: "Arial Unicode MS" | |||
| } | |||
| fun exportUserQrCode(request: ExportUserQrCodeRequest): Map<String, Any> { | |||
| val users = userRepository.findAllById(request.userIds) | |||
| val fields = mutableListOf<MutableMap<String, Any>>() | |||
| @@ -53,24 +71,33 @@ class UserQrCodeService( | |||
| } | |||
| val params: MutableMap<String, Any> = mutableMapOf() | |||
| // Configure for Chinese character support | |||
| // Try to find a Chinese-supporting font | |||
| 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" // Fallback | |||
| 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 | |||
| params["net.sf.jasperreports.default.pdf.font.name"] = chineseFontFamily | |||
| return mapOf( | |||
| "report" to PdfUtils.fillReport(qrCodeHandleReport, fields, params), | |||
| "fileName" to (users.firstOrNull()?.username ?: "user_qrcode") | |||
| ) | |||
| } | |||
| fun printUserQrCode(request: PrintUserQrCodeRequest) { | |||
| val printer = printerService.findById(request.printerId) ?: throw NoSuchElementException("No such printer") | |||
| val pdf = exportUserQrCode(ExportUserQrCodeRequest(request.userIds)) | |||
| val jasperPrint = pdf["report"] as JasperPrint | |||
| val tempPdfFile = File.createTempFile("print_job_", ".pdf") | |||
| try { | |||
| JasperExportManager.exportReportToPdfFile(jasperPrint, tempPdfFile.absolutePath) | |||
| val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty | |||
| printer.ip?.let { ip -> | |||
| val port = printer.port ?: 9100 | |||
| val driver = A4PrintDriverRegistry.getDriver(printer.brand) | |||
| driver.print(tempPdfFile, ip, port, printQty) | |||
| } | |||
| } finally { | |||
| tempPdfFile.delete() | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| package com.ffii.fpsms.modules.user.web | |||
| data class PrintUserQrCodeRequest( | |||
| val userIds: List<Long>, | |||
| val printerId: Long, | |||
| val printQty: Int? = 1, | |||
| ) | |||
| @@ -42,13 +42,11 @@ import com.ffii.fpsms.modules.user.req.UpdateUserReq; | |||
| import com.ffii.fpsms.modules.user.service.UserService; | |||
| import com.ffii.fpsms.modules.user.service.res.LoadUserRes; | |||
| import com.ffii.fpsms.modules.user.web.ExportUserQrCodeRequest; | |||
| import com.ffii.fpsms.modules.user.service.UserQrCodeService; | |||
| import jakarta.servlet.http.HttpServletResponse; | |||
| import net.sf.jasperreports.engine.JasperExportManager; | |||
| import net.sf.jasperreports.engine.JasperPrint; | |||
| import java.io.OutputStream; | |||
| import java.io.UnsupportedEncodingException; | |||
| import jakarta.validation.Valid; | |||
| import jakarta.validation.constraints.NotBlank; | |||
| @@ -255,6 +253,11 @@ public class UserController{ | |||
| out.flush(); | |||
| } | |||
| @PostMapping("/print-qrcode") | |||
| public void printQrCode(@Valid @RequestBody PrintUserQrCodeRequest request) { | |||
| userQrCodeService.printUserQrCode(request); | |||
| } | |||
| public static class AdminChangePwdReq { | |||
| private Long id; | |||
| @NotBlank | |||