Просмотр исходного кода

QR Code Printing Update

production
B.E.N.S.O.N 1 неделю назад
Родитель
Сommit
8002b6d621
9 измененных файлов: 186 добавлений и 64 удалений
  1. +52
    -19
      src/main/java/com/ffii/fpsms/modules/master/service/EquipmentQrCodeService.kt
  2. +48
    -19
      src/main/java/com/ffii/fpsms/modules/master/service/WarehouseQrCodeService.kt
  3. +5
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/EquipmentController.kt
  4. +7
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/PrintEquipmentQrCodeRequest.kt
  5. +7
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/PrintWarehouseQrCodeRequest.kt
  6. +4
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/WarehouseController.kt
  7. +51
    -24
      src/main/java/com/ffii/fpsms/modules/user/service/UserQrCodeService.kt
  8. +7
    -0
      src/main/java/com/ffii/fpsms/modules/user/web/PrintUserQrCodeRequest.kt
  9. +5
    -2
      src/main/java/com/ffii/fpsms/modules/user/web/UserController.java

+ 52
- 19
src/main/java/com/ffii/fpsms/modules/master/service/EquipmentQrCodeService.kt Просмотреть файл

@@ -4,30 +4,52 @@ import com.ffii.core.utils.PdfUtils
import com.ffii.core.utils.QrCodeUtil import com.ffii.core.utils.QrCodeUtil
import com.ffii.fpsms.modules.master.entity.EquipmentDetailRepository import com.ffii.fpsms.modules.master.entity.EquipmentDetailRepository
import com.ffii.fpsms.modules.master.web.ExportEquipmentQrCodeRequest 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.JasperCompileManager
import net.sf.jasperreports.engine.JasperExportManager
import net.sf.jasperreports.engine.JasperReport
import net.sf.jasperreports.engine.JasperPrint import net.sf.jasperreports.engine.JasperPrint
import org.springframework.core.io.ClassPathResource import org.springframework.core.io.ClassPathResource
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.File
import java.awt.GraphicsEnvironment import java.awt.GraphicsEnvironment
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString


@Service @Service
class EquipmentQrCodeService( 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()) { 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) val equipmentDetails = equipmentDetailRepository.findAllById(request.equipmentDetailIds)
if (equipmentDetails.isEmpty()) { if (equipmentDetails.isEmpty()) {
throw IllegalArgumentException("No equipment details found for the provided equipment detail IDs: ${request.equipmentDetailIds}") 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 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.encoding"] = "Identity-H"
params["net.sf.jasperreports.default.pdf.embedded"] = true 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() val firstEquipmentDetail = equipmentDetails.firstOrNull()
@@ -83,4 +97,23 @@ class EquipmentQrCodeService(
"fileName" to (firstEquipmentDetail?.code ?: "equipment_qrcode") "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()
}
}
} }

+ 48
- 19
src/main/java/com/ffii/fpsms/modules/master/service/WarehouseQrCodeService.kt Просмотреть файл

@@ -2,12 +2,17 @@ package com.ffii.fpsms.modules.master.service


import com.ffii.core.utils.PdfUtils import com.ffii.core.utils.PdfUtils
import com.ffii.core.utils.QrCodeUtil 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.entity.WarehouseRepository
import com.ffii.fpsms.modules.master.web.ExportWarehouseQrCodeRequest 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.JasperCompileManager
import net.sf.jasperreports.engine.JasperExportManager
import net.sf.jasperreports.engine.JasperReport
import net.sf.jasperreports.engine.JasperPrint import net.sf.jasperreports.engine.JasperPrint
import org.springframework.core.io.ClassPathResource import org.springframework.core.io.ClassPathResource
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.awt.GraphicsEnvironment import java.awt.GraphicsEnvironment
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@@ -15,19 +20,32 @@ import kotlinx.serialization.encodeToString


@Service @Service
class WarehouseQrCodeService( 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()) { 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) val warehouses = warehouseRepository.findAllById(request.warehouseIds)
if (warehouses.isEmpty()) { if (warehouses.isEmpty()) {
throw IllegalArgumentException("No warehouses found for the provided warehouse IDs: ${request.warehouseIds}") 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 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.encoding"] = "Identity-H"
params["net.sf.jasperreports.default.pdf.embedded"] = true 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() val firstWarehouse = warehouses.firstOrNull()
@@ -88,4 +98,23 @@ class WarehouseQrCodeService(
"fileName" to (firstWarehouse?.code ?: "warehouse_qrcode") "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()
}
}
} }

