Compare commits

...

33 Commits

Author SHA1 Message Date
f36e6703a3 删除用户身份证实体,调整与台式机一致 2025-02-17 17:06:20 +08:00
27d8b818c0 Merge remote-tracking branch 'origin/1.0' into 1.0
# Conflicts:
#	src/main/java/com/dpkj/modules/cardReader/service/impl/CardReaderServiceImpl.java
#	src/main/java/com/dpkj/modules/cardReader/utils/CardReaderSdk.java
2025-02-17 17:03:44 +08:00
c0b1e80525 身份证读取(完成)、社保卡读取(待调试) 2025-02-17 17:00:27 +08:00
47c9d8dc5a 身份证读取(完成)、社保卡读取(待调试) 2025-02-17 16:58:54 +08:00
62c9c7fa7d fix:修复result显示错误 2025-02-08 15:53:34 +08:00
3b0051cb37 feat:增加身份证信息获取 2025-01-23 10:06:36 +08:00
6ca1cdce4b 待64位动态库及开发包支撑测试 2025-01-21 17:40:05 +08:00
12ae2323f7 fix:修改串口服务bean名称 2025-01-20 14:59:57 +08:00
d9da4ea307 feat:增加com、TCP打印服务 2025-01-20 14:58:42 +08:00
2986fa0c14 feat:generateReceiptImage接口增加图片自定义保存路径 2025-01-20 14:31:42 +08:00
a8f13d72b9 fix:优化模板获取以及html模板 2025-01-20 14:16:50 +08:00
44a175aa66 fix:去除static以及js文件 2025-01-19 16:09:25 +08:00
9073fd9180 feat:增加模板相关接口 2025-01-19 16:00:40 +08:00
1474746864 feat:增加图片打印接口 2025-01-17 15:15:23 +08:00
eecc053eae feat:打包隔离,jar包和lib包进行分割 2025-01-16 11:32:19 +08:00
07d160a48d feat:增加AutoReplyPrint接口内置属性注释 2025-01-16 10:29:35 +08:00
aa46f4c66f feat:增加测试打印接口 2025-01-15 17:35:27 +08:00
8fb13e026b fix:修改异常处理 2025-01-15 17:34:43 +08:00
7b0a2a98cd feat:增加controller层的异常处理 2025-01-15 17:17:27 +08:00
b0bd972e66 feat:增加win64的autoreplyprint.dll文件,兼容64位 2025-01-15 17:16:32 +08:00
5e66fb8c3f fix:完善AutoReplyPrint本地接口注释说明 2025-01-15 16:26:33 +08:00
02129e4db6 fix:统一采用AutoReplyPrint接口进行调用本地接口 2025-01-15 16:05:06 +08:00
4291244a1d fix:去除autoreplypring.jar的依赖 2025-01-15 15:13:39 +08:00
26c2a7ba40 fix:统一加载win32的autoreplyprint.dll文件 2025-01-15 15:08:33 +08:00
e2111717bc fix:增加autoreplyprint.dll的win32文件 2025-01-15 15:06:46 +08:00
d4b66aa77a fix:去除receiptsPrint模块,统一采用autoReplyPrint模块 2025-01-15 15:04:54 +08:00
6754910edd fix:去除无用文件 2025-01-15 15:04:20 +08:00
68e2fbfc8c 壁挂打印机(缺文档接口说明)、身份证读取(缺64位dll动态库) 2025-01-14 17:46:26 +08:00
7d971f4d13 feat:增加小票打印模块 2025-01-14 14:48:38 +08:00
25af267b39 feat:修复去除Mspringsdk返回nullBUG 2025-01-14 11:36:47 +08:00
96d447fc0d fix:修改函数签名,将UnPack.dll换为SSSE32.dll 2025-01-14 11:36:47 +08:00
1fa6933c18 fix:取消Msprintsdk.x64中64位dll的加载 2025-01-14 11:36:47 +08:00
cfb6690df5 多合一读卡器(待对接、待测试) 2025-01-07 16:15:03 +08:00
52 changed files with 14570 additions and 117 deletions

95
pom.xml
View File

@@ -21,6 +21,8 @@
<hutool.version>5.8.25</hutool.version> <hutool.version>5.8.25</hutool.version>
<jna.version>5.14.0</jna.version> <jna.version>5.14.0</jna.version>
<pdfbox.version>3.0.2</pdfbox.version> <pdfbox.version>3.0.2</pdfbox.version>
<fastjson.version>1.2.83</fastjson.version>
</properties> </properties>
<dependencies> <dependencies>
@@ -68,15 +70,106 @@
<artifactId>pdfbox</artifactId> <artifactId>pdfbox</artifactId>
<version>${pdfbox.version}</version> <version>${pdfbox.version}</version>
</dependency> </dependency>
<!-- json处理-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<!-- Flying Saucer for HTML to Image 把html转换为图片 -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-core</artifactId>
<version>9.1.22</version>
</dependency>
<!-- ZXing for QR Code 二维码生成-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<!-- JSoup HTML Parser -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.4</version>
</dependency>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.1.28</version> <!-- 可以根据实际情况调整版本 -->
</dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>ems-express-call-dll</finalName> <finalName>${project.artifactId}</finalName>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> </plugin>
<!-- 跳过单元测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!-- 拷贝项目依赖包到lib/目录下 -->
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>ZIP</layout>
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins> </plugins>
<!--打包时将resource下的文件一起打包--> <!--打包时将resource下的文件一起打包-->
<resources> <resources>

View File

