Compare commits
67 Commits
master
...
6a5056bbc8
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a5056bbc8 | |||
| 8da947a401 | |||
| b759041f96 | |||
| 72288c08a4 | |||
| c937c4823f | |||
| 99bc6bfc55 | |||
| 8adb80409e | |||
| 70c2b487b3 | |||
| 5bc087860f | |||
| 2238acad76 | |||
| 7ad9124816 | |||
| 651b92e296 | |||
| eb983fef76 | |||
| 148321f002 | |||
| ab35bef704 | |||
| 9e13e1eaf0 | |||
| 4fbc844ddd | |||
| 0b9d16befc | |||
| d931ad2bdf | |||
| b2e6b71c2b | |||
| 9d3d5b2645 | |||
| ca37066696 | |||
| 0fe3d4b789 | |||
| 3b84448e47 | |||
| 46097c0825 | |||
| d3d908a407 | |||
| ba18bf9558 | |||
| 438d52da0c | |||
| 24820af27a | |||
| 0e9a6b6cf0 | |||
| e31bb24b4a | |||
| d2e40391c6 | |||
| 274bbc1167 | |||
| 62e47e55be | |||
| f36e6703a3 | |||
| 27d8b818c0 | |||
| c0b1e80525 | |||
| 47c9d8dc5a | |||
| 62c9c7fa7d | |||
| 3b0051cb37 | |||
| 6ca1cdce4b | |||
| 12ae2323f7 | |||
| d9da4ea307 | |||
| 2986fa0c14 | |||
| a8f13d72b9 | |||
| 44a175aa66 | |||
| 9073fd9180 | |||
| 1474746864 | |||
| eecc053eae | |||
| 07d160a48d | |||
| aa46f4c66f | |||
| 8fb13e026b | |||
| 7b0a2a98cd | |||
| b0bd972e66 | |||
| 5e66fb8c3f | |||
| 02129e4db6 | |||
| 4291244a1d | |||
| 26c2a7ba40 | |||
| e2111717bc | |||
| d4b66aa77a | |||
| 6754910edd | |||
| 68e2fbfc8c | |||
| 7d971f4d13 | |||
| 25af267b39 | |||
| 96d447fc0d | |||
| 1fa6933c18 | |||
| cfb6690df5 |
6
doc/lib/command.bat
Normal file
6
doc/lib/command.bat
Normal file
@@ -0,0 +1,6 @@
|
||||
echo off
|
||||
|
||||
:: 添加jar包到本地仓库
|
||||
cmd /k "mvn install:install-file -Dfile=jacob-1.21.jar -DgroupId=com.jacob -DartifactId=jacob -Dversion=1.21 -Dpackaging=jar"
|
||||
|
||||
pause
|
||||
BIN
doc/lib/jacob-1.21-x64.dll
Normal file
BIN
doc/lib/jacob-1.21-x64.dll
Normal file
Binary file not shown.
BIN
doc/lib/jacob-1.21-x86.dll
Normal file
BIN
doc/lib/jacob-1.21-x86.dll
Normal file
Binary file not shown.
BIN
doc/lib/jacob-1.21.jar
Normal file
BIN
doc/lib/jacob-1.21.jar
Normal file
Binary file not shown.
@@ -1,14 +1,14 @@
|
||||
# 安装服务
|
||||
ems-express-call-dll.exe install
|
||||
yinyitong-dll-hang.exe install
|
||||
|
||||
# 启动服务
|
||||
ems-express-call-dll.exe start
|
||||
yinyitong-dll-hang.exe start
|
||||
|
||||
# 查看服务运行状态
|
||||
ems-express-call-dll.exe status
|
||||
yinyitong-dll-hang.exe status
|
||||
|
||||
# 重启服务
|
||||
ems-express-call-dll.exe restart
|
||||
yinyitong-dll-hang.exe restart
|
||||
|
||||
# 停止服务
|
||||
ems-express-call-dll.exe stop
|
||||
yinyitong-dll-hang.exe stop
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<service>
|
||||
<id>dpkj-express</id>
|
||||
<name>dpkj-express</name>
|
||||
<description>快递寄递DLL调用服务</description>
|
||||
|
||||
<executable>java</executable>
|
||||
<arguments>-jar %BASE%\ems-express-call-dll.jar</arguments>
|
||||
</service>
|
||||
20
doc/win/yinyitong-dll-hang.bat
Normal file
20
doc/win/yinyitong-dll-hang.bat
Normal file
@@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
|
||||
:: ҽ<><D2BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ
|
||||
set CHSPATH=D:/Project/CHS
|
||||
:: jar<61><72><EFBFBD><EFBFBD>
|
||||
set NAME=yinyitong-dll-hang
|
||||
:: <20>˿ں<CBBF>
|
||||
set PROT=5946
|
||||
|
||||
echo <20>ر<EFBFBD>%PROT%<EFBFBD>˿ڽ<EFBFBD><EFBFBD><EFBFBD>
|
||||
for /f "tokens=1-5" %%i in ('netstat -ano^|findstr ":%PROT%"') do taskkill /pid %%m -t -f
|
||||
|
||||
echo <20><><EFBFBD><EFBFBD>%NAME%
|
||||
|
||||
start javaw -jar %~dp0\%NAME%.jar --server.port=%PROT% -Dfile.encoding=UTF-8 -Djava.library.path=%CHSPATH%
|
||||
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
::exit
|
||||
pause
|
||||
25
doc/win/yinyitong-dll-hang.xml
Normal file
25
doc/win/yinyitong-dll-hang.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<service>
|
||||
<!-- 环境变量 文件名称 -->
|
||||
<env name="NAME" value="yinyitong-dll-hang"/>
|
||||
|
||||
<!-- 服务ID -->
|
||||
<id>dpkj-%NAME%</id>
|
||||
<!-- 服务名称 -->
|
||||
<name>%BASE%</name>
|
||||
<!-- 服务描述 -->
|
||||
<description>银医壁挂机中间服务调用程序</description>
|
||||
|
||||
<!-- 启动命令 -->
|
||||
<executable>java</executable>
|
||||
<!-- 启动参数 -->
|
||||
<arguments>-jar %BASE%\%NAME%.jar</arguments>
|
||||
|
||||
<!-- 日志-->
|
||||
<logpath>%BASE%\logs\winws</logpath>
|
||||
<!-- 按大小和时间滚动模式 -->
|
||||
<log mode="roll-by-size-time">
|
||||
<sizeThreshold>10240</sizeThreshold>
|
||||
<pattern>yyyyMMdd</pattern>
|
||||
<autoRollAtTime>00:00:00</autoRollAtTime>
|
||||
</log>
|
||||
</service>
|
||||
Binary file not shown.
124
pom.xml
124
pom.xml
@@ -4,10 +4,10 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.dpkj</groupId>
|
||||
<artifactId>ems-express-call-dll</artifactId>
|
||||
<artifactId>yinyitong-dll-hang</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>ems-express-call-dll</name>
|
||||
<description>ems-express-call-dll</description>
|
||||
<name>yinyitong-dll-hang</name>
|
||||
<description>银医通-澜沧中医院-DLL-壁挂机</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -18,12 +18,19 @@
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<hutool.version>5.8.25</hutool.version>
|
||||
<jna.version>5.14.0</jna.version>
|
||||
<hutool.version>5.8.36</hutool.version>
|
||||
<pdfbox.version>3.0.2</pdfbox.version>
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<jna.version>5.17.0</jna.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@@ -47,37 +54,136 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 调用DLL -->
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<artifactId>jna-jpms</artifactId>
|
||||
<version>${jna.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna-platform</artifactId>
|
||||
<artifactId>jna-platform-jpms</artifactId>
|
||||
<version>${jna.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 调用DLL - COM库 -->
|
||||
<dependency>
|
||||
<groupId>com.jacob</groupId>
|
||||
<artifactId>jacob</artifactId>
|
||||
<version>1.21</version>
|
||||
</dependency>
|
||||
|
||||
<!--PDF转换工具-->
|
||||
<dependency>
|
||||
<groupId>org.apache.pdfbox</groupId>
|
||||
<artifactId>pdfbox</artifactId>
|
||||
<version>${pdfbox.version}</version>
|
||||
</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>
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>javase</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>
|
||||
|
||||
<!-- 数据校验-->
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>2.0.1.Final</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>ems-express-call-dll</finalName>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 只保留一个 spring-boot-maven-plugin -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<!-- 跳过单元测试 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- 拷贝依赖到 lib 目录 -->
|
||||
<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>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>false</overWriteSnapshots>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Javadoc 插件 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<!-- 打包时将 resource 下的文件一起打包 -->
|
||||
<resources>
|
||||
<resource>
|
||||
|
||||
37
src/main/java/com/dpkj/common/config/MvcConfig.java
Normal file
37
src/main/java/com/dpkj/common/config/MvcConfig.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.dpkj.common.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@@ -29,4 +28,15 @@ public class PrinterConfig {
|
||||
* 波特率 串口连接下使用
|
||||
*/
|
||||
private Integer baudRate;
|
||||
|
||||
/**
|
||||
* 终端号
|
||||
*/
|
||||
private String terminalNumber;
|
||||
|
||||
/**
|
||||
* 时间格式
|
||||
*/
|
||||
private String timeType;
|
||||
|
||||
}
|
||||
|
||||
139
src/main/java/com/dpkj/common/exception/ControllerAdvice.java
Normal file
139
src/main/java/com/dpkj/common/exception/ControllerAdvice.java
Normal 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("缺少请求参数");
|
||||
}
|
||||
|
||||
}
|
||||
70
src/main/java/com/dpkj/common/exception/ErrorEnum.java
Normal file
70
src/main/java/com/dpkj/common/exception/ErrorEnum.java
Normal 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;
|
||||
}
|
||||
}
|
||||
23
src/main/java/com/dpkj/common/exception/ErrorInterface.java
Normal file
23
src/main/java/com/dpkj/common/exception/ErrorInterface.java
Normal 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();
|
||||
|
||||
}
|
||||
59
src/main/java/com/dpkj/common/exception/RRException.java
Normal file
59
src/main/java/com/dpkj/common/exception/RRException.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.dpkj.common.vo;
|
||||
|
||||
import com.dpkj.common.constant.CommonConst;
|
||||
import com.dpkj.common.exception.ErrorEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -27,6 +28,11 @@ public class Result<T> implements Serializable {
|
||||
*/
|
||||
private Integer code = 0;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private int state;
|
||||
|
||||
/**
|
||||
* 返回数据对象 data
|
||||
*/
|
||||
@@ -41,41 +47,61 @@ public class Result<T> implements Serializable {
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok() {
|
||||
return ok("", null);
|
||||
return ok("", null, 0);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(String msg) {
|
||||
return ok(msg, null);
|
||||
return ok(msg, null, 0);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(T data) {
|
||||
return error("", data);
|
||||
return ok("", data, 0);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(String msg, T data) {
|
||||
return ok(msg, data, 0);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(String msg, T data, int state) {
|
||||
Result<T> r = new Result<T>();
|
||||
r.setSuccess(true);
|
||||
r.setCode(CommonConst.SC_200);
|
||||
r.setMessage(msg);
|
||||
r.setResult(data);
|
||||
r.setState(state);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(String msg, T data) {
|
||||
return error(CommonConst.SC_500, msg, data);
|
||||
return error(CommonConst.SC_500, msg, data, 0);
|
||||
}
|
||||
public static <T> Result<T> error(String msg, int state) {
|
||||
return error(CommonConst.SC_500, msg, null, state);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(String msg) {
|
||||
return error(CommonConst.SC_500, msg, null);
|
||||
return error(CommonConst.SC_500, msg, null, 0);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(ErrorEnum errorEnum) {
|
||||
return error(errorEnum.getCode(), errorEnum.getMessage(), null, 0);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(int code, String message) {
|
||||
return error(code, message, null, 0);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(int code, String msg, T data) {
|
||||
return error(code, msg, data, 0);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(int code, String msg, T data, int state) {
|
||||
Result<T> r = new Result<T>();
|
||||
r.setCode(code);
|
||||
r.setMessage(msg);
|
||||
r.setSuccess(false);
|
||||
r.setResult(data);
|
||||
r.setState(state);
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
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.dpkj.modules.autoReplyPrint.utils.TestFunction;
|
||||
import com.dpkj.modules.autoReplyPrint.vo.PrinterStatus;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.ptr.LongByReference;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 图片打印基础实现
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-17 14:37:23
|
||||
*/
|
||||
@Slf4j
|
||||
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 {
|
||||
System.out.println(devName + pszFile);
|
||||
// 开始打印图片
|
||||
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){
|
||||
log.error("{}, {}", this, e.getMessage(), e);
|
||||
if ( e instanceof RRException) {
|
||||
throw new RRException(((RRException) e).getCode(), e.getMessage());
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
public PrinterStatus getStatus(String devName){
|
||||
Pointer handle = getHandle(devName);
|
||||
try {
|
||||
// 获取状态前对打印机的错误进行清除、同时清除缓存
|
||||
boolean cleanTag = AutoReplyPrint.INSTANCE.CP_Printer_ClearPrinterError(handle);
|
||||
boolean cacheTag = AutoReplyPrint.INSTANCE.CP_Printer_ClearPrinterBuffer(handle);
|
||||
if ( !cleanTag){
|
||||
throw new RRException(501, "打印机错误清除失败");
|
||||
}
|
||||
|
||||
// 获取打印机状态,因为目前暂时没有办法获取更多的状态,直接获取打印机的状态,如果打印机的状态时非正常的,那么直接抛出打印机错误
|
||||
byte[] statusResult = new byte[100];
|
||||
LongByReference printer_error_status = new LongByReference();
|
||||
LongByReference printer_info_status = new LongByReference();
|
||||
LongByReference timestamp_ms_printer_status = new LongByReference();
|
||||
boolean statusTag = AutoReplyPrint.INSTANCE.CP_Printer_GetPrinterStatusInfo(handle,printer_error_status, printer_info_status, timestamp_ms_printer_status);
|
||||
if ( !statusTag){
|
||||
throw new RRException(501, "打印机发生错误");
|
||||
}
|
||||
|
||||
AutoReplyPrint.CP_PrinterStatus status = new AutoReplyPrint.CP_PrinterStatus(
|
||||
printer_error_status.getValue(), printer_info_status.getValue());
|
||||
String errorMessage = "";
|
||||
if (status.ERROR_OCCURED()) {
|
||||
if (status.ERROR_CUTTER())
|
||||
throw new RRException(500, "ERROR_CUTTER");
|
||||
if (status.ERROR_FLASH())
|
||||
throw new RRException(500, "ERROR_FLASH");
|
||||
if (status.ERROR_NOPAPER())
|
||||
throw new RRException(500, "ERROR_NOPAPER");
|
||||
if (status.ERROR_VOLTAGE())
|
||||
throw new RRException(500, "ERROR_VOLTAGE");
|
||||
if (status.ERROR_MARKER())
|
||||
throw new RRException(500, "ERROR_MARKER");
|
||||
if (status.ERROR_ENGINE())
|
||||
throw new RRException(500, "ERROR_ENGINE");
|
||||
if (status.ERROR_OVERHEAT())
|
||||
throw new RRException(500, "ERROR_OVERHEAT");
|
||||
if (status.ERROR_COVERUP())
|
||||
throw new RRException(500, "ERROR_COVERUP");
|
||||
if (status.ERROR_MOTOR())
|
||||
throw new RRException(500, "ERROR_MOTOR");
|
||||
}
|
||||
|
||||
Date dt_printer_status = new Date(timestamp_ms_printer_status.getValue());
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String str_printer_error_status = simpleDateFormat.format(dt_printer_status) + String.format(" Printer Error Status: 0x%04X\r\n", printer_error_status.getValue() & 0xffff);
|
||||
String str_printer_info_status = simpleDateFormat.format(dt_printer_status) + String.format(" Printer Info Status: 0x%04X\r\n", printer_info_status.getValue() & 0xffff);
|
||||
|
||||
return new PrinterStatus();
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
if ( e instanceof RRException){
|
||||
throw new RRException(((RRException) e).getCode(), e.getMessage());
|
||||
}
|
||||
throw new RRException(e);
|
||||
}finally {
|
||||
if ( handle != null) AutoReplyPrint.INSTANCE.CP_Port_Close(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* 壁挂打印机
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("autoReplyPrint")
|
||||
public class AutoReplyPrintController {
|
||||
@Autowired
|
||||
private AutoReplyPrintService autoReplyPrintService;
|
||||
|
||||
/**
|
||||
* 获取打印机固件版本
|
||||
*/
|
||||
@PostMapping("CPPrinterGetPrinterFirmwareVersion")
|
||||
public Result<?> CPPrinterGetPrinterFirmwareVersion(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String USBName) {
|
||||
return autoReplyPrintService.CPPrinterGetPrinterFirmwareVersion(USBName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试打印
|
||||
*/
|
||||
@PostMapping("/testWrite")
|
||||
private Result<?> testWrite(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String USBName){
|
||||
return this.autoReplyPrintService.testWrite(USBName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
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.PostMapping;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 图片打印,通过路径
|
||||
*/
|
||||
@PostMapping("/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[]
|
||||
*/
|
||||
@PostMapping("/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文件
|
||||
*/
|
||||
@PostMapping("/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[]
|
||||
*/
|
||||
@PostMapping("/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("图片打印成功");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.dpkj.modules.autoReplyPrint.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dpkj.common.config.PrinterConfig;
|
||||
import com.dpkj.common.exception.RRException;
|
||||
import com.dpkj.common.vo.Result;
|
||||
import com.dpkj.modules.autoReplyPrint.enums.ReceiptTemplateEnum;
|
||||
import com.dpkj.modules.autoReplyPrint.request.ReceiptPrintRequest;
|
||||
import com.dpkj.modules.autoReplyPrint.service.ImagePrintService;
|
||||
import com.dpkj.modules.autoReplyPrint.service.impl.TemplateService;
|
||||
import com.dpkj.modules.autoReplyPrint.utils.FolderUtils;
|
||||
import com.dpkj.modules.autoReplyPrint.vo.PrinterStatus;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thymeleaf.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 小票打印机控制层
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-03-10 9:29:22
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/receipt")
|
||||
public class ReceiptPrintController {
|
||||
|
||||
@Resource
|
||||
private PrinterConfig printerConfig;
|
||||
|
||||
private static final int MAX_HEIGHT = 1000;
|
||||
private static final int FIXED_WIDTH = 730;
|
||||
|
||||
@Resource(name = "USBImagePrint")
|
||||
private ImagePrintService usbImagePrintService;
|
||||
|
||||
@Resource
|
||||
private TemplateService templateService;
|
||||
|
||||
@PostMapping("/print")
|
||||
private Result<Void> print(@Validated @RequestBody ReceiptPrintRequest request) throws UnsupportedEncodingException {
|
||||
FolderUtils.checkFolderAndCreate(request.getFileDir());
|
||||
|
||||
String devName = "VID:0x0FE6,PID:0x811E"; // 采用默认的devName,不进行入参传值了
|
||||
String templateName = ReceiptTemplateEnum.getTemplateName(request.getTemplateName());
|
||||
|
||||
// 宽度固定
|
||||
request.setWidth(request.getWidth() > 610 ? 600 : request.getWidth());
|
||||
|
||||
JSONObject data = JSONObject.parseObject(URLDecoder.decode(request.getTemplateFillData(), "UTF-8"));
|
||||
|
||||
// 强行设置终端号 不需要固定,前端自己穿
|
||||
// data.put("terminalNumber", printerConfig.getTerminalNumber());
|
||||
|
||||
// 强行设置打印时间
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(printerConfig.getTimeType());
|
||||
String formattedDate = sdf.format(new Date());
|
||||
data.put("printTime", formattedDate);
|
||||
|
||||
Integer height = request.getHeight();
|
||||
|
||||
int dinyHeight = 0;
|
||||
if (templateName.equals("department")) {
|
||||
// 由于是使用的门诊小票-T2,那么默认的高度为1100,强行设置,通过动态修改渲染的图片的高度
|
||||
height = 800;//根据实际情况800较为合适
|
||||
|
||||
// 单行最大长度为10
|
||||
int singleLineMaxLength = 10;
|
||||
// 这里的长度取自于department.html模板中的项目单个tr高度,并且略高于该高度
|
||||
int singleLineHeight = 30;
|
||||
// 动态计算长度
|
||||
JSONArray items = data.getJSONArray("items");
|
||||
for (Object item : items) {
|
||||
JSONObject itemEntity = (JSONObject) item;
|
||||
String projectName = String.valueOf(itemEntity.get("name"));
|
||||
int length = projectName.length();
|
||||
int count = (int) Math.ceil((double) length / singleLineMaxLength);
|
||||
dinyHeight += count * singleLineHeight;
|
||||
}
|
||||
|
||||
// 计算是否有门诊号
|
||||
String outpatientNumber = data.getString("outpatientNumber");
|
||||
if (!StringUtils.isEmpty(outpatientNumber)) {
|
||||
dinyHeight += 28;
|
||||
}
|
||||
|
||||
// 计算是否有就诊医生
|
||||
String doctor = data.getString("doctor");
|
||||
if (!StringUtils.isEmpty(doctor)) {
|
||||
dinyHeight += 28;
|
||||
}
|
||||
|
||||
// 计算是否有就诊科室
|
||||
String department = data.getString("department");
|
||||
if (!StringUtils.isEmpty(department)) {
|
||||
dinyHeight += 28;
|
||||
}
|
||||
|
||||
}
|
||||
height += dinyHeight;
|
||||
|
||||
// 进行模板填充
|
||||
// String testData = "{\"hospitalName\":\"澜沧县中医医院\",\"registeTerminalName\":\"中国农业银行自助终端\",\"registeType\":\"自助挂号\",\"name\":\"刘博雅\",\"gender\":\"男\",\"age\":28,\"birthDate\":\"1996-06-31\",\"cardNumber\":\"6221**********0731\",\"outpatientNumber\":\"2501150038\",\"department\":\"普外科门诊\",\"visitLevel\":\"普通号\",\"doctor\":\"普通门诊\",\"sequence\":\"1\",\"registerDate\":\"2025-01-15\",\"totalFee\":4.00,\"paymentMethod\":\"微信扫码支付\",\"orderNumber\":\"\",\"transactionNumber\":\"2025011513090412092794szztzzj\",\"qrCodeBase64_2base64Type_1_250_250\":\"maby this is a Base64 code data if has\",\"terminalNumber\":\"12092794\",\"printTime\":\"2025-01-15 13:10:08\"}";
|
||||
StringBuilder filePath = new StringBuilder(request.getFileDir());
|
||||
// 校验是否选中了模板,如果没选中模板的话则不需要另外生成了
|
||||
if (!StringUtils.isEmpty(request.getTemplateName()) && !StringUtils.isEmpty(request.getFileDir())) {
|
||||
byte[] image = templateService.generateReceiptImage(
|
||||
data,
|
||||
templateName,
|
||||
request.getWidth(),
|
||||
height,
|
||||
filePath);
|
||||
} else {
|
||||
throw new RRException("模板渲染错误");
|
||||
}
|
||||
usbImagePrintService.imagePrintFromPath(devName,
|
||||
request.getWidth(),
|
||||
height,
|
||||
filePath.toString(),
|
||||
1,
|
||||
0);
|
||||
|
||||
// 删除图片
|
||||
File file = new File(filePath.toString());
|
||||
// 检查文件是否存在
|
||||
if (file.exists()) {
|
||||
// 尝试删除文件
|
||||
if (file.delete()) {
|
||||
log.info("文件删除成功: " + filePath);
|
||||
} else {
|
||||
log.info("文件删除失败: " + filePath);
|
||||
}
|
||||
} else {
|
||||
log.info("文件不存在: " + filePath);
|
||||
}
|
||||
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/getStatus")
|
||||
public Result<PrinterStatus> print() {
|
||||
String devName = "VID:0x0FE6,PID:0x811E"; // 采用默认的devName,不进行入参传值了
|
||||
return Result.ok(this.usbImagePrintService.getStatus(devName));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
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;
|
||||
|
||||
|
||||
@PostMapping("/template/{templateName}")
|
||||
public Result<String> testTemplate(@RequestParam String jsonData,
|
||||
@RequestParam Integer width,
|
||||
@RequestParam Integer height,
|
||||
@PathVariable String templateName,
|
||||
@RequestParam(defaultValue = "E:\\images") String saveDir) {
|
||||
StringBuilder filePath = new StringBuilder();
|
||||
this.templateService.generateReceiptImage(JSONObject.parseObject(jsonData), templateName, width, height, filePath);
|
||||
return Result.ok("模板生成成功");
|
||||
}
|
||||
|
||||
@PostMapping("/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>";
|
||||
StringBuilder filePath = new StringBuilder();
|
||||
this.templateService.generateReceiptImage(JSONObject.parseObject(jsonData), html, width, height, filePath);
|
||||
return Result.ok("模板生成成功");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.dpkj.modules.autoReplyPrint.enums;
|
||||
|
||||
|
||||
import com.dpkj.common.exception.RRException;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum ReceiptTemplateEnum {
|
||||
|
||||
/**
|
||||
* 自助挂号模板
|
||||
*/
|
||||
REGISTER("1", "register", "自助挂号的"),
|
||||
|
||||
/**
|
||||
* 住院缴费的模板
|
||||
*/
|
||||
HOSPITAL_PAYMENT("2", "hospitalPayment", "住院缴费的小票"),
|
||||
|
||||
/**
|
||||
* 门诊缴费的模板
|
||||
*/
|
||||
OUTPATIENT_T1("3", "outpatientPayment", "门诊缴费的小票-T1"),
|
||||
|
||||
/**
|
||||
* 门诊缴费的模板
|
||||
*/
|
||||
OUTPATIENT_T2("4", "department", "门诊缴费的小票-T2");
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String templateName;
|
||||
|
||||
private final String desc;
|
||||
|
||||
|
||||
ReceiptTemplateEnum(String code, String templateName, String desc) {
|
||||
this.code = code;
|
||||
this.templateName = templateName;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过code获取模板名称
|
||||
*
|
||||
* @param code code/也有可能直接是一个名称
|
||||
* @return 模板的名称
|
||||
*/
|
||||
public static String getTemplateName(String code) {
|
||||
if (code == null || "".equals(code)) {
|
||||
throw new RRException("模板名称不能为空");
|
||||
}
|
||||
|
||||
String name = null;
|
||||
for (ReceiptTemplateEnum enumEntity : ReceiptTemplateEnum.values()) {
|
||||
String enumCode = enumEntity.getCode();
|
||||
if (enumCode.equals(code)) {
|
||||
name = enumEntity.getTemplateName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return name == null ? code : name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.dpkj.modules.autoReplyPrint.request;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 小票打印请求,壁挂和打印机接口 进行统一
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-03-10 9:25:34
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ReceiptPrintRequest implements Serializable {
|
||||
|
||||
/**
|
||||
* 文件路径地址,填写本地路径,默认为 D:/images
|
||||
*/
|
||||
@NotBlank(message = "文件路径不能为空")
|
||||
private String fileDir = "D:/images";
|
||||
|
||||
/**
|
||||
* 模板名称,也可以是自己写的html的字符串文件
|
||||
*/
|
||||
private String templateName = "receipt";
|
||||
|
||||
/**
|
||||
* 填充模板的数据,必填,也可以通过自己设置的模板名称进行设计,部分设计规则
|
||||
* <p>1、特殊字段(二维码图片)类型:qrCodeBase64_2base64Type_120_120,字段解析,qrCodeBase64为字段名称,
|
||||
* _2base64Type为将这个数据转换为图片类型的base64编码,第一个_120图片的宽度,第二个_120的为图片高度</p>
|
||||
* <p>2、需要对传入的JSON数据进行URI编码</p>
|
||||
* <p>3、{"hospitalName":"澜沧县中医医院","registeTerminalName":"中国农业银行自助终端","registeType":"自助挂号","name":"刘博雅","gender":"男","age":28,"birthDate":"1996-06-31","cardNumber":"6221**********0731","outpatientNumber":"2501150038","department":"普外科门诊","visitLevel":"普通号","doctor":"普通门诊","sequence":"1","registerDate":"2025-01-15","totalFee":4.00,"paymentMethod":"微信扫码支付","orderNumber":"","transactionNumber":"2025011513090412092794szztzzj","qrCodeBase64_2base64Type_120_120":"这里应是二维码的Base64编码数据(如果有)","terminalNumber":"12092794","printTime":"2025-01-15 13:10:08"}</p>
|
||||
*/
|
||||
@NotBlank(message = "模板填充数据不能为空")
|
||||
private String templateFillData;
|
||||
|
||||
/**
|
||||
* 小票渲染宽度,默认为:690
|
||||
*/
|
||||
private Integer width = 690;
|
||||
|
||||
/**
|
||||
* 生成的模板的高度,默认为:1050
|
||||
*/
|
||||
private Integer height = 1050;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.dpkj.modules.autoReplyPrint.service;
|
||||
|
||||
import com.dpkj.modules.autoReplyPrint.vo.PrinterStatus;
|
||||
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);
|
||||
|
||||
/**
|
||||
* 获取打印机状态
|
||||
*/
|
||||
PrinterStatus getStatus(String usbName);
|
||||
|
||||
|
||||
}
|
||||
@@ -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("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
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.dpkj.modules.autoReplyPrint.vo.PrinterStatus;
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrinterStatus getStatus(String usbName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
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.dpkj.modules.autoReplyPrint.vo.PrinterStatus;
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrinterStatus getStatus(String usbName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
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.client.j2se.MatrixToImageWriter;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.oned.Code128Writer;
|
||||
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.context.Context;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 模板服务类
|
||||
*
|
||||
* @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, StringBuilder 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.toString())) {
|
||||
String outputPath = saveDir.toString() + "/genera_image_" + System.currentTimeMillis() + ".png";
|
||||
ImageIO.write(image, "PNG", new File(outputPath));
|
||||
saveDir.delete(0, saveDir.length());
|
||||
saveDir.append(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();
|
||||
if (data != null) {
|
||||
Set<String> keys = data.keySet();
|
||||
for (String key : keys) {
|
||||
// 判单是否有图片生成,统一后面采用的是_2base64Type
|
||||
String[] split = key.split("_");
|
||||
if (split.length > 1 && split[1].equals("2base64Type")) {
|
||||
int type = split.length > 2 ? Integer.parseInt(split[2]) : 1;
|
||||
int width = split.length > 3 ? Integer.parseInt(split[3]) : 100;
|
||||
int height = split.length > 4 ? Integer.parseInt(split[4]) : 100;
|
||||
// 如果是图片类型,需要进行base64转换
|
||||
String base64 = this.generateQRCode(type, 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(int type, String content, int width, int height) {
|
||||
try {
|
||||
BufferedImage qrImage = null;
|
||||
BitMatrix bitMatrix = null;
|
||||
if (type == 1) {
|
||||
Map<EncodeHintType, Object> hints = new HashMap<>();
|
||||
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 设置字符编码为 UTF-8
|
||||
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // 设置纠错级别
|
||||
hints.put(EncodeHintType.MARGIN, 1); // // 添加边距控制,设置边距为1
|
||||
QRCodeWriter qrCodeWriter = new QRCodeWriter();
|
||||
bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
|
||||
|
||||
qrImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
} else if (type == 2) {
|
||||
Map<EncodeHintType, Object> hints = new HashMap<>();
|
||||
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
|
||||
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
|
||||
hints.put(EncodeHintType.MARGIN, 1); // // 添加边距控制,设置边距为1
|
||||
|
||||
Code128Writer barcodeWriter = new Code128Writer();
|
||||
bitMatrix = barcodeWriter.encode(content, BarcodeFormat.CODE_128, width, height, hints);
|
||||
|
||||
qrImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
|
||||
}
|
||||
|
||||
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("二维码/条形码生成失败", e);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
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.dpkj.modules.autoReplyPrint.vo.PrinterStatus;
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrinterStatus getStatus(String devName) {
|
||||
return super.getStatus(devName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
package com.dpkj.modules.autoReplyPrint.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 文件夹处理
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-03-31 17:28:34
|
||||
*/
|
||||
@Slf4j
|
||||
public class FolderUtils {
|
||||
|
||||
public static void checkFolderAndCreate(String folderPath){
|
||||
File fileOrFolder = new File(folderPath);
|
||||
String targetPath;
|
||||
if (fileOrFolder.isFile()) {
|
||||
// 如果是文件,获取文件所在目录路径
|
||||
targetPath = fileOrFolder.getParent();
|
||||
} else {
|
||||
// 如果不是文件(可能是不存在的文件夹或已存在的文件夹),使用原始路径
|
||||
targetPath = folderPath;
|
||||
}
|
||||
|
||||
File targetFolder = new File(targetPath);
|
||||
if (!targetFolder.exists()) {
|
||||
boolean success = targetFolder.mkdirs();
|
||||
if (success) {
|
||||
log.info("文件夹创建成功: " + targetPath);
|
||||
} else {
|
||||
log.error("文件夹创建失败: " + targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.dpkj.modules.autoReplyPrint.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PrinterStatus {
|
||||
/**
|
||||
* sp服务版本号
|
||||
*/
|
||||
private String SPVersion;
|
||||
|
||||
/**
|
||||
* 打印机状态
|
||||
* HEALTHY: 正常
|
||||
* FATAL: 故障
|
||||
* BUSY: 忙
|
||||
* NODEVICE: 无设备
|
||||
*/
|
||||
private String StDeviceStatus;
|
||||
|
||||
/**
|
||||
* 介质状态
|
||||
* PRESENT: 介质在通道
|
||||
* NOTPRESENT: 无介质
|
||||
* JAMMED: 卡介质
|
||||
* NOTSUPP: 不支持
|
||||
* UNKNOWN: 未知
|
||||
* EXITING: 介质在出口
|
||||
* RETRACTED 介质被回收(复位时)
|
||||
*/
|
||||
private String StMedia;
|
||||
|
||||
/**
|
||||
* 磁带状态
|
||||
* TONERFULL: 碳带满
|
||||
* TONERLOW: 碳带少
|
||||
* TONEROUT: 缺碳带
|
||||
* TONERNOTSUPP: 不支持碳带检测
|
||||
* TONERUNKNOWN: 碳带未知
|
||||
*/
|
||||
private String StToner;
|
||||
|
||||
/**
|
||||
* 墨盒状态
|
||||
* INKFULL: 墨盒满
|
||||
* INKLOW: 墨盒少
|
||||
* INKOUT: 缺墨盒
|
||||
* INKNOTSUPP: 不支持墨盒检测
|
||||
* INKUNKNOWN: 墨盒未知
|
||||
*/
|
||||
private String StInk;
|
||||
|
||||
/**
|
||||
* 纸盒扩展,取前面两个
|
||||
*/
|
||||
private String StPaperEx;
|
||||
|
||||
/**
|
||||
* 设备扩展,第一个是打印机状态,第二个是盖章机状态
|
||||
*/
|
||||
private String StDeviceExStatus;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* 多合一读卡器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("readCard")
|
||||
public class CardReaderController {
|
||||
@Autowired
|
||||
private CardReaderService cardReaderService;
|
||||
|
||||
/**
|
||||
* 连接设备并读取身份证信息
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("IDCardReader")
|
||||
public Result IDCardReader() {
|
||||
try {
|
||||
return cardReaderService.IDCardReader();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.info("设备连接失败 {}", e.getMessage());
|
||||
return Result.error("设备连接失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 连接设备并读取社保卡信息
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("SocialSecurityCardReader")
|
||||
public Result<?> SocialSecurityCardReader() {
|
||||
try {
|
||||
return cardReaderService.SocialSecurityCardReader();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.info("连接设备并读取社保卡信息失败 {}", e.getMessage());
|
||||
return Result.error("连接设备并读取社保卡信息失败");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.dpkj.modules.cardReader.service;
|
||||
|
||||
|
||||
import com.dpkj.common.vo.Result;
|
||||
|
||||
public interface CardReaderService {
|
||||
|
||||
Result IDCardReader();
|
||||
|
||||
Result SocialSecurityCardReader();
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
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.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;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class CardReaderServiceImpl implements CardReaderService {
|
||||
|
||||
@Value("${IDCardReader.waitingTime}")
|
||||
private Long waitingTime;
|
||||
private final Object cardReaderLock = new Object();
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
@Override
|
||||
public Result IDCardReader() {
|
||||
lock.lock();
|
||||
try {
|
||||
Long handle = null;
|
||||
UserInfoVO userInfo = null;
|
||||
try {
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][33]: 开始连接指定设备与电脑端口,即打开端口");
|
||||
// 连接指定设备与电脑端口,即打开端口
|
||||
handle = CardReaderSdk.INSTANCE.ICC_Reader_Open("USB1");
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][36]: 连接指定设备与电脑端口,即打开端口,返回句柄: {}", handle);
|
||||
if (handle == null) {
|
||||
log.error("[CardReaderServiceImpl][IDCardReader][38]: 打开端口失败");
|
||||
return Result.error("打开端口失败,请检查设备连接");
|
||||
}
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][42]: 开始循环读取 ID 卡信息,开始时间: {}", startTime);
|
||||
|
||||
byte[] bytes = new byte[1024];
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][45]: 尝试读取 ID 卡");
|
||||
while (true) {
|
||||
int idCard = CardReaderSdk.INSTANCE.PICC_Reader_ReadIDCard(handle, bytes);
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][48]: PICC_Reader_ReadIDCard读取 ID 卡,返回值: {}", idCard);
|
||||
|
||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||
if (elapsedTime >= waitingTime) {
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][52]: 读取 ID 卡信息超时,等待了 {} 秒。", waitingTime);
|
||||
return Result.error("读取 ID 卡信息超时,请重试。", -18);
|
||||
}
|
||||
if (idCard == 0) {
|
||||
try {
|
||||
userInfo = new UserInfoVO(CardReaderSdk.INSTANCE);
|
||||
if (userInfo.getName() != null && !userInfo.getName().isEmpty()) {
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][59]: 成功读取到用户信息,返回结果");
|
||||
return Result.ok(userInfo);
|
||||
} else {
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][62]: 读取到的用户信息异常,请尝试重新读取");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][65]: 创建 UserInfoVO 对象时发生异常", e);
|
||||
return Result.error("创建用户信息对象时发生异常,请重试。");
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (handle != null) {
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][72]: 开始关闭端口,句柄: {}", handle);
|
||||
// 不管什么情况最终都要关闭端口
|
||||
CardReaderSdk.INSTANCE.ICC_Reader_Close(handle);
|
||||
log.info("[CardReaderServiceImpl][IDCardReader][75]: 端口关闭成功");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result SocialSecurityCardReader() {
|
||||
lock.lock();
|
||||
try{
|
||||
Long handle = null;
|
||||
try {
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][89]: 开始连接指定设备与电脑端口,即打开端口");
|
||||
handle = CardReaderSdk.INSTANCE.ICC_Reader_Open("USB1");
|
||||
if (handle == null) {
|
||||
log.error("[CardReaderServiceImpl][SocialSecurityCardReader][92]: 打开端口失败");
|
||||
return Result.error("打开端口失败,请检查设备连接");
|
||||
}
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][95]: 连接指定设备与电脑端口,即打开端口,返回句柄: {}", handle);
|
||||
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][99]: 开始循环读取社保卡信息,开始时间: {}", startTime);
|
||||
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][101]: 获取设备卡读取设备状态");
|
||||
while (true) {
|
||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||
if (elapsedTime >= waitingTime) {
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][105]: 读取社保卡信息超时,等待了 {} 毫秒。", waitingTime);
|
||||
return Result.error("读取社保卡信息超时,请重试。", -18);
|
||||
}
|
||||
int l = CardReaderSdk.INSTANCE.ICC_Reader_GetStatus(handle, 0x01);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][109]: 获取社保卡读取设备状态:结果{}", l);
|
||||
if (l == 0) {
|
||||
byte[] data = new byte[4096];
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][112]: 尝试读取社保卡基本信息");
|
||||
int i = CardReaderSdk.INSTANCE.iReadCardBas(3, data);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][114]: 读取社保卡基本信息,返回值: {}", i);
|
||||
if (i == 0) {
|
||||
java.lang.String bas = new java.lang.String(data, "GBK");
|
||||
java.lang.String[] split = bas.split("\\|");
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][118]: 读基本信息,结果: {}", bas);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][119]: 开始创建 SocialSecurityCardInfoVO 对象");
|
||||
SocialSecurityCardInfoVO socialSecurityCardInfoVO = new SocialSecurityCardInfoVO();
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][121]: 开始截取区号代码");
|
||||
// 区号代码截取
|
||||
socialSecurityCardInfoVO.setAreaCode(split[0].substring(split[0].length() - 6));
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][124]: 区号代码截取完成,结果: {}", socialSecurityCardInfoVO.getAreaCode());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][125]: 开始设置社保卡号");
|
||||
socialSecurityCardInfoVO.setSocialSecurityNo(split[1]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][127]: 社保卡号设置完成,结果: {}", socialSecurityCardInfoVO.getSocialSecurityNo());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader]: 开始设置卡号");
|
||||
socialSecurityCardInfoVO.setCardNumber(split[2]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][130]: 卡号设置完成,结果: {}", socialSecurityCardInfoVO.getCardNumber());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][131]: 开始设置识别码");
|
||||
socialSecurityCardInfoVO.setIdentificationCode(split[3]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][133]: 识别码设置完成,结果: {}", socialSecurityCardInfoVO.getIdentificationCode());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][134]: 开始设置姓名");
|
||||
socialSecurityCardInfoVO.setName(split[4]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][136]: 姓名设置完成,结果: {}", socialSecurityCardInfoVO.getName());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][137]: 开始设置卡重置信息");
|
||||
socialSecurityCardInfoVO.setCardResetInformation(split[5]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][139]: 卡重置信息设置完成,结果: {}", socialSecurityCardInfoVO.getCardResetInformation());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][140]: 开始设置规格版本");
|
||||
socialSecurityCardInfoVO.setSpecificationVersion(split[6]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][142]: 规格版本设置完成,结果: {}", socialSecurityCardInfoVO.getSpecificationVersion());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][143]: 开始设置发卡日期");
|
||||
socialSecurityCardInfoVO.setIssuanceDate(split[7]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][145]: 发卡日期设置完成,结果: {}", socialSecurityCardInfoVO.getIssuanceDate());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][146]: 开始设置过期日期");
|
||||
socialSecurityCardInfoVO.setExpireDate(split[8]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][148]: 过期日期设置完成,结果: {}", socialSecurityCardInfoVO.getExpireDate());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][149]: 开始设置终端号");
|
||||
socialSecurityCardInfoVO.setTerminalNumber(split[9]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][151]: 终端号设置完成,结果: {}", socialSecurityCardInfoVO.getTerminalNumber());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][152]: 开始设置终端设备号");
|
||||
socialSecurityCardInfoVO.setTerminalDeviceNumber(split[10]);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][154]: 终端设备号设置完成,结果: {}", socialSecurityCardInfoVO.getTerminalDeviceNumber());
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][155]: 成功读取社保卡信息,返回结果");
|
||||
return Result.ok(socialSecurityCardInfoVO);
|
||||
}
|
||||
} else {
|
||||
return Result.error("社保卡读取失败!");
|
||||
}
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (handle != null) {
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][166]: 开始关闭端口,句柄: {}", handle);
|
||||
// 关闭端口
|
||||
CardReaderSdk.INSTANCE.ICC_Reader_Close(handle);
|
||||
log.info("[CardReaderServiceImpl][SocialSecurityCardReader][169]: 端口关闭成功");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
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(GetLibraryPath_Helper.GetLibraryPath(), 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, int 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 :副卡座 ;
|
||||
* 0x11:SAM1卡座;
|
||||
* 0x12:SAM2卡座;
|
||||
* 0x13:SAM3卡座;
|
||||
* 0x14:SAM4卡座
|
||||
* 0x3x:非接CPU卡
|
||||
* @return 上电返回ATR值,数据格式为十六进制字符串
|
||||
*/
|
||||
Long ICC_Reader_PowerOnHEX(Long ReaderHandle, String ICCSlotNo);
|
||||
|
||||
|
||||
/**
|
||||
* CPU 卡下电
|
||||
*
|
||||
* @param ReaderHandle ICCReaderOpen函数所返回的值
|
||||
* @param ICCSlotNo ICC插槽号 0x01 :大卡座 ;
|
||||
* 0x02 :副卡座 ;
|
||||
* 0x11:SAM1卡座;
|
||||
* 0x12:SAM2卡座;
|
||||
* 0x13:SAM3卡座;
|
||||
* 0x14:SAM4卡座
|
||||
* 0x3x:非接CPU卡
|
||||
* @return =0成功,非0失败
|
||||
*/
|
||||
Long ICC_Reader_PowerOff(Long ReaderHandle, String ICCSlotNo);
|
||||
|
||||
|
||||
/**
|
||||
* 获取卡座状态
|
||||
*
|
||||
* @param ReaderHandle ICCReaderOpen函数所返回的值
|
||||
* @param ICCSlotNo ICC插槽号 0x01 :大卡座 ;
|
||||
* 0x02 :副卡座 ;
|
||||
* 0x11:SAM1卡座;
|
||||
* 0x12:SAM2卡座;
|
||||
* 0x13:SAM3卡座;
|
||||
* 0x14:SAM4卡座
|
||||
* 0x3x:非接CPU卡
|
||||
* @return = 0 表示卡座有卡,其他见状态码
|
||||
*/
|
||||
int ICC_Reader_GetStatus(Long ReaderHandle, int ICCSlotNo);
|
||||
|
||||
|
||||
/**
|
||||
* 接触 CPU 卡执行APDU命令
|
||||
*
|
||||
* @param ReaderHandle ICCReaderOpen函数所返回的值
|
||||
* @param ICCSlotNo ICC插槽号 0x01 :大卡座 ;
|
||||
* 0x02 :副卡座 ;
|
||||
* 0x11:SAM1卡座;
|
||||
* 0x12:SAM2卡座;
|
||||
* 0x13:SAM3卡座;
|
||||
* 0x14:SAM4卡座
|
||||
* 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);
|
||||
int PICC_Reader_ReadIDCard(Long ReaderHandle, byte[] response);
|
||||
|
||||
/** 以下函数须在 “PICC_Reader_ReadIDCard” 函数执行成功之后调用,否则获取不到有效信息 */
|
||||
/**
|
||||
* 获取证件类型
|
||||
*
|
||||
* @return 0:居民身份证
|
||||
* 1:外国人永久居留证
|
||||
* 2:港澳台居民居住证
|
||||
* 4:新外国人永久居留证
|
||||
*/
|
||||
int GetCardType();
|
||||
|
||||
/**
|
||||
* 姓名(类型为 1 时表示:外国人中文姓名)
|
||||
*/
|
||||
int GetName(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
int GetSex(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 民族
|
||||
*/
|
||||
int GetNation(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
int GetBirth(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 住址
|
||||
*/
|
||||
int GetAddress(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 公民身份证号码(类型为 1时表示:外国人居留证号码)
|
||||
*/
|
||||
int GetCertNo(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 签发机关
|
||||
*/
|
||||
int GetDepartemt(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 有效起始日期
|
||||
*/
|
||||
int GetEffectDate(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 有效截止日期
|
||||
*/
|
||||
int GetExpireDate(byte[] outParam);
|
||||
|
||||
/**
|
||||
* bmp 格式照片数据
|
||||
*/
|
||||
int GetBmpFileData(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 生成照片
|
||||
* TODO 入参参考文中3.4.1
|
||||
*/
|
||||
int GetBmpFile(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 是否含存在指纹信息:存在时返回 512 或者 1024
|
||||
* 不存在时返回 0
|
||||
*/
|
||||
int IsFingerExist();
|
||||
|
||||
/**
|
||||
* 获取指纹数据:成功时返回获取到的字节长度
|
||||
*/
|
||||
int GetFingerprint(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 外国人英文姓名
|
||||
*/
|
||||
int GetEnName(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 外国人国籍代码,符合GB/T2659-2000规定
|
||||
*/
|
||||
int GetNationalityCode(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 港澳台通行证号码
|
||||
*/
|
||||
int GetTXZHM(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 港澳台通行证签发次数
|
||||
*/
|
||||
int GetTXZQFCS(byte[] outParam);
|
||||
|
||||
/**
|
||||
* 外国人换证次数
|
||||
*/
|
||||
int GetHZCS(byte[] outParam);
|
||||
|
||||
/** ------------ 社保卡 ---------*/
|
||||
/**
|
||||
* 选择社保卡社会保障系统环境后,通过 PSAM 卡对社保卡进行内部认证,通过后将卡内的基本信息读出返回。
|
||||
*
|
||||
* @param iType 操作卡的类型
|
||||
* 1-接触式操作卡;
|
||||
* 2-非接触式操作卡;
|
||||
* 3-自动寻卡,接触式操作卡优先;
|
||||
* 4-自动寻卡,非接触式操作卡优先
|
||||
* @return
|
||||
*/
|
||||
int iReadCardBas(int iType, byte[] data);
|
||||
|
||||
/**
|
||||
* 基于加密机的读基本信息(步骤一)
|
||||
* 选择社会保障系统环境后,返回内部认证和外部认证所需信息。
|
||||
*
|
||||
* @param iType 操作卡的类型
|
||||
* 1-接触式操作卡;
|
||||
* 2-非接触式操作卡;
|
||||
* 3-自动寻卡,接触式操作卡优先;
|
||||
* 4-自动寻卡,非接触式操作卡优先
|
||||
* @return
|
||||
*/
|
||||
Long iReadCardBas_HSM_Step1(int iType, byte[] data);
|
||||
|
||||
/**
|
||||
* 基于加密机的读基本信息(步骤二)
|
||||
* 根据加密机返回的内部认证和外部认证结果数据对社保卡进行内部认证和外部认证,通过后将卡内的基本信息读出返回。
|
||||
*
|
||||
* @param pKey 加密机返回的内部认证和外部认证结果数据,依次为:内部认证结果数据
|
||||
* (即内部认证鉴别数据(16 位)和内部认证鉴别所需的原始信息(16 位)拼接组
|
||||
* 成)、外部认证结果数据(即外部认证鉴别数据(16 位)和外部认证鉴别所需的
|
||||
* 原始信息(16 位)拼接组成)。各数据项之间以“|”分割,且最后一个数据项以“|”结尾。
|
||||
* @return
|
||||
*/
|
||||
public Long iReadCardBas_HSM_Step2(byte[] pKey, byte[] pOutInfo);
|
||||
|
||||
public static class GetLibraryPath_Helper {
|
||||
public GetLibraryPath_Helper() {
|
||||
}
|
||||
|
||||
private static String GetLibraryPath() {
|
||||
return "/win32-x86/SSSE32.dll";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.dpkj.modules.express.controller;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.dpkj.common.config.FileConfig;
|
||||
import com.dpkj.common.vo.Result;
|
||||
import com.dpkj.modules.express.service.PrinterService;
|
||||
import com.dpkj.modules.express.utils.FileUtils;
|
||||
import com.dpkj.modules.express.utils.PrinterUtil;
|
||||
import com.dpkj.modules.express.vo.Print;
|
||||
import com.dpkj.modules.express.vo.PrinterStatus;
|
||||
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.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Auther: 萧道子
|
||||
* @Date: 2024/4/28 13:58
|
||||
* @Description: 打印机
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("printer")
|
||||
public class PrinterController {
|
||||
@Autowired
|
||||
private PrinterService printerService;
|
||||
@Autowired
|
||||
private FileConfig fileConfig;
|
||||
|
||||
/**
|
||||
* 获取打印机状态
|
||||
*
|
||||
* @return com.dpkj.common.vo.Result
|
||||
* @author 萧道子 2024/4/28
|
||||
*/
|
||||
@GetMapping("status")
|
||||
public Result getStatus() {
|
||||
try {
|
||||
PrinterStatus val = printerService.getPrinterStatus();
|
||||
return Result.ok("获取成功", val);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.info("[printer][PrinterController.getStatus]获取打印机状态失败 {}", e.getMessage());
|
||||
return Result.error("打印机状态获取失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印文件
|
||||
*
|
||||
* @return com.dpkj.common.vo.Result
|
||||
* @author 萧道子 2024/4/29
|
||||
*/
|
||||
@PostMapping("print")
|
||||
public Result print(@RequestBody Print val) {
|
||||
try {
|
||||
String base64 = val.getBase64();
|
||||
if (StrUtil.isEmpty(base64)) throw new Exception("参数缺失!");
|
||||
|
||||
List<File> files = FileUtils.saveBase64ToBmp(base64);
|
||||
if (files.isEmpty()) throw new Exception("打印失败,面单未生成!");
|
||||
|
||||
File file = files.get(0);
|
||||
String filePath = file.getAbsolutePath();
|
||||
Integer num = printerService.printBmpByPath(filePath);// 打印
|
||||
FileUtil.del(file); // 删除文件
|
||||
|
||||
if (num != 0) throw new Exception("打印失败,请检查打印机是否连接正常!");
|
||||
|
||||
return Result.ok("打印成功");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.info("[printer][PrinterController.print] 打印面单 {}", e.getMessage());
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.dpkj.modules.express.service;
|
||||
|
||||
import com.dpkj.modules.express.utils.PrinterUtil;
|
||||
import com.dpkj.modules.express.vo.PrinterStatus;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface PrinterService {
|
||||
|
||||
/**
|
||||
* 获取打印机状态
|
||||
*
|
||||
* @return com.dpkj.modules.express.vo.PrinterStatus
|
||||
* @author 萧道子 2024/4/28
|
||||
*/
|
||||
PrinterStatus getPrinterStatus();
|
||||
|
||||
/**
|
||||
* 通过路径打印BMP文件
|
||||
*
|
||||
* @param path :
|
||||
* @return java.lang.Integer
|
||||
* @author 萧道子 2024/4/29
|
||||
*/
|
||||
Integer printBmpByPath(String path) throws Exception;
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package com.dpkj.modules.express.service.impl;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.dpkj.common.config.PrinterConfig;
|
||||
import com.dpkj.modules.express.service.PrinterService;
|
||||
import com.dpkj.modules.express.utils.PrinterUtil;
|
||||
import com.dpkj.modules.express.vo.PrinterStatus;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @Auther: 萧道子
|
||||
* @Date: 2024/4/28 14:34
|
||||
* @Description:
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PrinterServiceImpl implements PrinterService {
|
||||
@Resource
|
||||
private PrinterConfig printerConfig;
|
||||
|
||||
private PrinterUtil.MsPrintSdk printSDK = PrinterUtil.getPrintSDK();
|
||||
|
||||
public PrinterServiceImpl() throws PrinterUtil.PrinterRegistrationException {
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
log.info("[printer] 打印机初始化");
|
||||
initPrinter();
|
||||
}
|
||||
|
||||
private void initPrinter() {
|
||||
if (StrUtil.equals(printerConfig.getConnectionType(), "USB")) {
|
||||
// USB连接
|
||||
Integer n = printSDK.SetUsbportauto();
|
||||
log.info("[printer][SetUsbportauto] USB自动识别 {}", n);
|
||||
} else {
|
||||
// 串口连接
|
||||
String portName = printerConfig.getPortName();
|
||||
Integer baudRate = printerConfig.getBaudRate();
|
||||
Integer n = printSDK.SetPrintPort(portName, baudRate);
|
||||
log.info("[printer][SetPrintPort] 串口配置 {}", n);
|
||||
}
|
||||
Integer n = printSDK.SetInit();
|
||||
log.info("[printer][SetInit] 初始化打印机设置 {}", n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrinterStatus getPrinterStatus() {
|
||||
Integer num = printSDK.GetStatusspecial();
|
||||
PrinterStatus statusVo = getStatusVo(num);
|
||||
return statusVo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer printBmpByPath(String path) {
|
||||
initPrinter();
|
||||
|
||||
String formattedFilePath = path.replace("\\", "\\\\");
|
||||
int n = printSDK.PrintDiskimgfile(formattedFilePath);
|
||||
log.info("[printer][PrintDiskimgfile] 打印BMP文件 {}", n);
|
||||
|
||||
printMarkpositioncut();
|
||||
// printMarkcutpaper();
|
||||
printCutpaper();
|
||||
return n;
|
||||
}
|
||||
|
||||
private PrinterStatus getStatusVo(Integer num) {
|
||||
PrinterStatus val = new PrinterStatus().setValue(num);
|
||||
switch (num) {
|
||||
case 0:
|
||||
val.setText("打印机正常");
|
||||
break;
|
||||
case 1:
|
||||
val.setText("打印机未连接或未上电");
|
||||
break;
|
||||
case 2:
|
||||
val.setText("打印机和调用库不匹配");
|
||||
break;
|
||||
case 3:
|
||||
val.setText("当前使用打印机无特殊功能");
|
||||
break;
|
||||
case 4:
|
||||
val.setText("容纸器没有可靠上纸");
|
||||
break;
|
||||
case 5:
|
||||
val.setText("纸张堵在出票口,持续堆叠");
|
||||
break;
|
||||
case 6:
|
||||
val.setText("卡纸");
|
||||
break;
|
||||
case 7:
|
||||
val.setText("拽纸");
|
||||
break;
|
||||
case 8:
|
||||
val.setText("出纸传感器有纸");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检测黑标进纸到切纸位置
|
||||
*/
|
||||
private Integer printMarkpositioncut() {
|
||||
int n = printSDK.PrintMarkpositioncut();
|
||||
log.info("[printer][PrintMarkpositioncut] 检测黑标进纸到切纸位置 {}", n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印切纸
|
||||
*/
|
||||
private Integer printCutpaper() {
|
||||
int n = printSDK.PrintCutpaper(0);
|
||||
log.info("[printer][PrintCutpaper] 打印切纸 {}", n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* 黑标切纸
|
||||
*/
|
||||
private Integer printMarkcutpaper() {
|
||||
int n = printSDK.PrintMarkcutpaper(0);
|
||||
log.info("[printer][printMarkcutpaper] 打印黑标切纸 {}", n);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检测黑标进纸到打印位置
|
||||
*/
|
||||
private Integer printMarkpositionPrint() {
|
||||
int n = printSDK.PrintMarkpositionPrint();
|
||||
log.info("[printer][printMarkpositionPrint] 检测黑标进纸到打印位置 {}", n);
|
||||
return n;
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package com.dpkj.modules.express.utils;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.dpkj.common.config.FileConfig;
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @Auther: 萧道子
|
||||
* @Date: 2024/4/29 14:47
|
||||
* @Description:
|
||||
*/
|
||||
@Component
|
||||
public class FileUtils {
|
||||
|
||||
private static FileConfig fileConfig;
|
||||
|
||||
@Autowired
|
||||
public void setFileConfig(FileConfig fileConfig) {
|
||||
this.fileConfig = fileConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* base64Pdf 转字节流
|
||||
*
|
||||
* @param base64Pdf :
|
||||
* @return java.util.List<java.awt.image.BufferedImage>
|
||||
* @author 萧道子 2024/4/29
|
||||
*/
|
||||
public static List<BufferedImage> convertBase64PdfToBufferedImage(String base64Pdf) throws IOException {
|
||||
List<BufferedImage> images = new ArrayList<>();
|
||||
base64Pdf = base64Pdf.replaceAll("[^A-Za-z0-9+/=]", "");
|
||||
byte[] base64Bytes = Base64.getDecoder().decode(base64Pdf);
|
||||
PDDocument document = Loader.loadPDF(base64Bytes);
|
||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||
for (int i = 0; i < document.getNumberOfPages(); i++) {
|
||||
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(i, 188);
|
||||
int h = bufferedImage.getHeight();
|
||||
int w = bufferedImage.getWidth();
|
||||
int x = 15;
|
||||
int y = 28;
|
||||
// int y = new BigDecimal(0.03).multiply(new BigDecimal(h)).setScale(0, BigDecimal.ROUND_UP).intValue();
|
||||
BufferedImage croppedImageTopOne = bufferedImage.getSubimage(x, y, w - x, h - y);
|
||||
images.add(croppedImageTopOne);
|
||||
}
|
||||
document.close();
|
||||
return images;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将BufferedImage 保存为 BMP文件
|
||||
*
|
||||
* @param images :
|
||||
* @return java.util.List<java.io.File>
|
||||
* @author 萧道子 2024/4/29
|
||||
*/
|
||||
public static List<File> saveBufferedImageToBmp(List<BufferedImage> images) throws IOException {
|
||||
List<File> fileList = new ArrayList<>();
|
||||
String path = fileConfig.getPath();
|
||||
// 创建文件夹
|
||||
FileUtil.mkdir(fileConfig.getPath());
|
||||
for (BufferedImage bufferedImage : images) {
|
||||
String filePath = path + "/" + IdUtil.simpleUUID() + ".bmp";
|
||||
filePath = FileUtil.normalize(filePath);
|
||||
File bmpFile = new File(filePath);
|
||||
// 使用ImageIO将BufferedImage保存为BMP文件
|
||||
ImageIO.write(bufferedImage, "BMP", bmpFile);
|
||||
fileList.add(bmpFile);
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将base64保存为Bmp文件
|
||||
*
|
||||
* @param base64 :
|
||||
* @return java.util.List<java.io.File>
|
||||
* @author 萧道子 2024/4/29
|
||||
*/
|
||||
public static List<File> saveBase64ToBmp(String base64) throws IOException {
|
||||
List<BufferedImage> bufferedImages = FileUtils.convertBase64PdfToBufferedImage(base64);
|
||||
List<File> files = FileUtils.saveBufferedImageToBmp(bufferedImages);
|
||||
return files;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
package com.dpkj.modules.express.utils;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Native;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
public class PrinterUtil {
|
||||
|
||||
/**
|
||||
* 获取 MsPrintSdk 实例,同时注册 MsPrintSdk 控件。
|
||||
*
|
||||
* @return MsPrintSdk 实例
|
||||
* @throws PrinterRegistrationException 如果注册控件失败,抛出此异常
|
||||
*/
|
||||
public static MsPrintSdk getPrintSDK() throws PrinterRegistrationException {
|
||||
/* String ocxPath = "src/lib/Msprintsdk.ocx";
|
||||
ProcessBuilder processBuilder = new ProcessBuilder("regsvr32", "/s", ocxPath);
|
||||
try {
|
||||
Process process = processBuilder.start();
|
||||
int exitCode = process.waitFor();
|
||||
// if (exitCode != 0) {
|
||||
// throw new PrinterRegistrationException("Failed to register MsPrintSdk OCX. Exit code: " + exitCode);
|
||||
// }
|
||||
} catch (InterruptedException | IOException e) {
|
||||
throw new PrinterRegistrationException("Error occurred while registering MsPrintSdk OCX.", e);
|
||||
}*/
|
||||
|
||||
try {
|
||||
return Native.load("Msprintsdk.x64", MsPrintSdk.class);
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
log.info("[printer][PrinterUtil.getPrintSDK] SDK注册失败 {}", e.getMessage());
|
||||
throw new PrinterRegistrationException("Failed to load MsPrintSdk library: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void convertAndPassStringAsJCharArray(String filePath) {
|
||||
// 将字符串转换为char[]
|
||||
char[] chars = filePath.toCharArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义接口映射本地库中的函数。
|
||||
*/
|
||||
public interface MsPrintSdk extends Library {
|
||||
|
||||
/**
|
||||
* 设置打印端口和波特率。
|
||||
*
|
||||
* @param strPort 打印端口名
|
||||
* @param baudRate 波特率
|
||||
* @return 返回操作结果代码
|
||||
*/
|
||||
Integer SetPrintPort(String strPort, int baudRate);
|
||||
|
||||
/**
|
||||
* 设置打印连接方式、名称和值。
|
||||
*
|
||||
* @param iConnWay 连接方式
|
||||
* @param strName 名称
|
||||
* @param strValue 值
|
||||
* @return 返回操作结果代码
|
||||
*/
|
||||
Integer SetPrintConn(int iConnWay, String strName, String strValue);
|
||||
|
||||
/**
|
||||
* 设置 USB 端口自动识别。
|
||||
*
|
||||
* @return 返回操作结果代码
|
||||
*/
|
||||
Integer SetUsbportauto();
|
||||
|
||||
/**
|
||||
* 获取打印机状态。
|
||||
* 0 打印机正常
|
||||
* 1 打印机未连接或未上电
|
||||
* 2 打印机和调用库不匹配
|
||||
* 3 打印头打开
|
||||
* 4 切刀未复位
|
||||
* 5 打印头温度异常
|
||||
* 6 黑标错误,可能的情况为 黑标传感器坏 | 使用错误白纸 | 黑标不标准,浓度偏低
|
||||
* 7 纸尽
|
||||
* 8 纸将尽
|
||||
*
|
||||
* @return 返回打印机状态代码 0:成功 1:失败
|
||||
*/
|
||||
Integer GetStatus();
|
||||
|
||||
/**
|
||||
* 获取打印机特殊功能状态
|
||||
* 0 打印机正常
|
||||
* 1 打印机未连接或未上电
|
||||
* 2 打印机和调用库不匹配
|
||||
* 3 当前使用打印机无特殊功能
|
||||
* 4 容纸器没有可靠上纸
|
||||
* 5 纸张堵在出票口,持续堆叠
|
||||
* 6 卡纸,出纸口未堵的情况下,胶辊无法驱动纸持续前进了。 比如纸卷在胶辊上了、切刀堵住了纸的前进、纸张被拉住无法前进等
|
||||
* 7 拽纸,打印机感受到凭条被外力拖拽
|
||||
* 8 出纸传感器有纸
|
||||
*
|
||||
* @return 返回打印机状态代码 0:成功 1:失败
|
||||
*/
|
||||
Integer GetStatusspecial();
|
||||
|
||||
/**
|
||||
* 初始化打印机设置。
|
||||
*
|
||||
* @return 返回操作结果代码
|
||||
*/
|
||||
Integer SetInit();
|
||||
|
||||
/**
|
||||
* 打印二维码。
|
||||
*
|
||||
* @param s 二维码数据
|
||||
* @param i 参数 i
|
||||
* @param i1 参数 i1
|
||||
* @param i2 参数 i2
|
||||
*/
|
||||
void PrintQrcode(String s, int i, int i1, int i2);
|
||||
|
||||
/**
|
||||
* 设置左边距。
|
||||
*
|
||||
* @param i 左边距值
|
||||
*/
|
||||
void SetLeftmargin(int i);
|
||||
|
||||
/**
|
||||
* 打印字符串。
|
||||
*
|
||||
* @param s 字符串内容
|
||||
* @param i 参数 i
|
||||
* @return 返回操作结果代码
|
||||
*/
|
||||
Integer PrintString(String s, int i);
|
||||
|
||||
/**
|
||||
* 打印剩余的二维码。
|
||||
*
|
||||
* @return 返回操作结果代码
|
||||
*/
|
||||
Integer PrintRemainQR();
|
||||
|
||||
/**
|
||||
* 设置 设置汉字模式 模式。
|
||||
*
|
||||
* @param i 模式参数
|
||||
* @return 返回操作结果代码
|
||||
*/
|
||||
Integer SetReadZKmode(int i);
|
||||
|
||||
/**
|
||||
* 切纸。
|
||||
*
|
||||
* @param i 切纸参数
|
||||
* 0 全切
|
||||
* 1 半切
|
||||
* @return 返回操作结果代码
|
||||
*/
|
||||
Integer PrintCutpaper(int i);
|
||||
|
||||
/**
|
||||
* 设置 HT 座位信息。
|
||||
*
|
||||
* @param s 座位信息
|
||||
* @param i 参数 i
|
||||
*/
|
||||
void SetHTseat(String s, int i);
|
||||
|
||||
/**
|
||||
* 打印下一个 HT 数据。
|
||||
*/
|
||||
void PrintNextHT();
|
||||
|
||||
/**
|
||||
* 打印BMP
|
||||
*
|
||||
* @param strPath BMP文件路径
|
||||
*/
|
||||
Integer PrintDiskbmpfile(String strPath);
|
||||
|
||||
/**
|
||||
* 打印BMP
|
||||
*
|
||||
* @param strPath BMP文件路径
|
||||
*/
|
||||
Integer PrintDiskimgfile(String strPath);
|
||||
|
||||
|
||||
/**
|
||||
* 检测黑标进纸到打印位置
|
||||
*/
|
||||
Integer PrintMarkpositionPrint();
|
||||
|
||||
/**
|
||||
* 检测黑标进纸到切纸位置
|
||||
*/
|
||||
Integer PrintMarkpositioncut();
|
||||
|
||||
/**
|
||||
* 打印黑标切纸
|
||||
* <p>
|
||||
* 0 检测黑标全切
|
||||
* 1 不检测黑标半切
|
||||
*/
|
||||
Integer PrintMarkcutpaper(int iMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义自定义异常类,用于表示注册控件时发生的错误
|
||||
*/
|
||||
public static class PrinterRegistrationException extends Exception {
|
||||
public PrinterRegistrationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PrinterRegistrationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.dpkj.modules.express.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @Auther: 萧道子
|
||||
* @Date: 2024/4/29 17:03
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class Print implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String base64;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.dpkj.modules.express.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @Auther: 萧道子
|
||||
* @Date: 2024/4/28 14:44
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class PrinterStatus implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private int value;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String text;
|
||||
|
||||
}
|
||||
@@ -6,8 +6,16 @@ dpkj:
|
||||
printer:
|
||||
# 打印机连接方式 USB: usb连接 | BTMS:串口连接
|
||||
connection-type: USB
|
||||
# 终端号设置
|
||||
terminal-Number: PORT-000000001
|
||||
# 打印时间格式化
|
||||
time-Type: yyyy-MM-dd HH:mm:ss
|
||||
# 打印端口 串口连接下使用
|
||||
port-name:
|
||||
# 波特率 串口连接下使用
|
||||
baud-rate:
|
||||
|
||||
# 身份证读取等待时间
|
||||
IDCardReader:
|
||||
waitingTime: 30000
|
||||
|
||||
|
||||
@@ -6,8 +6,17 @@ dpkj:
|
||||
printer:
|
||||
# 打印机连接方式 USB: usb连接 | BTMS:串口连接
|
||||
connection-type: USB
|
||||
# 终端号设置
|
||||
terminal-Number: PORT-000000001
|
||||
# 打印时间格式化
|
||||
time-Type: yyyy-MM-dd HH:mm:ss
|
||||
# 打印端口 串口连接下使用
|
||||
port-name:
|
||||
# 波特率 串口连接下使用
|
||||
baud-rate:
|
||||
|
||||
|
||||
# 身份证读取等待时间
|
||||
IDCardReader:
|
||||
waitingTime: 30000
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration debug="false">
|
||||
<!--定义日志文件的存储地址 -->
|
||||
<property name="LOG_HOME" value="./logs/java"/>
|
||||
<property name="LOG_HOME" value="./logs/server"/>
|
||||
|
||||
<!--<property name="COLOR_PATTERN" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta( %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''})- %gray(%msg%xEx%n)" />-->
|
||||
<!-- 控制台输出 -->
|
||||
|
||||
71
src/main/resources/templates/department.html
Normal file
71
src/main/resources/templates/department.html
Normal file
@@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>门诊缴费凭证T2</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="font-size: 24px;font-weight: bold;">
|
||||
<div>
|
||||
<div style="text-align: center; font-size: 32px; "><span th:text="${hospitalName}"></span>
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 20px;">
|
||||
****<span th:text="${registeTerminalName}"></span>****
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
<div style="text-align: center; font-size: 32px; "><span th:text="${registeType}"></span>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
</div>
|
||||
<div style="font-size: 29px;word-break: break-all;margin: 10px 20px;">
|
||||
<div style="display: flex;">
|
||||
<span style="margin-right: 25px;">姓名:<span th:text="${name}"></span></span>
|
||||
<span style="margin-right: 25px;">性别:<span th:text="${gender}"></span></span>
|
||||
<span>年龄:<span th:text="${age}"></span></span>
|
||||
</div>
|
||||
<div style="word-break: break-all;margin-top: 10px;">
|
||||
<span style="margin-right: 25px;" th:if="${outpatientNumber}">
|
||||
门诊号:<span th:text="${outpatientNumber}"></span></span>
|
||||
<span th:if="${doctor}">
|
||||
就诊医生:<span th:text="${doctor}"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div style="margin-top: 10px;" th:if="${department}">就诊科室:<span th:text="${department}"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
<div style="font-size: 29px; margin: 10px 20px">
|
||||
<div style="word-break: break-all;">
|
||||
<span style="margin-right: 25px;">费用总额:<span th:text="${totalFee}"></span></span>
|
||||
<span>个人支付:<span th:text="${personalPayment}"></span></span>
|
||||
</div>
|
||||
<div style="margin-top: 10px;word-break: break-all;"><span>实收金额:</span><span
|
||||
th:text="${actualReceiptAmount}"></span></div>
|
||||
<div style="margin-top: 10px;"><span>实收金额:</span><span th:text="${actualReceiptAmountChinese}"></span></div>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
<div style="margin: 10px 20px">
|
||||
<table style="width: 100%; table-layout: fixed; border-collapse: collapse;">
|
||||
<tr style="font-size: 29px;">
|
||||
<th style="width: 300px;text-align: left;">项目名称</th>
|
||||
<th style="width: calc(25% - 5px);text-align: center;">数量</th>
|
||||
<th style="width: calc(25% - 5px);text-align: center;">单价</th>
|
||||
<th style="width: calc(25% - 5px);text-align: center;">小计</th>
|
||||
</tr>
|
||||
<tr style="font-size: 24px;word-break: break-all;" th:each="item : ${items}">
|
||||
<td style="text-align: left;width: 300px;" th:text="${item.name}"></td>
|
||||
<td style="text-align: center;" th:text="${item.quantity}"></td>
|
||||
<td style="text-align: center;" th:text="${item.unitPrice}"></td>
|
||||
<td style="text-align: center;" th:text="${item.subtotal}"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
<div style="margin-left: 20px;font-size: 29px;">终端编号:<span th:text="${terminalNumber}"></span></div>
|
||||
<div style="margin-left: 20px;font-size: 29px;">打印时间:<span th:text="${printTime}"></span></div>
|
||||
<div style="margin-left: 20px; margin-top: 10px;font-size: 27px;">
|
||||
<span>温馨提示:请取走缴费凭证,并妥善保管。<br/>如果对缴费存在疑问,请到人工窗口咨询!</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
39
src/main/resources/templates/hospitalPayment.html
Normal file
39
src/main/resources/templates/hospitalPayment.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>住院押金预缴</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="font-size: 24px;font-weight: bold;">
|
||||
<div>
|
||||
<div style="text-align: center; font-size: 32px; "><span th:text="${hospitalName}"></span>
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 20px;">
|
||||
****<span th:text="${registeTerminalName}"></span>****
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
<div style="text-align: center; font-size: 32px; "><span th:text="${registeType}"></span>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
</div>
|
||||
<div style=" font-size: 29px;">
|
||||
<div style="margin-left: 20px;">姓  名:<span th:text="${name}"></span></div>
|
||||
<div style="margin-left: 20px;">性  别:<span th:text="${gender}"></span></div>
|
||||
<div style="margin-left: 20px;">年  龄:<span th:text="${age}"></span></div>
|
||||
<div style="margin-left: 20px;">出生日期:<span th:text="${birthDate}"></span></div>
|
||||
<div style="margin-left: 20px;">门 诊 号:<span th:text="${outpatientNumber}"></span></div>
|
||||
<div style="margin-left: 20px;">入院科室:<span th:text="${department}"></span></div>
|
||||
<div style="margin-left: 20px;">总 费 用:<span th:text="${totalFee}"></span> 元</div>
|
||||
<div style="margin-left: 20px;">支付方式:<span th:text="${paymentMethod}"></span></div>
|
||||
<div style="margin-left: 20px;">交易流水:<span style="font-size: 27px;" th:text="${transactionNumber}"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
<div style="margin-left: 20px;font-size: 29px;">终端编号:<span th:text="${terminalNumber}"></span></div>
|
||||
<div style="margin-left: 20px;font-size: 29px;">打印时间:<span th:text="${printTime}"></span></div>
|
||||
<div style="margin-left: 20px; margin-top: 10px;font-size: 27px;">
|
||||
<span>温馨提示:请取走缴费凭证,并妥善保管。<br/>如果对缴费存在疑问,请到人工窗口咨询!</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
37
src/main/resources/templates/outpatientPayment.html
Normal file
37
src/main/resources/templates/outpatientPayment.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>门诊缴费凭证T1</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="font-size: 24px;font-weight: bold;">
|
||||
<div>
|
||||
<div style="text-align: center; font-size: 32px; "><span th:text="${hospitalName}"></span>
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 20px;">
|
||||
****<span th:text="${registeTerminalName}"></span>****
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
<div style="text-align: center; font-size: 32px; "><span th:text="${registeType}"></span>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
</div>
|
||||
<div style="font-size: 29px;">
|
||||
<div style="margin-left: 20px;">姓  名:<span th:text="${name}"></span></div>
|
||||
<div style="margin-left: 20px;">性  别:<span th:text="${gender}"></span></div>
|
||||
<div style="margin-left: 20px;">年  龄:<span th:text="${age}"></span></div>
|
||||
<div style="margin-left: 20px;">出生日期:<span th:text="${birthDate}"></span></div>
|
||||
<div style="margin-left: 20px;">总 费 用:<span th:text="${totalFee}"></span> 元</div>
|
||||
<div style="margin-left: 20px;">支付方式:<span th:text="${paymentMethod}"></span></div>
|
||||
<div style="margin-left: 20px;">交易流水:<span style="font-size: 27px;" th:text="${transactionNumber}"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
<div style="margin-left: 20px;font-size: 29px;">终端编号:<span th:text="${terminalNumber}"></span></div>
|
||||
<div style="margin-left: 20px;font-size: 29px;">打印时间:<span th:text="${printTime}"></span></div>
|
||||
<div style="margin-left: 20px; margin-top: 10px;font-size: 27px;">
|
||||
<span>温馨提示:请取走缴费凭证,并妥善保管。<br/>如果对缴费存在疑问,请到人工窗口咨询!</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
47
src/main/resources/templates/register.html
Normal file
47
src/main/resources/templates/register.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>挂号单</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="font-size: 24px;font-weight: bold;">
|
||||
<div>
|
||||
<div style="text-align: center; font-size: 32px;"><span th:text="${hospitalName}"></span>
|
||||
</div>
|
||||
<div style="text-align: center;font-size: 20px;">
|
||||
<span>****<span th:text="${registeTerminalName}"></span>****</span>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
<div style="text-align: center; font-size: 32px;"><span th:text="${registeType}"></span>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin: 3px 0;"></div>
|
||||
</div>
|
||||
<div style="font-size: 29px;">
|
||||
<div style="margin-left: 20px;">姓  名:<span th:text="${name}"></span></div>
|
||||
<div style="margin-left: 20px;">性  别:<span th:text="${gender}"></span></div>
|
||||
<div style="margin-left: 20px;">年  龄:<span th:text="${age}"></span></div>
|
||||
<div style="margin-left: 20px;">出生日期:<span th:text="${birthDate}"></span></div>
|
||||
<div style="margin-left: 20px;">门 诊 号:<span th:text="${outpatientNumber}"></span></div>
|
||||
<div style="margin-left: 20px;">就诊科室:<span th:text="${department}"></span></div>
|
||||
<div style="margin-left: 20px;">出诊级别:<span th:text="${visitLevel}"></span></div>
|
||||
<div style="margin-left: 20px;">就诊医生:<span th:text="${doctor}"></span></div>
|
||||
<div style="margin-left: 20px;">号  序:<span th:text="${sequence}"></span></div>
|
||||
<div style="margin-left: 20px;">挂号日期:<span th:text="${registerDate}"></span></div>
|
||||
<div style="margin-left: 20px;">总 费 用:<span th:text="${totalFee}"></span> 元</div>
|
||||
<div style="margin-left: 20px;">支付方式:<span th:text="${paymentMethod}"></span></div>
|
||||
<div style="margin-left: 20px;">交易流水:<span style="font-size: 26px;"
|
||||
th:text="${transactionNumber}"></span></div>
|
||||
<div style="width:100%;text-align: center;">
|
||||
<img th:src="${qrCodeBase64}"
|
||||
alt="#"/>
|
||||
</div>
|
||||
</div>
|
||||
<div style="border-bottom: 1px dashed #000;margin-bottom: 3px;"></div>
|
||||
<div style="margin-left: 20px;font-size: 29px;">终端编号:<span th:text="${terminalNumber}"></span></div>
|
||||
<div style="margin-left: 20px;font-size: 29px;">打印时间:<span th:text="${printTime}"></span></div>
|
||||
<div style="margin-left: 20px; margin-top: 10px;font-size: 27px;">
|
||||
<span>温馨提示:请取走缴费凭证,并妥善保管。<br/>如果对缴费存在疑问,请到人工窗口咨询!</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Binary file not shown.
BIN
src/main/resources/win32-x86/SSSE32.dll
Normal file
BIN
src/main/resources/win32-x86/SSSE32.dll
Normal file
Binary file not shown.
BIN
src/main/resources/win32-x86/UnPack.dll
Normal file
BIN
src/main/resources/win32-x86/UnPack.dll
Normal file
Binary file not shown.
BIN
src/main/resources/win32-x86/autoreplyprint.dll
Normal file
BIN
src/main/resources/win32-x86/autoreplyprint.dll
Normal file
Binary file not shown.
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user