+ 5
- 0
src/main/java/com/ffii/fpsms/modules/master/web/EquipmentController.kt Просмотреть файл

@@ -93,4 +93,9 @@ fun getAllEquipmentByPage(
out.flush() out.flush()
} }


@PostMapping("/print-qrcode")
fun printQrCode(@Valid @RequestBody request: PrintEquipmentQrCodeRequest) {
equipmentQrCodeService.printEquipmentQrCode(request)
}

} }

+ 7
- 0
src/main/java/com/ffii/fpsms/modules/master/web/PrintEquipmentQrCodeRequest.kt Просмотреть файл

@@ -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,
)

+ 7
- 0
src/main/java/com/ffii/fpsms/modules/master/web/PrintWarehouseQrCodeRequest.kt Просмотреть файл

@@ -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,
)

+ 4
- 0
src/main/java/com/ffii/fpsms/modules/master/web/WarehouseController.kt Просмотреть файл

@@ -98,6 +98,10 @@ class WarehouseController(
out.write(JasperExportManager.exportReportToPdf(jasperPrint)) out.write(JasperExportManager.exportReportToPdf(jasperPrint))
out.flush() out.flush()
} }
@PostMapping("/print-qrcode")
fun printQrCode(@Valid @RequestBody request: PrintWarehouseQrCodeRequest) {
warehouseQrCodeService.printWarehouseQrCode(request)
}
@GetMapping("/stockTakeSections") @GetMapping("/stockTakeSections")
fun getStockTakeSections(): List<StockTakeSectionInfo> { fun getStockTakeSections(): List<StockTakeSectionInfo> {
return warehouseService.getStockTakeSections() return warehouseService.getStockTakeSections()


+ 51
- 24
src/main/java/com/ffii/fpsms/modules/user/service/UserQrCodeService.kt Просмотреть файл

@@ -2,15 +2,18 @@ package com.ffii.fpsms.modules.user.service


import com.ffii.core.utils.PdfUtils import com.ffii.core.utils.PdfUtils
import com.ffii.core.utils.QrCodeUtil 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.entity.UserRepository
import com.ffii.fpsms.modules.user.web.ExportUserQrCodeRequest 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.JasperCompileManager
import net.sf.jasperreports.engine.JasperExportManager
import net.sf.jasperreports.engine.JasperReport
import net.sf.jasperreports.engine.JasperPrint 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.core.io.ClassPathResource
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.awt.GraphicsEnvironment import java.awt.GraphicsEnvironment
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@@ -18,19 +21,34 @@ import kotlinx.serialization.encodeToString


@Service @Service
class UserQrCodeService( 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()) { 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 users = userRepository.findAllById(request.userIds)
val fields = mutableListOf<MutableMap<String, Any>>() val fields = mutableListOf<MutableMap<String, Any>>()
@@ -53,24 +71,33 @@ class UserQrCodeService(
} }
val params: MutableMap<String, Any> = mutableMapOf() 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.encoding"] = "Identity-H"
params["net.sf.jasperreports.default.pdf.embedded"] = true 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( return mapOf(
"report" to PdfUtils.fillReport(qrCodeHandleReport, fields, params), "report" to PdfUtils.fillReport(qrCodeHandleReport, fields, params),
"fileName" to (users.firstOrNull()?.username ?: "user_qrcode") "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()
}
}
} }

+ 7
- 0
src/main/java/com/ffii/fpsms/modules/user/web/PrintUserQrCodeRequest.kt Просмотреть файл

@@ -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,
)

+ 5
- 2
src/main/java/com/ffii/fpsms/modules/user/web/UserController.java Просмотреть файл

@@ -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.UserService;
import com.ffii.fpsms.modules.user.service.res.LoadUserRes; 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 com.ffii.fpsms.modules.user.service.UserQrCodeService;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JasperExportManager; import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JasperPrint;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException;


import jakarta.validation.Valid; import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
@@ -255,6 +253,11 @@ public class UserController{
out.flush(); out.flush();
} }


@PostMapping("/print-qrcode")
public void printQrCode(@Valid @RequestBody PrintUserQrCodeRequest request) {
userQrCodeService.printUserQrCode(request);
}

public static class AdminChangePwdReq { public static class AdminChangePwdReq {
private Long id; private Long id;
@NotBlank @NotBlank


Загрузка…
Отмена
Сохранить