@@ -0,0 +1,37 @@
package com.dpkj.common.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
* mvcConfig配置类
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @since 2023-05-23 15:24:43
*/
@RequiredArgsConstructor
@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
/**
* 添加资源过滤处理器
* @param registry 资源处理器
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/");
registry.addResourceHandler("/swagger-ui.html/**")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.resourceChain(false);
super.addResourceHandlers(registry);
}
}

View File

@@ -0,0 +1,139 @@
package com.dpkj.common.exception;
import com.dpkj.common.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* controller控制器异常处理接口实现类
* <p>其它module可以集成此类进行controller层的异常处理</p>
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
*/
@Slf4j
@Component
@RestControllerAdvice(annotations = {RestController.class, Controller.class})
public class ControllerAdvice {
/**
* controller方法中的参数校验失败但是前提是要使用
* #@Validated注解开启参数校验
*
* @param e 异常
* @return Result
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Result<String> bindingException(MethodArgumentNotValidException e) {
// 获得所有校验出错的返回集
BindingResult bindingResult = e.getBindingResult();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
// 循环获得所有校验异常的字段
Map<String, String> fieldMap = new HashMap<>();
for (FieldError fieldError : fieldErrors) {
fieldMap.put(fieldError.getField(), fieldError.getDefaultMessage());
}
String errMsg = fieldMap.values().toString().replaceAll("]", "").replaceAll("\\[", "");
log.warn(errMsg);
// 返回给前端
return Result.error(errMsg);
}
/**
* 处理空指针异常
* @param nullPointerException 空指针异常
* @return Result
*/
@ExceptionHandler(value = java.lang.NullPointerException.class)
public Result<String> nullPointException(java.lang.NullPointerException nullPointerException) {
log.error("空指针异常类型: {},信息: {}", nullPointerException.getClass(),nullPointerException.getMessage());
return Result.error(ErrorEnum.NULL_POINTER_EXCEPTION);
}
/**
* 所有的运行时异常,抛出异常
* @param throwable 异常
* @return Result
*/
@ExceptionHandler(value = Throwable.class)
public Result<String> handleException(Throwable throwable) {
log.error("异常类型: {}, {}, 信息为: {}", throwable.getCause(), throwable.getClass(), throwable.getMessage());
if (throwable instanceof RRException){
RRException rrException = (RRException) throwable;
return Result.error(rrException.getCode(), rrException.getMsg());
}
return Result.error(ErrorEnum.RUNTIME_EXCEPTION);
}
/**
* http信息无可读
* @param e 异常
* @return Result
*/
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public Result<String> httpMessageNotReadAbleException(HttpMessageNotReadableException e){
log.warn("异常类型: {} 无可读信息: {}", e.getClass(), e.getMessage());
return Result.error(ErrorEnum.HTTP_MESSAGE_NOT_READABLE_EXCEPTION);
}
/**
* 运行时异常
* @param e 运行异常对象
* @return Result
*/
@ExceptionHandler(value = RuntimeException.class)
public Result<String> runtimeException(RuntimeException e){
log.error("运行时异常:{}", e.getMessage());
if (e instanceof RRException){
RRException rrException = (RRException) e;
return Result.error(rrException.getCode(), rrException.getMsg());
}
return Result.error(ErrorEnum.RUNTIME_EXCEPTION);
}
/**
* 请求不支持
* @return Result
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public Result<String> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e){
log.warn("暂不支持该请求: {}", e.getMessage());
return Result.error("暂不支持此请求方式");
}
/**
* 参数类型错误
* @return Result
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public Result<String> methodArgument(MethodArgumentTypeMismatchException e){
log.warn("参数发生错误: {}", e.getMessage());
return Result.error("参数发生错误");
}
/**
* 缺少请求参数
* @param e 缺少请求参数异常
* @return Result
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public Result<String> exception(MissingServletRequestParameterException e){
log.warn("缺少请求参数: {}", e.getMessage());
return Result.error("缺少请求参数");
}
}

View File

@@ -0,0 +1,70 @@
package com.dpkj.common.exception;
import lombok.Getter;
/**
* 错误返回枚举类
*/
@Getter
public enum ErrorEnum implements ErrorInterface{
// ==========================================================================
/**
* 成功范围
* @code 200
* @apiNote 访问成功
*/
SUCCESS(200, "访问成功"),
/**
* 系统异常
* @code 500
* @apiNote 系统异常
*/
FAIL(500, "系统异常"),
/**
* 调用对象位空(null)
* @code 10002
* @apiNote 调用对象位空(null)
*/
NULL_POINTER_EXCEPTION(501, "调用对象位空(null)"),
/**
* 运行时异常,
* @code 10003
* @apiNote 系统发生错误,请联系管理员
*/
RUNTIME_EXCEPTION(502, "系统发生错误,请联系管理员"),
/**
* Http传入的参数没有可以读的数据
* @code 10004
* @apiNote 传入的数据不可读
*/
HTTP_MESSAGE_NOT_READABLE_EXCEPTION(503, "传入的数据不可读"),
// ==========================================================================
;
private final Integer code;
private final String message;
ErrorEnum(Integer code, String message){
this.message = message;
this.code = code;
}
@Override
public int getCode(){
return this.code;
}
@Override
public String getMessage(){
return this.message;
}
}

View File

@@ -0,0 +1,23 @@
package com.dpkj.common.exception;
/**
* 错误枚举接口,所有的错误枚举都需要实现该接口,如果需要自定义
* 错误信息可通过实现该接口与RespBean进行使用
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @see com.dpkj.common.vo.Result
* @since 2023-07-27 13:05:00
*/
public interface ErrorInterface {
/**
* 获取响应码
*/
int getCode();
/**
* 获取响应信息
*/
String getMessage();
}

View File

@@ -0,0 +1,59 @@
package com.dpkj.common.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 总的异常,所有的自定义异常都需要继承类
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @since 2023-07-24 09:42:00
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class RRException extends RuntimeException{
private static final long serialVersionUID = 1L;
private String msg;
private int code = 500;
public RRException(){
super("系统错误");
this.msg = "系统错误";
}
public RRException(Throwable throwable){
super(throwable);
}
public RRException(String msg) {
super(msg);
this.msg = msg;
}
public RRException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public RRException(int code, String msg) {
super(msg);
this.msg = msg;
this.code = code;
}
public RRException(ErrorInterface error){
super(error.getMessage());
this.code = error.getCode();
this.msg = error.getMessage();
}
public RRException(int code,String msg, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
}

View File

@@ -1,6 +1,7 @@
package com.dpkj.common.vo; package com.dpkj.common.vo;
import com.dpkj.common.constant.CommonConst; import com.dpkj.common.constant.CommonConst;
import com.dpkj.common.exception.ErrorEnum;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
@@ -49,7 +50,7 @@ public class Result<T> implements Serializable {
} }
public static <T> Result<T> ok(T data) { public static <T> Result<T> ok(T data) {
return error("", data); return ok("", data);
} }
public static <T> Result<T> ok(String msg, T data) { public static <T> Result<T> ok(String msg, T data) {
@@ -69,6 +70,14 @@ public class Result<T> implements Serializable {
return error(CommonConst.SC_500, msg, null); return error(CommonConst.SC_500, msg, null);
} }
public static <T> Result<T> error(ErrorEnum errorEnum) {
return error(errorEnum.getCode(), errorEnum.getMessage(), null);
}
public static <T> Result<T> error(int code, String message) {
return error(code, message, null);
}
public static <T> Result<T> error(int code, String msg, T data) { public static <T> Result<T> error(int code, String msg, T data) {
Result<T> r = new Result<T>(); Result<T> r = new Result<T>();
r.setCode(code); r.setCode(code);

View File

@@ -0,0 +1,89 @@
package com.dpkj.modules.autoReplyPrint.base;
import com.dpkj.common.exception.RRException;
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
import com.dpkj.modules.autoReplyPrint.utils.ImageUtils;
import com.sun.jna.Pointer;
import org.springframework.web.multipart.MultipartFile;
/**
* 图片打印基础实现
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-01-17 14:37:23
*/
public abstract class BaseImagePrint {
/**
* 必须要实现这个,获取句柄
* @param devName 设备名称/串口名称
* @return 窗口句柄
*/
public abstract Pointer getHandle(String devName);
public void printFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
Pointer handle = getHandle(devName);
try {
// 开始打印图片
boolean printTag = AutoReplyPrint.INSTANCE.CP_Pos_PrintRasterImageFromFile(handle, dstw, dsth, pszFile, binaryzation_method, compression_method);
if( !printTag ){
throw new RRException("打印图片失败");
}
// 切纸
AutoReplyPrint.INSTANCE.CP_BlackMark_SetBlackMarkPaperCutPosition(handle, 0);
boolean cutPaper = AutoReplyPrint.INSTANCE.CP_Pos_FeedAndHalfCutPaper(handle);
if ( !cutPaper){
throw new RRException("图片裁剪失败,请自行裁剪");
}
}catch (Exception e){
e.printStackTrace();
throw new RRException(e);
}finally {
if ( handle != null) AutoReplyPrint.INSTANCE.CP_Port_Close(handle);
}
}
public void printFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
// 获取图片
byte[] data = ImageUtils.imageToBytes(pszFile);
this.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
}
public void printFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method) {
Pointer handle = getHandle(devName);
try {
// 开始打印图片
boolean printTag = AutoReplyPrint.INSTANCE.CP_Pos_PrintRasterImageFromData(handle, dstw, dsth, data, data.length, binaryzation_method, compression_method);
if( !printTag ){
throw new RRException("打印图片失败");
}
// 切纸
AutoReplyPrint.INSTANCE.CP_BlackMark_SetBlackMarkPaperCutPosition(handle, 0);
boolean cutPaper = AutoReplyPrint.INSTANCE.CP_Pos_FeedAndHalfCutPaper(handle);
if ( !cutPaper){
throw new RRException("图片裁剪失败,请自行裁剪");
}
}catch (Exception e){
e.printStackTrace();
throw new RRException(e);
}finally {
if ( handle != null) AutoReplyPrint.INSTANCE.CP_Port_Close(handle);
}
}
public void printFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method){
byte[] data = ImageUtils.getByteToMultipartFile(file);
this.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
}
}

View File

@@ -0,0 +1,38 @@
package com.dpkj.modules.autoReplyPrint.controller;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.autoReplyPrint.service.AutoReplyPrintService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 壁挂打印机
*/
@Slf4j
@RestController
@RequestMapping("autoReplyPrint")
public class AutoReplyPrintController {
@Autowired
private AutoReplyPrintService autoReplyPrintService;
/**
* 获取打印机固件版本
*/
@GetMapping("CPPrinterGetPrinterFirmwareVersion")
public Result<?> CPPrinterGetPrinterFirmwareVersion(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String USBName) {
return autoReplyPrintService.CPPrinterGetPrinterFirmwareVersion(USBName);
}
/**
* 测试打印
*/
@GetMapping("/testWrite")
private Result<?> testWrite(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String USBName){
return this.autoReplyPrintService.testWrite(USBName);
}
}

View File

@@ -0,0 +1,78 @@
package com.dpkj.modules.autoReplyPrint.controller;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.autoReplyPrint.service.ImagePrintService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
/**
* 图片打印控制层
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-01-17 13:57:11
*/
@RequestMapping("/autoReplyPrint")
public class ImagePrintController {
@Resource(name = "USBImagePrint")
private ImagePrintService usbImagePrintService;
/**
* 图片打印,通过路径
*/
@GetMapping("/usb/imagePrint/path")
Result<Void> usbImagePrintFromPath(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String usbName,
@RequestParam(defaultValue = "500") Integer width,
@RequestParam(defaultValue = "500") Integer height,
@RequestParam String path, Integer binaryMethod, Integer compressionMethod){
this.usbImagePrintService.imagePrintFromPath(usbName, width, height, path, binaryMethod, compressionMethod);
return Result.ok("图片打印成功");
}
/**
* 图片打印通过byte[]
*/
@GetMapping("/usb/imagePrint/pathData")
Result<Void> usbImagePrintFromData(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String usbName,
@RequestParam(defaultValue = "500") Integer width,
@RequestParam(defaultValue = "500") Integer height,
@RequestParam String path, Integer binaryMethod, Integer compressionMethod){
this.usbImagePrintService.imagePrintFromPathData(usbName, width, height, path, binaryMethod, compressionMethod);
return Result.ok("图片打印成功");
}
/**
* 图片打印通过multipartFile文件
*/
@GetMapping("/usb/imagePrint/multipartFile")
Result<Void> usbImagePrintFromData(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String usbName,
@RequestParam(defaultValue = "500") Integer width,
@RequestParam(defaultValue = "500") Integer height,
@RequestParam MultipartFile file, Integer binaryMethod, Integer compressionMethod){
this.usbImagePrintService.imagePrintFromMultipartFile(usbName, width, height, file, binaryMethod, compressionMethod);
return Result.ok("图片打印成功");
}
/**
* 图片打印通过byte[]
*/
@GetMapping("/usb/imagePrint/data")
Result<Void> usbImagePrintFromData(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String usbName,
@RequestParam(defaultValue = "500") Integer width,
@RequestParam(defaultValue = "500") Integer height,
@RequestParam byte[] data, Integer binaryMethod, Integer compressionMethod){
this.usbImagePrintService.imagePrintFromData(usbName, width, height, data, binaryMethod, compressionMethod);
return Result.ok("图片打印成功");
}
}

View File

@@ -0,0 +1,54 @@
package com.dpkj.modules.autoReplyPrint.controller;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.autoReplyPrint.service.impl.TemplateService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 打印模板控制层
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-01-17 15:25:03
*/
@RestController
@RequestMapping("/autoReplyPrint")
public class TemplateController {
@Resource
private TemplateService templateService;
@GetMapping("/template/{templateName}")
public Result<String> testTemplate(@RequestParam String jsonData,
@RequestParam Integer width,
@RequestParam Integer height,
@PathVariable String templateName,
@RequestParam(defaultValue = "E:\\images") String saveDir) {
this.templateService.generateReceiptImage(JSONObject.parseObject(jsonData), templateName, width, height, saveDir);
return Result.ok("模板生成成功");
}
@GetMapping("/template")
public Result<String> testTemplate(@RequestParam String jsonData,
@RequestParam Integer width,
@RequestParam Integer height, @RequestParam(defaultValue = "E:\\images") String saveDir) {
String html = "<!DOCTYPE html>\n" +
"<html>\n" +
"<head><title>Test</title></head>\n" +
"<body>\n" +
"<h1>你好,我是一个好的模板</h1>\n" +
"<h2><span th:text='${name}'> </span></h2>\n" +
"</body>\n" +
"</html>";
this.templateService.generateReceiptImage(JSONObject.parseObject(jsonData), html, width, height, saveDir);
return Result.ok("模板生成成功");
}
}

View File

@@ -0,0 +1,19 @@
package com.dpkj.modules.autoReplyPrint.service;
import com.dpkj.common.vo.Result;
public interface AutoReplyPrintService {
/**
* 测试打印
* @param USBName USB名称
*/
Result<?> CPPrinterGetPrinterFirmwareVersion(String USBName);
/**
* 测试打印
* @param USBName USB名称
*/
Result<?> testWrite(String USBName);
}

View File

@@ -0,0 +1,59 @@
package com.dpkj.modules.autoReplyPrint.service;
import org.springframework.web.multipart.MultipartFile;
/**
* 图片打印服务接口
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-01-17 11:38:36
*/
public interface ImagePrintService {
/**
* 图片打印
* @param devName 设备名称/usb/tcp/com
* @param dstw 要打印的宽度
* @param dsth 要打印的高度
* @param pszFile 图片的路径
* @param binaryzation_method 图片二值化算法。0 表示抖动算法1 表示阀值算法2 表示误差扩散法。具体效果请测试查看。
* @param compression_method 最终打印数据的压缩方式各值定义如下0 不压缩1 一级压缩2 二级压缩。
*/
void imagePrintFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method);
/**
* 图片打印内部直接读取转换为byte进行打印
* @param devName 设备名称/usb/tcp/com
* @param dstw 要打印的宽度
* @param dsth 要打印的高度
* @param pszFile 图片的路径
* @param binaryzation_method 图片二值化算法。0 表示抖动算法1 表示阀值算法2 表示误差扩散法。具体效果请测试查看。
* @param compression_method 最终打印数据的压缩方式各值定义如下0 不压缩1 一级压缩2 二级压缩。
*/
void imagePrintFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method);
/**
* 直接传输数据进行打印
* @param devName 设备名称/usb/tcp/com
* @param dstw 要打印的宽度
* @param dsth 要打印的高度
* @param data 图片数据
* @param binaryzation_method 图片二值化算法。0 表示抖动算法1 表示阀值算法2 表示误差扩散法。具体效果请测试查看。
* @param compression_method 最终打印数据的压缩方式各值定义如下0 不压缩1 一级压缩2 二级压缩。
*/
void imagePrintFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method);
/**
* 直接传的MultipartFile文件过来
* @param devName 设备名称/usb/tcp/com
* @param dstw 要打印的宽度
* @param dsth 要打印的高度
* @param file 图片数据
* @param binaryzation_method 图片二值化算法。0 表示抖动算法1 表示阀值算法2 表示误差扩散法。具体效果请测试查看。
* @param compression_method 最终打印数据的压缩方式各值定义如下0 不压缩1 一级压缩2 二级压缩。
*/
void imagePrintFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method);
}

View File

@@ -0,0 +1,52 @@
package com.dpkj.modules.autoReplyPrint.service.impl;
import com.dpkj.common.exception.RRException;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.autoReplyPrint.service.AutoReplyPrintService;
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
import com.dpkj.modules.autoReplyPrint.utils.TestFunction;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
@Slf4j
@Service
public class AutoReplyPrintServiceImpl implements AutoReplyPrintService {
@Override
public Result<?> CPPrinterGetPrinterFirmwareVersion(String USBName) {
Result<String> ok = Result.ok();
try {
Pointer handle = AutoReplyPrint.INSTANCE.CP_Port_OpenUsb(USBName, 1);
if ( handle == null ) throw new NullPointerException();
ok.setResult(AutoReplyPrint.CP_Printer_GetPrinterFirmwareVersion_Helper.GetPrinterFirmwareVersion(handle));
} catch (Exception e) {
log.info("获取打印机固件版本失败: {}", e.getMessage(), e);
throw new NullPointerException("获取打印机固件版本失败");
}
return ok;
}
@Override
public Result<?> testWrite(String USBName) {
Pointer handle = AutoReplyPrint.INSTANCE.CP_Port_OpenUsb(USBName, 1);
AutoReplyPrint.CP_Printer_GetPrinterFirmwareVersion_Helper.GetPrinterFirmwareVersion(handle);
try {
TestFunction fun = new TestFunction();
Method m = TestFunction.class.getDeclaredMethod("Test_Port_Write", Pointer.class);
m.invoke(fun, handle);
}catch (Exception e){
e.printStackTrace();
throw new RRException("反射调用TestFunction错误");
}
return Result.ok("");
}
}

View File

@@ -0,0 +1,59 @@
package com.dpkj.modules.autoReplyPrint.service.impl;
import com.dpkj.common.exception.RRException;
import com.dpkj.modules.autoReplyPrint.base.BaseImagePrint;
import com.dpkj.modules.autoReplyPrint.service.ImagePrintService;
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
/**
* 串口图片打印
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-01-20 14:55:37
*/
@Service("COMImagePrint")
@Slf4j
public class COMImagePrintServiceImpl extends BaseImagePrint implements ImagePrintService {
@Override
public Pointer getHandle(String devName) {
try {
Pointer pointer = AutoReplyPrint.INSTANCE.CP_Port_OpenLpt(devName);
if ( pointer == null ){
throw new RRException();
}
return pointer;
}catch (Exception e){
e.printStackTrace();
throw new RRException("获取COM串口句柄失败");
}
}
@Override
public void imagePrintFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
super.printFromPath(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
}
@Override
public void imagePrintFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
super.printFromPathData(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
}
@Override
public void imagePrintFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method) {
super.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
}
@Override
public void imagePrintFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method) {
super.printFromMultipartFile(devName, dstw, dsth, file, binaryzation_method, compression_method);
}
}

View File

@@ -0,0 +1,63 @@
package com.dpkj.modules.autoReplyPrint.service.impl;
import com.dpkj.common.exception.RRException;
import com.dpkj.modules.autoReplyPrint.base.BaseImagePrint;
import com.dpkj.modules.autoReplyPrint.service.ImagePrintService;
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
import com.sun.jna.Pointer;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
/**
* tcp类型句柄的图片打印
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-01-20 14:46:06
*/
@Data
@Service("TCPImagePrint")
@Slf4j
public class TCPImagePrintServiceImpl extends BaseImagePrint implements ImagePrintService {
private String destIP; // 目标ip
private Short destPort; // 目标端口
@Override
public Pointer getHandle(String devName) {
try {
Pointer pointer = AutoReplyPrint.INSTANCE.CP_Port_OpenTcp("0", destIP, destPort, 300000, 1);
if ( pointer == null ){
throw new RRException();
}
return pointer;
}catch (Exception e){
e.printStackTrace();
throw new RRException("获取TCP句柄失败");
}
}
@Override
public void imagePrintFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
super.printFromPath(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
}
@Override
public void imagePrintFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
super.printFromPathData(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
}
@Override
public void imagePrintFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method) {
super.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
}
@Override
public void imagePrintFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method) {
super.printFromMultipartFile(devName, dstw, dsth, file, binaryzation_method, compression_method);
}
}

View File

@@ -0,0 +1,334 @@
package com.dpkj.modules.autoReplyPrint.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.exception.RRException;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.StringTemplateResolver;
import org.w3c.dom.Document;
import org.xhtmlrenderer.layout.SharedContext;
import org.xhtmlrenderer.simple.Graphics2DRenderer;
import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.thymeleaf.context.Context;
/**
* 模板服务类
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-01-18 15:33:51
*/
@Service
@Slf4j
public class TemplateService {
/**
* 生成小票图片
*
* @param data json数据用来填充模板
* @param template 模板html字符串或者模板名称
* @param width 图片宽度
* @param height 图片高度
* @param saveDir 图片的保存路径,如果为空,那么不进行图片的保存
* @return 图片字节数组
*/
public byte[] generateReceiptImage(JSONObject data, String template, int width, int height, String saveDir) {
try {
// 获取模板上下文
Context context = this.getContext(data);
TemplateEngine templateEngine = new TemplateEngine();
if (checkIsHtml(template)) {
StringTemplateResolver resolver = new StringTemplateResolver();
resolver.setTemplateMode("HTML");
// 设置模板引擎使用这个自定义的模板解析器
templateEngine.setTemplateResolver(resolver);
}else {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(true);
resolver.setApplicationContext(new AnnotationConfigReactiveWebApplicationContext());
templateEngine.setTemplateResolver(resolver);
}
// 渲染模板
String html = templateEngine.process(template, context);
BufferedImage image = this.generate(html, width, height);
// 保存图片
if (saveDir != null && !"".equals(saveDir)) {
String outputPath = saveDir + "\\genera_image_" + System.currentTimeMillis() + ".png";
ImageIO.write(image, "PNG", new File(outputPath));
}
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", byteOutputStream);
return byteOutputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
throw new RRException("图片打印失败");
}
}
/**
* 生成图片
* @param html html内容
*/
private BufferedImage generate(String html, int width, int height){
try {
// 转换为xhtml
String xhtml = this.htmlToXhtml(html);
// 转换为document
Document document = this.xhtmlToDocument(xhtml);
// 生成图片
return createImageToDocument(document, width, height);
} catch (Exception e) {
e.printStackTrace();
throw new RRException("图片打印失败");
}
}
/**
* 通过JsonObject进行context内容填充
* @param data jsonObject
* @return context
*/
private Context getContext(JSONObject data) {
// 创建Thymeleaf上下文
Context context = new Context();
Set<String> keys = data.keySet();
for (String key : keys) {
// 判单是否有图片生成统一后面采用的是_2base64Type
String[] split = key.split("_");
if (split.length > 1 && split[1].equals("2base64Type")) {
int width = split.length > 2 ? Integer.parseInt(split[2]) : 100;
int height = split.length > 3 ? Integer.parseInt(split[3]) : 100;
// 如果是图片类型,需要进行base64转换
String base64 = this.generateQRCode(String.valueOf(data.get(key)), width, height);
context.setVariable(split[0], "data:image/jpeg;base64," + base64);
} else {
// 普通字段直接设置
context.setVariable(key, data.get(key));
}
}
return context;
}
/**
* 根据内容生成二维码
* @param content 转换内容
*/
private String generateQRCode(String content, int width, int height) {
try {
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 设置字符编码为 UTF-8
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // 设置纠错级别
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
BufferedImage qrImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
qrImage.createGraphics();
Graphics2D graphics = (Graphics2D) qrImage.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
graphics.setColor(Color.BLACK);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (bitMatrix.get(i, j)) {
graphics.fillRect(i, j, 1, 1);
}
}
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(qrImage, "png", baos);
return Base64.getEncoder().encodeToString(baos.toByteArray());
}catch (Exception e){
log.error("二维码生成失败");
throw new RRException("二维码生成失败");
}
}
/**
* xhtml 转换为 Document
* @param xhtml xhtml
* @return document
* @throws Exception e
*/
private Document xhtmlToDocument(String xhtml) throws Exception {
// 创建DocumentBuilder
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析XHTML字符串为Document
return builder.parse(new ByteArrayInputStream(xhtml.getBytes(StandardCharsets.UTF_8)));
}
/**
* 转换将html转换为xhtml
* @param html html内容
* @return xhtml
*/
private String htmlToXhtml(String html) {
org.jsoup.nodes.Document doc = Jsoup.parse(html);
doc.outputSettings()
.syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml)
.charset(StandardCharsets.UTF_8);
// 不使用外部DTD
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
"<head>\n" +
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n" +
"<style type=\"text/css\">\n" +
"body { font-family: SimSun, serif; }\n" +
"</style>\n" +
"</head>\n" +
"<body>\n" +
doc.body().html() +
"\n</body>\n" +
"</html>";
}
/**
* 通过document转换为图片
* @param document doc
* @param width 图片的宽度
* @param height 图片的高度
* @return bufferedImage
*/
private BufferedImage createImageToDocument(Document document, int width, int height) {
try {
// 创建Dimension对象
Dimension dimension = new Dimension(width, height);
// 创建图片
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = (Graphics2D) image.getGraphics();
// 设置白色背景
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
// 设置渲染提示
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// 创建渲染器
Graphics2DRenderer renderer = new Graphics2DRenderer();
renderer.setDocument(document, null);
// 设置渲染参数
SharedContext sharedContext = renderer.getSharedContext();
sharedContext.setInteractive(false);
sharedContext.setDPI(256f);
// 使用系统默认字体假设系统默认字体支持UTF - 8
Font font = new Font(Font.DIALOG, Font.PLAIN, 20);
graphics.setFont(font);
// 使用Dimension对象进行布局
renderer.layout(graphics, dimension);
renderer.render(graphics);
graphics.dispose();
return image;
} catch (Exception e) {
throw new RRException("渲染图片失败", e);
}
}
/**
* 校验这个模板内容是不是html字符串而非模板名称
* @param template template
* @return 是否是html字符串
*/
private boolean checkIsHtml(String template){
try {
String pattern = "<(\"[^\"]*\"|'[^']*'|[^'\">])*>";
Pattern r = Pattern.compile(pattern);
return r.matcher(template).find();
}catch (Exception e) {
return false;
}
}
/**
* 将document对象转换为字符串
* @param doc document
* @return document转换为的字符串
*/
private String documentToString(Document doc) {
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(doc), new StreamResult(writer));
return writer.getBuffer().toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

View File

@@ -0,0 +1,60 @@
package com.dpkj.modules.autoReplyPrint.service.impl;
import com.dpkj.common.exception.RRException;
import com.dpkj.modules.autoReplyPrint.base.BaseImagePrint;
import com.dpkj.modules.autoReplyPrint.service.ImagePrintService;
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
/**
* 图片打印服务
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-01-17 11:38:36
*/
@Slf4j
@Service("USBImagePrint")
public class USBImagePrintServiceImpl extends BaseImagePrint implements ImagePrintService {
@Override
public Pointer getHandle(String devName) {
try {
Pointer pointer = AutoReplyPrint.INSTANCE.CP_Port_OpenUsb(devName, 1);
if ( pointer == null ){
throw new RRException();
}
return pointer;
}catch (Exception e){
e.printStackTrace();
throw new RRException("获取USB句柄失败");
}
}
@Override
public void imagePrintFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
super.printFromPath(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
}
@Override
public void imagePrintFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
super.printFromPathData(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
}
@Override
public void imagePrintFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method) {
super.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
}
@Override
public void imagePrintFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method) {
super.printFromMultipartFile(devName, dstw, dsth, file, binaryzation_method, compression_method);
}
}

View File

@@ -0,0 +1,429 @@
package com.dpkj.modules.autoReplyPrint.service.impl;
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
import com.dpkj.modules.autoReplyPrint.utils.TestFunction;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class samplelabel {
@SuppressWarnings("serial")
class GroupBoxPort extends JPanel implements ActionListener {
public JRadioButton rbCom = new JRadioButton();
public JRadioButton rbTcp = new JRadioButton();
public JRadioButton rbUsb = new JRadioButton();
public ButtonGroup rbGroup = new ButtonGroup();
public JComboBox cbxComName = new JComboBox();
public JComboBox cbxComBaudrate = new JComboBox();
public JTextField editIPAddress = new JTextField();
public JTextField editTcpPort = new JTextField();
public JComboBox cbxUsbName = new JComboBox();
public GroupBoxPort() {
this.setLayout(null);
this.setSize(760, 120);
this.setBorder(BorderFactory.createTitledBorder("Select Port"));
rbGroup.add(rbCom);
rbGroup.add(rbTcp);
rbGroup.add(rbUsb);
rbCom.setText("COM");
rbCom.setBounds(10, 20, 60, 30);
this.add(rbCom);
rbTcp.setText("TCP");
rbTcp.setBounds(10, 50, 60, 30);
this.add(rbTcp);
rbUsb.setText("USB");
rbUsb.setBounds(10, 80, 60, 30);
this.add(rbUsb);
cbxComName.setBounds(70, 20, 600, 30);
this.add(cbxComName);
cbxComBaudrate.setBounds(670, 20, 80, 30);
this.add(cbxComBaudrate);
editIPAddress.setBounds(70, 50, 600, 31);
this.add(editIPAddress);
editTcpPort.setBounds(670, 50, 81, 31);
this.add(editTcpPort);
cbxUsbName.setBounds(70, 80, 680, 30);
this.add(cbxUsbName);
rbUsb.setSelected(true);
editIPAddress.setText("192.168.1.87");
editTcpPort.setText("9100");
String[] ComBaudrateStringList = {"1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200",
"230400", "256000", "500000", "750000", "1125000", "1500000"};
for (String baudrate : ComBaudrateStringList)
cbxComBaudrate.addItem(baudrate);
cbxComBaudrate.setSelectedIndex(7);
}
@Override
public void actionPerformed(ActionEvent arg0) {
}
}
@SuppressWarnings("serial")
class TestForm extends JFrame implements ActionListener {
String[] testFunctionOrderedList = new String[]{
"Test_Label_SampleTicket_58MM_1",
"Test_Label_SampleTicket_80MM_1",
"Test_Port_Write",
"Test_Port_Read",
"Test_Port_Available",
"Test_Port_SkipAvailable",
"Test_Port_IsConnectionValid",
"Test_Printer_GetPrinterInfo",
"Test_Printer_ClearPrinterBuffer",
"Test_Printer_ClearPrinterError",
"Test_Pos_QueryPrintResult",
"Test_Pos_KickOutDrawer",
"Test_Pos_Beep",
"Test_Pos_PrintSelfTestPage",
"Test_Label_EnableLabelMode",
"Test_Label_DisableLabelMode",
"Test_Label_CalibrateLabel",
"Test_Label_FeedLabel",
"Test_Label_SetLabelPositionAdjustment",
"Test_Label_PageBegin_PagePrint",
"Test_Label_DrawText",
"Test_Label_DrawBarcode_UPCA",
"Test_Label_DrawBarcode_UPCE",
"Test_Label_DrawBarcode_EAN13",
"Test_Label_DrawBarcode_EAN8",
"Test_Label_DrawBarcode_CODE39",
"Test_Label_DrawBarcode_ITF",
"Test_Label_DrawBarcode_CODEBAR",
"Test_Label_DrawBarcode_CODE93",
"Test_Label_DrawBarcode_CODE128",
"Test_Label_DrawQRCode",
"Test_Label_DrawPDF417Code",
"Test_Label_DrawLine",
"Test_Label_DrawRect",
"Test_Label_DrawBox",
"Test_Label_DrawImageFromBufferedImage",
};
GroupBoxPort groupBoxPort = new GroupBoxPort();
JButton btnEnumPort = new JButton();
JButton btnOpenPort = new JButton();
JButton btnClosePort = new JButton();
JList listFunction = new JList(testFunctionOrderedList);
JScrollPane scrollFunction = new JScrollPane(listFunction);
JLabel labelFirmwareVersion = new JLabel();
JLabel labelResolution = new JLabel();
JLabel labelErrorStatus = new JLabel();
JLabel labelInfoStatus = new JLabel();
JLabel labelPrinterReceivedByteCount = new JLabel();
JLabel labelPrinterPrintedPageID = new JLabel();
Pointer h = Pointer.NULL;
AutoReplyPrint.CP_OnPortClosedEvent_Callback closed_callback = new AutoReplyPrint.CP_OnPortClosedEvent_Callback() {
@Override
public void CP_OnPortClosedEvent(Pointer handle, Pointer private_data) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
btnClosePort.doClick();
}
});
}
};
AutoReplyPrint.CP_OnPrinterStatusEvent_Callback status_callback = new AutoReplyPrint.CP_OnPrinterStatusEvent_Callback() {
@Override
public void CP_OnPrinterStatusEvent(Pointer h, final long printer_error_status,
final long printer_info_status, Pointer private_data) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Calendar calendar = Calendar.getInstance();
Date calendarDate = calendar.getTime();
String time = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss").format(calendarDate);
AutoReplyPrint.CP_PrinterStatus status = new AutoReplyPrint.CP_PrinterStatus(
printer_error_status, printer_info_status);
String error_status_string = String.format(" Printer Error Status: 0x%04X",
printer_error_status & 0xffff);
if (status.ERROR_OCCURED()) {
if (status.ERROR_CUTTER())
error_status_string += "[ERROR_CUTTER]";
if (status.ERROR_FLASH())
error_status_string += "[ERROR_FLASH]";
if (status.ERROR_NOPAPER())
error_status_string += "[ERROR_NOPAPER]";
if (status.ERROR_VOLTAGE())
error_status_string += "[ERROR_VOLTAGE]";
if (status.ERROR_MARKER())
error_status_string += "[ERROR_MARKER]";
if (status.ERROR_ENGINE())
error_status_string += "[ERROR_ENGINE]";
if (status.ERROR_OVERHEAT())
error_status_string += "[ERROR_OVERHEAT]";
if (status.ERROR_COVERUP())
error_status_string += "[ERROR_COVERUP]";
if (status.ERROR_MOTOR())
error_status_string += "[ERROR_MOTOR]";
}
String info_status_string = String.format(" Printer Info Status: 0x%04X",
printer_info_status & 0xffff);
if (status.INFO_LABELMODE())
info_status_string += "[Label Mode]";
if (status.INFO_LABELPAPER())
info_status_string += "[Label Paper]";
if (status.INFO_PAPERNOFETCH())
info_status_string += "[Paper Not Fetch]";
labelErrorStatus.setText(time + error_status_string);
labelInfoStatus.setText(time + info_status_string);
}
});
}
};
AutoReplyPrint.CP_OnPrinterReceivedEvent_Callback received_callback = new AutoReplyPrint.CP_OnPrinterReceivedEvent_Callback() {
@Override
public void CP_OnPrinterReceivedEvent(Pointer h, final int printer_received_byte_count,
Pointer private_data) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Calendar calendar = Calendar.getInstance();
Date calendarDate = calendar.getTime();
String time = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss").format(calendarDate);
labelPrinterReceivedByteCount
.setText(time + " PrinterReceived: " + printer_received_byte_count);
}
});
}
};
AutoReplyPrint.CP_OnPrinterPrintedEvent_Callback printed_callback = new AutoReplyPrint.CP_OnPrinterPrintedEvent_Callback() {
@Override
public void CP_OnPrinterPrintedEvent(Pointer h, final int printer_printed_page_id, Pointer private_data) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Calendar calendar = Calendar.getInstance();
Date calendarDate = calendar.getTime();
String time = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss").format(calendarDate);
labelPrinterPrintedPageID.setText(time + " PrinterPrinted: " + printer_printed_page_id);
}
});
}
};
private void AddCallback() {
AutoReplyPrint.INSTANCE.CP_Port_AddOnPortClosedEvent(closed_callback, Pointer.NULL);
AutoReplyPrint.INSTANCE.CP_Printer_AddOnPrinterStatusEvent(status_callback, Pointer.NULL);
AutoReplyPrint.INSTANCE.CP_Printer_AddOnPrinterReceivedEvent(received_callback, Pointer.NULL);
AutoReplyPrint.INSTANCE.CP_Printer_AddOnPrinterPrintedEvent(printed_callback, Pointer.NULL);
}
private void RemoveCallback() {
AutoReplyPrint.INSTANCE.CP_Port_RemoveOnPortClosedEvent(closed_callback);
AutoReplyPrint.INSTANCE.CP_Printer_RemoveOnPrinterStatusEvent(status_callback);
AutoReplyPrint.INSTANCE.CP_Printer_RemoveOnPrinterReceivedEvent(received_callback);
AutoReplyPrint.INSTANCE.CP_Printer_RemoveOnPrinterPrintedEvent(printed_callback);
}
public TestForm() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
this.setVisible(true);
this.setSize(800, 700);
this.setTitle(AutoReplyPrint.INSTANCE.CP_Library_Version());
groupBoxPort.setLocation(10, 10);
this.add(groupBoxPort);
btnEnumPort.setBounds(20, 140, 240, 30);
btnEnumPort.setText("EnumPort");
btnEnumPort.addActionListener(this);
this.add(btnEnumPort);
btnOpenPort.setBounds(270, 140, 240, 30);
btnOpenPort.setText("OpenPort");
btnOpenPort.addActionListener(this);
this.add(btnOpenPort);
btnClosePort.setBounds(520, 140, 240, 30);
btnClosePort.setText("ClosePort");
btnClosePort.addActionListener(this);
this.add(btnClosePort);
labelFirmwareVersion.setBounds(20, 460, 740, 30);
labelFirmwareVersion.setText("Firmware Version:");
this.add(labelFirmwareVersion);
labelResolution.setBounds(20, 490, 740, 30);
labelResolution.setText("Resolution:");
this.add(labelResolution);
labelErrorStatus.setBounds(20, 520, 740, 30);
labelErrorStatus.setText("Error Status:");
this.add(labelErrorStatus);
labelInfoStatus.setBounds(20, 550, 740, 30);
labelInfoStatus.setText("Info Status:");
this.add(labelInfoStatus);
labelPrinterReceivedByteCount.setBounds(20, 580, 740, 30);
labelPrinterReceivedByteCount.setText("Printer Received Byte Count:");
this.add(labelPrinterReceivedByteCount);
labelPrinterPrintedPageID.setBounds(20, 610, 740, 30);
labelPrinterPrintedPageID.setText("Printer Printed Page ID:");
this.add(labelPrinterPrintedPageID);
scrollFunction.setBounds(20, 180, 740, 270);
this.add(scrollFunction);
listFunction.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// listFunction.addListSelectionListener(this);
listFunction.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
if (e.getSource() == listFunction) {
String functionName = listFunction.getSelectedValue().toString();
if ((functionName == null) || (functionName.isEmpty()))
return;
try {
TestFunction fun = new TestFunction();
Method m = TestFunction.class.getDeclaredMethod(functionName, Pointer.class);
m.invoke(fun, h);
} catch (Throwable tr) {
tr.printStackTrace();
}
}
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
});
btnEnumPort.doClick();
refreshUI();
AddCallback();
}
protected void finalize() throws Throwable {
RemoveCallback();
super.finalize();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == btnEnumPort) {
String[] port_list = null;
groupBoxPort.cbxComName.removeAllItems();
port_list = AutoReplyPrint.CP_Port_EnumCom_Helper.EnumCom();
if (port_list != null) {
for (String port : port_list)
groupBoxPort.cbxComName.addItem(port);
}
groupBoxPort.cbxUsbName.removeAllItems();
port_list = AutoReplyPrint.CP_Port_EnumUsb_Helper.EnumUsb();
if (port_list != null) {
for (String port : port_list)
groupBoxPort.cbxUsbName.addItem(port);
}
} else if (e.getSource() == btnOpenPort) {
if (groupBoxPort.rbCom.isSelected()) {
if (groupBoxPort.cbxComName.getSelectedItem() == null)
return;
String name = groupBoxPort.cbxComName.getSelectedItem().toString();
int baudrate = Integer.parseInt(groupBoxPort.cbxComBaudrate.getSelectedItem().toString());
h = AutoReplyPrint.INSTANCE.CP_Port_OpenCom(name, baudrate, AutoReplyPrint.CP_ComDataBits_8,
AutoReplyPrint.CP_ComParity_NoParity, AutoReplyPrint.CP_ComStopBits_One, AutoReplyPrint.CP_ComFlowControl_None,
1);
} else if (groupBoxPort.rbUsb.isSelected()) {
if (groupBoxPort.cbxUsbName.getSelectedItem() == null)
return;
String name = groupBoxPort.cbxUsbName.getSelectedItem().toString();
h = AutoReplyPrint.INSTANCE.CP_Port_OpenUsb(name, 1);
} else if (groupBoxPort.rbTcp.isSelected()) {
String ip = groupBoxPort.editIPAddress.getText();
int port = Integer.parseInt(groupBoxPort.editTcpPort.getText());
h = AutoReplyPrint.INSTANCE.CP_Port_OpenTcp(null, ip, (short) port, 5000, 1);
}
refreshUI();
} else if (e.getSource() == btnClosePort) {
if (h != Pointer.NULL) {
AutoReplyPrint.INSTANCE.CP_Port_Close(h);
h = Pointer.NULL;
}
refreshUI();
}
}
private void refreshUI() {
groupBoxPort.setEnabled(h == Pointer.NULL);
btnEnumPort.setEnabled(h == Pointer.NULL);
btnOpenPort.setEnabled(h == Pointer.NULL);
btnClosePort.setEnabled(h != Pointer.NULL);
listFunction.setEnabled(h != Pointer.NULL);
if (h != Pointer.NULL) {
labelFirmwareVersion.setText("Firmware Version: "
+ AutoReplyPrint.CP_Printer_GetPrinterFirmwareVersion_Helper.GetPrinterFirmwareVersion(h));
IntByReference width_mm = new IntByReference();
IntByReference height_mm = new IntByReference();
IntByReference dots_per_mm = new IntByReference();
if (AutoReplyPrint.INSTANCE.CP_Printer_GetPrinterResolutionInfo(h, width_mm, height_mm, dots_per_mm)) {
labelResolution.setText("Resolution: " + "width:" + width_mm.getValue() + "mm " + "height:"
+ height_mm.getValue() + "mm " + "dots_per_mm:" + dots_per_mm.getValue());
}
}
}
}
static TestForm testform;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
testform = new samplelabel().new TestForm();
}
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
package com.dpkj.modules.autoReplyPrint.utils;
import com.dpkj.common.exception.RRException;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
/**
* 图片转换工具
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-01-17 14:06:09
*/
public class ImageUtils {
/**
* 读取图片并转换为byte数组
* @param imagePath 图片路径
* @return byte数组
*/
public static byte[] imageToBytes(String imagePath) {
try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(Paths.get(imagePath)));
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
throw new RRException("读取图片信息失败");
}
}
/**
* 获取文件byte[],通过 MultipartFile
* @param file 文件信息
* @return byte数组
*/
public static byte[] getByteToMultipartFile(MultipartFile file){
// 验证文件是否为空
if (file.isEmpty()) {
throw new RRException("文件内容为空");
}
// 验证文件类型
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
throw new RRException("只支持图片文件");
}
// 获取文件扩展名
String fileName = file.getOriginalFilename();
String fileExtension = fileName != null ?
fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase() : "";
// 验证扩展名
List<String> allowedExtensions = Arrays.asList("jpg", "jpeg", "png", "gif");
if (!allowedExtensions.contains(fileExtension)) {
throw new RRException("不支持的文件格式");
}
try {
// 转换为字节数组
return file.getBytes();
}catch (Exception e){
e.printStackTrace();
throw new RRException("获取文件字节失败");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
package com.dpkj.modules.autoReplyPrint.utils;
import javax.swing.*;
import java.io.FileInputStream;
import java.io.InputStream;
public class TestUtils {
public static byte[] ReadFromFile(String fileName) {
byte[] data = null;
try {
InputStream in = new FileInputStream(fileName);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (Throwable tr) {
tr.printStackTrace();
}
return data;
}
public static void showMessageOnUiThread(final String msg) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog(null, msg);
}
});
}
}

View File

@@ -0,0 +1,139 @@
package com.dpkj.modules.cardReader.constant;
/**
* StatusConstant 枚举类,用于表示读卡器的各种状态。
* 该枚举包含了不同的状态码及其对应的状态描述,提供了根据状态码获取状态描述的方法。
*/
public enum StatusConstant {
/**
* 执行成功
*/
IFD_OK("0", "执行成功"),
/**
* 卡片类型不对
*/
IFD_ICC_TypeError("-1", "卡片类型不对"),
/**
* 无卡
*/
IFD_ICC_NoExist("-2", "无卡"),
/**
* 有卡未上电
*/
IFD_ICC_NoPower("-3", "有卡未上电"),
/**
* 卡片无应答
*/
IFD_ICC_NoResponse("-4", "卡片无应答"),
/**
* 卡片状态异常
*/
IFD_ICC_StatusErr("-5", "卡片状态异常"),
/**
* 读写器执行指令失败
*/
IFD_ICC_ExecuteErr("-6", "读写器执行指令失败"),
/**
* 读卡器连接错
*/
IFD_ConnectError("-11", "读卡器连接错"),
/**
* 未建立连接(没有执行打开设备函数)
*/
IFD_UnConnected("-12", "未建立连接(没有执行打开设备函数)"),
/**
* (动态库)不支持该命令
*/
IFD_BadCommand("-13", "(动态库)不支持该命令"),
/**
* (发给动态库的)命令参数出错
*/
IFD_ParameterError("-14", "(发给动态库的)命令参数出错"),
/**
* 信息校验和出错
*/
IFD_CheckSumError("-15", "信息校验和出错"),
/**
* 超时
*/
IFD_TimeOut("-16", "超时"),
/**
* 读取固件信息错误
*/
IFD_ReadErr("-17", "读取固件信息错误"),
/**
* 底层返回数据长度错误
*/
IFD_DeviceDataLen("-18", "底层返回数据长度错误"),
/**
* 卡片数据异常错误
*/
IFD_CardDataErr("-19", "卡片数据异常错误"),
/**
* 标志位不支持
*/
IFD_TAGNoExist("-20", "标志位不支持"),
/**
* 读取文件异常
*/
IFD_FileDataErr("-21", "读取文件异常"),
/**
* 设备返回数据异常
*/
IFD_DeviceDataErr("-22", "设备返回数据异常"),
/**
* 加载动态库失败
*/
IFD_LoadDllError("-23", "加载动态库失败"),
/**
* 用户取消操作
*/
IFD_UserCancel("-24", "用户取消操作"),
/**
* 密码长度错误
*/
IFD_KeyLengthErr("-25", "密码长度错误");
private final String code;
private final String message;
/**
* 构造函数,用于初始化枚举的状态码和状态信息
* @param code 状态码
* @param message 状态信息
*/
StatusConstant(String code, String message) {
this.code = code;
this.message = message;
}
/**
* 获取状态码
* @return 状态码
*/
public String getCode() {
return code;
}
/**
* 获取状态信息
* @return 状态信息
*/
public String getMessage() {
return message;
}
/**
* 根据状态码获取相应的枚举实例
* @param code 状态码
* @return 对应的枚举实例,如果不存在则返回 null
*/
public static String fromCode(String code) {
for (StatusConstant status : values()) {
if (status.code.equals(code)) {
return status.getMessage();
}
}
return null;
}
}

View File

@@ -0,0 +1,53 @@
package com.dpkj.modules.cardReader.controller;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.cardReader.service.CardReaderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 多合一读卡器
*/
@Slf4j
@RestController
@RequestMapping("readCard")
public class CardReaderController {
@Autowired
private CardReaderService cardReaderService;
/**
* 连接设备并读取身份证信息
* @return
*/
@GetMapping("IDCardReader")
public Result IDCardReader() {
try {
return cardReaderService.IDCardReader();
} catch (Exception e) {
e.printStackTrace();
log.info("设备连接失败 {}", e.getMessage());
return Result.error("设备连接失败");
}
}
/**
* 连接设备并读取社保卡信息
* @return
*/
@GetMapping("SocialSecurityCardReader")
public Result<?> SocialSecurityCardReader() {
try {
return cardReaderService.SocialSecurityCardReader();
} catch (Exception e) {
e.printStackTrace();
log.info("连接设备并读取社保卡信息失败 {}", e.getMessage());
return Result.error("连接设备并读取社保卡信息失败");
}
}
}

View File

@@ -0,0 +1,11 @@
package com.dpkj.modules.cardReader.service;
import com.dpkj.common.vo.Result;
public interface CardReaderService {
Result IDCardReader();
Result SocialSecurityCardReader();
}

View File

@@ -0,0 +1,98 @@
package com.dpkj.modules.cardReader.service.impl;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.cardReader.service.CardReaderService;
import com.dpkj.modules.cardReader.utils.CardReaderSdk;
import com.dpkj.modules.cardReader.utils.CardReaderUtil;
import com.dpkj.modules.cardReader.utils.SocialSecurityCardInfoVO;
import com.dpkj.modules.cardReader.utils.UserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class CardReaderServiceImpl implements CardReaderService {
private CardReaderSdk cardReaderSdk = CardReaderUtil.getCardReaderSDK();
@Value("${IDCardReader.waitingTime}")
private Long waitingTime;
@Override
public Result IDCardReader() {
Long handle = null;
UserInfoVO userInfo = null;
try {
// 连接指定设备与电脑端口,即打开端口
handle = cardReaderSdk.ICC_Reader_Open("USB1");
log.info("连接指定设备与电脑端口,即打开端口:{}", handle);
long startTime = System.currentTimeMillis();
while (true) {
try {
Long idCard = cardReaderSdk.PICC_Reader_ReadIDCard(handle);
userInfo = new UserInfoVO(cardReaderSdk);
if (!"".equals(userInfo.getName()) && userInfo.getName() != null) {
break;
}
} catch (Exception e) {
// 处理创建 UserInfoVO 时可能出现的异常
log.info("创建 UserInfoVO 对象时发生异常", e);
}
long elapsedTime = System.currentTimeMillis() - startTime;
if (elapsedTime >= waitingTime) {
log.info("读取 ID 卡信息超时,等待了 {} 秒。", waitingTime);
return Result.error("读取 ID 卡信息超时,请重试。");
}
try {
// 短暂休眠,避免 CPU 空转
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
return Result.ok(userInfo);
} finally {
if (handle != null) {
// 不管什么情况最终都要关闭端口
cardReaderSdk.ICC_Reader_Close(handle);
}
}
}
@Override
public Result SocialSecurityCardReader() {
Long handle = null;
try {
// 连接指定设备与电脑端口,即打开端口
handle = cardReaderSdk.ICC_Reader_Open("USB1");
log.info("连接指定设备与电脑端口,即打开端口:{}", handle);
// 读基本信息
String bas = cardReaderSdk.iReadCardBas(3);
log.info("读基本信息:{}", bas);
String[] split = bas.split("\\|");
SocialSecurityCardInfoVO socialSecurityCardInfoVO = new SocialSecurityCardInfoVO();
// 区号代码截取
socialSecurityCardInfoVO.setAreaCode(split[0].substring(split[0].length() - 6));
socialSecurityCardInfoVO.setSocialSecurityNo(split[1]);
socialSecurityCardInfoVO.setCardNumber(split[2]);
socialSecurityCardInfoVO.setIdentificationCode(split[3]);
socialSecurityCardInfoVO.setName(split[4]);
socialSecurityCardInfoVO.setCardResetInformation(split[5]);
socialSecurityCardInfoVO.setSpecificationVersion(split[6]);
socialSecurityCardInfoVO.setIssuanceDate(split[7]);
socialSecurityCardInfoVO.setExpireDate(split[8]);
socialSecurityCardInfoVO.setTerminalNumber(split[9]);
socialSecurityCardInfoVO.setTerminalDeviceNumber(split[10]);
return Result.ok(socialSecurityCardInfoVO);
} catch (Exception e) {
log.error("Error occurred during card reader operation: ", e);
if (handle != null) {
// 关闭端口
cardReaderSdk.ICC_Reader_Close(handle);
}
return Result.error("操作失败:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,418 @@
package com.dpkj.modules.cardReader.utils;
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface CardReaderSdk extends Library {
CardReaderSdk INSTANCE = (CardReaderSdk) Native.loadLibrary("SSSE32", CardReaderSdk.class);
/**
* 连接指定设备与电脑端口,即打开端口
*
* @param devName 接口名称如”USB1”
* @return >0成功<=0失败
*/
Long ICC_Reader_Open(String devName);
/**
* 关闭已打开的电脑接口
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @return =0成功非0失败
*/
Long ICC_Reader_Close(Long ReaderHandle);
/**
* 读写器产生蜂鸣
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param time 蜂鸣时间,以毫秒为单位
* @return =0成功非0失败
*/
Long ICC_PosBeep(Long ReaderHandle, Long time);
/**
* 获取动态库及设备版本信息
*
* @return 设备及动态库版本信息 =0成功非0失败
*/
Long ICC_Reader_Libinfo();
/**
* 从EEPROM读取数据
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param offset 偏移地址(起始地址)
* @param length 要读取的数据的长度
* @return 读取的数据 =0成功非0失败
*/
Long ICC_Reader_ReadEEPROM(Long ReaderHandle, int offset, int length);
/**
* 往EEPROM写入数据
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param offset 偏移地址(起始地址)
* @param length 要写入的数据的长度
* @param buffer 待写入的数据
* @return =0成功非0失败
*/
Long ICC_Reader_WriteEEPROM(Long ReaderHandle, int offset, int length, String buffer);
/**
* 播放语音SSSE32.dll暴露函数中未发现该函数但是文档中有
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param voiceType 语音类型:
* 1、请插卡
* 2、请刷卡
* 3、读卡错误
* 4、请输入密码
* 5、密码错误
* 6、操作成功
* 7、操作超时
* 8、操作失败
* 9、请取回卡
* 10、请重新输入密码
* 11、请再次输入密码
* 12、请输入新密码
* 13、请输入旧密码
* 14、请确认新密码
* @return =0成功非0失败
*/
@Deprecated
Long ICC_Reader_DispSound(Long ReaderHandle, String voiceType);
/**
* 接触 CPU 卡上电复位,返回值数据格式为十六进制字符串
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param ICCSlotNo ICC插槽号 0x01 :大卡座
* 0x02 :副卡座
* 0x11SAM1卡座
* 0x12SAM2卡座
* 0x13SAM3卡座
* 0x14SAM4卡座
* 0x3x非接CPU卡
* @return 上电返回ATR值数据格式为十六进制字符串
*/
Long ICC_Reader_PowerOnHEX(Long ReaderHandle, String ICCSlotNo);
/**
* CPU 卡下电
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param ICCSlotNo ICC插槽号 0x01 :大卡座
* 0x02 :副卡座
* 0x11SAM1卡座
* 0x12SAM2卡座
* 0x13SAM3卡座
* 0x14SAM4卡座
* 0x3x非接CPU卡
* @return =0成功非0失败
*/
Long ICC_Reader_PowerOff(Long ReaderHandle, String ICCSlotNo);
/**
* 获取卡座状态
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param ICCSlotNo ICC插槽号 0x01 :大卡座
* 0x02 :副卡座
* 0x11SAM1卡座
* 0x12SAM2卡座
* 0x13SAM3卡座
* 0x14SAM4卡座
* 0x3x非接CPU卡
* @return = 0 表示卡座有卡,其他见状态码
*/
Long ICC_Reader_GetStatus(Long ReaderHandle, String ICCSlotNo);
/**
* 接触 CPU 卡执行APDU命令
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param ICCSlotNo ICC插槽号 0x01 :大卡座
* 0x02 :副卡座
* 0x11SAM1卡座
* 0x12SAM2卡座
* 0x13SAM3卡座
* 0x14SAM4卡座
* 0x3x非接CPU卡
* @param CommandAPDU APDU命令数据以十六进制字符串形式输入
* @return 大于 0 表示执行成功,其值为 Response_APDU 的数据长度.否则表示执行失败
*/
Long ICC_Reader_ApplicationHEX(Long ReaderHandle, String ICCSlotNo, String CommandAPDU);
/**
* 4442卡片上电数据以十六进制字符串输出
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @return 返回卡片上电信息 = 0 表示成功,非0失败
*/
Long ICC_Reader_4428_PowerOnHEX(Long ReaderHandle);
/**
* 4442卡片下电
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @return = 0 表示成功,非0失败
*/
Long ICC_Reader_4442_PowerOff(Long ReaderHandle);
/**
* 读取4442卡的指定字段内容,以十六进制字符串输出
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param offset 起始地址需小于256
* @param len 数据长度offset+len需小于256
* @return 返回卡片读取信息 = 0 表示成功,非0失败
*/
Long ICC_Reader_4428_ReadHEX(Long ReaderHandle, int offset, int len);
/**
* 写 4442 卡的指定字段内容,以十六进制字符串输入参数
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param offset 起始地址需小于256
* @param len 数据长度offset+len需小于256
* @param data 待写入卡片数据
* @return 返回卡片读取信息 = 0 表示成功,非0失败
*/
Long ICC_Reader_4428_WriteHEX(Long ReaderHandle, int offset, int len, String data);
/**
* 4442卡认证密钥
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param keyHex 卡密钥
* @return = 0 表示成功,非0失败
*/
Long ICC_Reader_4442_Verify(Long ReaderHandle, String keyHex);
/** ----- 非接触操作函数 -------------- */
/**
* 查找天线磁场区域内有无卡,只有卡片上电后才能查找到
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @return = 0 表示成功,非0失败
*/
Long PICC_Reader_Request(Long ReaderHandle);
/**
* 天线操作
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param flag 00-关场强 01-开场强
* @return = 0 表示成功,非0失败
*/
Long PICC_Reader_RFControl(Long ReaderHandle, String flag);
/** --------------- 非接 CPU 卡操作函数 ------------ */
/**
* 非接TypeA CPU卡上电复位输出十六进制字符串SSSE32.dll暴露函数中未发现该函数但是文档中有
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @return 输出上电成功返回的卡片复位信息(ATS) 返回ATS长度大于0 表示成功,其他失败
*/
@Deprecated
Long PICC_Reader_PowerOnTypeAHEX(Long ReaderHandle);
/**
* 非接TypeB CPU卡上电复位输出十六进制字符串SSSE32.dll暴露函数中未发现该函数但是文档中有
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @return 输出上电成功返回的卡片复位信息(ATS) 返回ATS长度大于0 表示成功,其他失败
*/
@Deprecated
Long PICC_Reader_PowerOnTypeBHEX(Long ReaderHandle);
/**
* 非接触 CPU 卡执行APDU命令命令以十六进制字符串传输SSSE32.dll暴露函数中未发现该函数但是文档中有
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @param CommandAPDU APDU命令数据以十六进制字符串形式输入
* @return APDU命令执行后, 响应的数据, 返回数据为十六进制字符串
* 大于 0 表示执行成功,其值为 Response_APDU 的数据长度.否则表示执行失败
*/
@Deprecated
Long PICC_Reader_ApplicationHEX(Long ReaderHandle, String CommandAPDU);
/** ---------------- 二代证/外国人居留证/港澳台居住证 --------------- */
/**
* 读取身份证物理ID号
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @return 返回身份证物理ID信息 = 0 表示成功,非0失败
*/
Long PICC_Reader_Read_CardID(Long ReaderHandle, byte[] response);
/**
* 读取身份证物理ID号
*
* @param ReaderHandle ICCReaderOpen函数所返回的值
* @return 返回错误信息 = 0 表示成功,非0失败
*/
Long PICC_Reader_ReadIDCard(Long ReaderHandle);
/** 以下函数须在 “PICC_Reader_ReadIDCard” 函数执行成功之后调用,否则获取不到有效信息 */
/**
* 获取证件类型
*
* @return 0居民身份证
* 1外国人永久居留证
* 2港澳台居民居住证
* 4新外国人永久居留证
*/
Integer GetCardType();
/**
* 姓名(类型为 1 时表示:外国人中文姓名)
*/
Integer GetName(byte[] outParam);
/**
* 性别
*/
Integer GetSex(byte[] outParam);
/**
* 民族
*/
Integer GetNation(byte[] outParam);
/**
* 出生日期
*/
Integer GetBirth(byte[] outParam);
/**
* 住址
*/
Integer GetAddress(byte[] outParam);
/**
* 公民身份证号码(类型为 1时表示外国人居留证号码)
*/
Integer GetCertNo(byte[] outParam);
/**
* 签发机关
*/
Integer GetDepartemt(byte[] outParam);
/**
* 有效起始日期
*/
Integer GetEffectDate(byte[] outParam);
/**
* 有效截止日期
*/
Integer GetExpireDate(byte[] outParam);
/**
* bmp 格式照片数据
*/
String GetBmpFileData(byte[] outParam);
/**
* 生成照片
* TODO 入参参考文中3.4.1
*/
Integer GetBmpFile(byte[] outParam);
/**
* 是否含存在指纹信息:存在时返回 512 或者 1024
* 不存在时返回 0
*/
Integer IsFingerExist();
/**
* 获取指纹数据:成功时返回获取到的字节长度
*/
Integer GetFingerprint(byte[] outParam);
/**
* 外国人英文姓名
*/
Integer GetEnName(byte[] outParam);
/**
* 外国人国籍代码符合GB/T2659-2000规定
*/
Integer GetNationalityCode(byte[] outParam);
/**
* 港澳台通行证号码
*/
Integer GetTXZHM(byte[] outParam);
/**
* 港澳台通行证签发次数
*/
Integer GetTXZQFCS(byte[] outParam);
/**
* 外国人换证次数
*/
Integer GetHZCS(byte[] outParam);
/** ------------ 社保卡 ---------*/
/**
* 选择社保卡社会保障系统环境后,通过 PSAM 卡对社保卡进行内部认证,通过后将卡内的基本信息读出返回。
*
* @param iType 操作卡的类型
* 1-接触式操作卡;
* 2-非接触式操作卡;
* 3-自动寻卡,接触式操作卡优先;
* 4-自动寻卡,非接触式操作卡优先
* @return
*/
String iReadCardBas(Integer iType);
/**
* 基于加密机的读基本信息(步骤一)
* 选择社会保障系统环境后,返回内部认证和外部认证所需信息。
*
* @param iType 操作卡的类型
* 1-接触式操作卡;
* 2-非接触式操作卡;
* 3-自动寻卡,接触式操作卡优先;
* 4-自动寻卡,非接触式操作卡优先
* @return
*/
Long iReadCardBas_HSM_Step1(Integer iType);
/**
* 基于加密机的读基本信息(步骤二)
* 根据加密机返回的内部认证和外部认证结果数据对社保卡进行内部认证和外部认证,通过后将卡内的基本信息读出返回。
*
* @param pKey 加密机返回的内部认证和外部认证结果数据,依次为:内部认证结果数据
* 即内部认证鉴别数据16 位和内部认证鉴别所需的原始信息16 位)拼接组
* 成、外部认证结果数据即外部认证鉴别数据16 位)和外部认证鉴别所需的
* 原始信息16 位)拼接组成)。各数据项之间以“|”分割,且最后一个数据项以“|”结尾。
* @return
*/
Long iReadCardBas_HSM_Step2(String pKey, byte[] outParam);
}

View File

@@ -0,0 +1,25 @@
package com.dpkj.modules.cardReader.utils;
import com.dpkj.common.exception.RRException;
import com.sun.jna.Native;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CardReaderUtil {
/**
* 获取 多合一读卡器 实例,同时注册 CardReaderSdk 控件。
*
* @return CardReaderSdk 实例
* @throws RRException 如果注册控件失败,抛出此异常
*/
public static CardReaderSdk getCardReaderSDK() throws RRException {
try {
return Native.loadLibrary("SSSE32", CardReaderSdk.class);
} catch (UnsatisfiedLinkError e) {
log.info("[CardReader][AutoReplyPrint2.getCardReaderSDK] SDK注册失败 {}", e.getMessage());
throw new RRException("Failed to load CardReaderSdk library: ", e);
}
}
}

View File

@@ -0,0 +1,29 @@
package com.dpkj.modules.cardReader.utils;
import lombok.Data;
@Data
public class SocialSecurityCardInfoVO {
// 发卡地区行政区划代码
private String areaCode;
// 社会保障号码
private String socialSecurityNo;
// 卡号
private String cardNumber;
// 卡识别码
private String identificationCode;
// 姓名
private String name;
// 卡复位信息
private String cardResetInformation;
// 规范版本
private String specificationVersion;
// 发卡日期
private String issuanceDate;
// 卡有效期
private String expireDate;
// 终端机编号
private String terminalNumber;
// 终端设备号
private String terminalDeviceNumber;
}

View File

@@ -0,0 +1,73 @@
package com.dpkj.modules.cardReader.utils;
import com.dpkj.common.exception.RRException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.function.Consumer;
@Data
public class UserInfoVO {
// 姓名
private String name;
// 性别
private String sex;
// 民族
private String nation;
// 出生年月
private String born;
// 地址
private String address;
// 身份证号
private String IDCardNo;
// 签发机关
private String grantDept;
// 有效期开始日期
private String userLifeBegin;
// 有效期过期时间
private String userLifeEnd;
@JsonIgnore
private CardReaderSdk cardReaderSdk;
public UserInfoVO(CardReaderSdk cardReaderSdk) {
if (cardReaderSdk == null) {
throw new RRException("sdk初始化失败");
}
this.cardReaderSdk = cardReaderSdk;
this.name = this.convert2Str(this.cardReaderSdk::GetName, "姓名转换失败");
this.sex = this.convert2Str(this.cardReaderSdk::GetSex, "性别转换失败");
this.nation = this.convert2Str(this.cardReaderSdk::GetNation, "民族转换失败");
this.born = this.convert2Str(this.cardReaderSdk::GetBirth, "出生日期转换失败");
this.address = this.convert2Str(this.cardReaderSdk::GetAddress, "住址转换失败");
this.IDCardNo = this.convert2Str(this.cardReaderSdk::GetCertNo, "身份证转换失败");
this.grantDept = this.convert2Str(this.cardReaderSdk::GetDepartemt, "签发机关转换失败");
this.userLifeBegin = this.convert2Str(this.cardReaderSdk::GetEffectDate, "有效期开始日期转换失败");
this.userLifeEnd = this.convert2Str(this.cardReaderSdk::GetExpireDate, "有效期截至日期转换失败");
}
private String convert2Str(Consumer<byte[]> function, String tip) {
byte[] param = new byte[256];
function.accept(param);
try {
return new String(param, "GBK").trim();
} catch (Exception e) {
throw new RRException(tip == null ? "参数转换失败" : tip);
}
}
private LocalDateTime str2LocalDateTime(String str) {
// 定义日期格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
// 将字符串解析为 LocalDate
LocalDate localDate = LocalDate.parse(str, formatter);
// 将 LocalDate 转换为 LocalDateTime设置时间为 00:00:00
return localDate.atStartOfDay();
}
}

View File

@@ -29,7 +29,104 @@ public class PrinterUtil {
}*/ }*/
try { try {
return Native.load("Msprintsdk.x64", MsPrintSdk.class); // return Native.load("Msprintsdk.x64", MsPrintSdk.class);
return new MsPrintSdk() {
@Override
public Integer SetPrintPort(String strPort, int baudRate) {
return null;
}
@Override
public Integer SetPrintConn(int iConnWay, String strName, String strValue) {
return null;
}
@Override
public Integer SetUsbportauto() {
return null;
}
@Override
public Integer GetStatus() {
return null;
}
@Override
public Integer GetStatusspecial() {
return null;
}
@Override
public Integer SetInit() {
return null;
}
@Override
public void PrintQrcode(String s, int i, int i1, int i2) {
}
@Override
public void SetLeftmargin(int i) {
}
@Override
public Integer PrintString(String s, int i) {
return null;
}
@Override
public Integer PrintRemainQR() {
return null;
}
@Override
public Integer SetReadZKmode(int i) {
return null;
}
@Override
public Integer PrintCutpaper(int i) {
return null;
}
@Override
public void SetHTseat(String s, int i) {
}
@Override
public void PrintNextHT() {
}
@Override
public Integer PrintDiskbmpfile(String strPath) {
return null;
}
@Override
public Integer PrintDiskimgfile(String strPath) {
return null;
}
@Override
public Integer PrintMarkpositionPrint() {
return null;
}
@Override
public Integer PrintMarkpositioncut() {
return null;
}
@Override
public Integer PrintMarkcutpaper(int iMode) {
return null;
}
};
} catch (UnsatisfiedLinkError e) { } catch (UnsatisfiedLinkError e) {
log.info("[printer][PrinterUtil.getPrintSDK] SDK注册失败 {}", e.getMessage()); log.info("[printer][PrinterUtil.getPrintSDK] SDK注册失败 {}", e.getMessage());
throw new PrinterRegistrationException("Failed to load MsPrintSdk library: ", e); throw new PrinterRegistrationException("Failed to load MsPrintSdk library: ", e);

View File

@@ -11,3 +11,7 @@ dpkj:
# 波特率 串口连接下使用 # 波特率 串口连接下使用
baud-rate: baud-rate:
# 身份证读取等待时间
IDCardReader:
waitingTime: 30000

View File

@@ -11,3 +11,7 @@ dpkj:
# 波特率 串口连接下使用 # 波特率 串口连接下使用
baud-rate: baud-rate:
# 身份证读取等待时间
IDCardReader:
waitingTime: 30000

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>挂号单</title>
</head>
<body>
<div style="font-size: 24px;">
<div style="">
<div style="text-align: center; font-size: 36px; font-weight: 700;"><span th:text="${hospitalName}"></span></div>
<div style="text-align: center; font-weight: 700; font-size: 26px;">****<span th:text="${registeTerminalName}"></span></span>****</div>
<div style="text-align: center; font-weight: 700; margin-bottom: -5px;">-----------------------------------------------------------------------------</div>
<div style="text-align: center; font-size: 36px; font-weight: 700;"><span th:text="${registeType}"></span></div>
<div style="text-align: center; font-weight: 700; margin-top: -7px;">-----------------------------------------------------------------------------</div>
</div>
<div style="font-weight: 700;">
<div style="margin-left: 59px;">&emsp;&emsp;名:<span th:text="${name}"></span></div>
<div style="margin-left: 59px;">&emsp;&emsp;别:<span th:text="${gender}"></span></div>
<div style="margin-left: 59px;">&emsp;&emsp;龄:<span th:text="${age}"></span></div>
<div style="margin-left: 59px;">出生日期:<span th:text="${birthDate}"></span></div>
<div style="margin-left: 59px;">&emsp;&emsp;号:<span th:text="${cardNumber}"></span></div>
<div style="margin-left: 59px;">&ensp;&ensp;号:<span th:text="${outpatientNumber}"></span></div>
<div style="margin-left: 59px;">就诊科室:<span th:text="${department}"></span></div>
<div style="margin-left: 59px;">出诊级别:<span th:text="${visitLevel}"></span></div>
<div style="margin-left: 59px;">就诊医生:<span th:text="${doctor}"></span></div>
<div style="margin-left: 59px;">&emsp;&emsp;序:<span th:text="${sequence}"></span></div>
<div style="margin-left: 59px;">挂号日期:<span th:text="${registerDate}"></span></div>
<div style="margin-left: 59px; font-size: 31px; ">总费用:<span th:text="${totalFee}"></span>&thinsp;</div>
<div style="margin-left: 59px;">支付方式:<span th:text="${paymentMethod}"></span></div>
<div style="margin-left: 59px;">&ensp;&ensp;号:<span th:text="${orderNumber}"></span></div>
<div style="margin-left: 59px;">交易流水:<span th:text="${transactionNumber}"></span></div>
<img style="margin-left: 35px; margin-top: -8px; margin-bottom: -8px;" th:src="${qrCodeBase64}" alt="QR Code"/>
</div>
<div>
<div style="margin-top: -20px; text-align: center; font-weight: 700;">-----------------------------------------------------------------------------</div>
<div style="margin-left: 59px; font-weight: 700;">备注(REFERENCE):凭此条退费,请妥善保管!</div>
<div style="margin-left: 59px;">&ensp;&ensp;号:<span th:text="${terminalNumber}"></span></div>
<div style="margin-left: 59px;">打印时间:<span th:text="${printTime}"></span></div>
</div>
</div>
</body>
</html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,13 +0,0 @@
package com.dpkj.ems;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class EmsExpressBridgeApplicationTests {
@Test
void contextLoads() {
}
}

View File

@@ -1,79 +0,0 @@
package com.dpkj.ems.utils;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
import com.dpkj.Application;
import com.dpkj.common.config.FileConfig;
import com.dpkj.modules.express.service.PrinterService;
import com.dpkj.modules.express.utils.FileUtils;
import com.dpkj.modules.express.utils.PrinterUtil;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
class PrinterUtilTest {
@Autowired
private PrinterService printerService;
@Autowired
private FileConfig fileConfig;
@Test
void getPrintSDK() throws PrinterUtil.PrinterRegistrationException {
PrinterUtil.MsPrintSdk sdk = PrinterUtil.getPrintSDK();
int i = sdk.SetUsbportauto();
Console.log(i);
int i1 = sdk.SetInit();
Console.log(i1);
int i2 = sdk.GetStatusspecial();
Console.log(i2);
}
@Test
void t2() throws IOException {
String base64Path = "G:\\Temp\\text\\base64.txt";
String base64 = FileUtil.readString(FileUtil.file(base64Path), StandardCharsets.UTF_8);
String bpmPath = "G:\\Temp\\img";
List<BufferedImage> bufferedImages = FileUtils.convertBase64PdfToBufferedImage(base64);
FileUtils.saveBufferedImageToBmp(bufferedImages);
}
@Test
void t3() throws Exception {
String base64Path = "G:\\Temp\\text\\base64.txt";
String base64 = FileUtil.readString(FileUtil.file(base64Path), StandardCharsets.UTF_8);
// String bpmPath = "G:\\Temp\\img";
String bpmPath = fileConfig.getPath();
List<BufferedImage> bufferedImages = FileUtils.convertBase64PdfToBufferedImage(base64);
List<File> files = FileUtils.saveBufferedImageToBmp(bufferedImages);
/*for (File file : files) {
String filePath = file.getAbsolutePath();
printerService.printBmpByPath(filePath);
}*/
}
@Test
void t4() throws IOException {
String base64Path = "G:\\Temp\\text\\base64.txt";
String base64 = FileUtil.readString(FileUtil.file(base64Path), StandardCharsets.UTF_8);
List<BufferedImage> bufferedImages = FileUtils.convertBase64PdfToBufferedImage(base64);
System.out.println(bufferedImages);
}
}

View File

@@ -1,22 +0,0 @@
package com.dpkj.ems.utils;
import cn.hutool.core.io.FileUtil;
import com.dpkj.modules.express.utils.FileUtils;
import org.junit.jupiter.api.Test;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
class PrinterUtilTest2 {
@Test
void t4() throws IOException {
String base64Path = "G:\\Temp\\text\\base64.txt";
String base64 = FileUtil.readString(FileUtil.file(base64Path), StandardCharsets.UTF_8);
List<BufferedImage> bufferedImages = FileUtils.convertBase64PdfToBufferedImage(base64);
System.out.println(bufferedImages);
}
}