Compare commits

...

66 Commits

Author SHA1 Message Date
58c1975b05 Merge remote-tracking branch 'origin/1.0' into 1.0
# Conflicts:
#	pom.xml
2025-03-26 14:39:54 +08:00
906928ea0f 更新配置 2025-03-26 14:37:56 +08:00
3eb441d41c 优化 2025-03-26 14:01:23 +08:00
12f2374ccf 优化 2025-03-26 13:33:30 +08:00
aa0dc12805 优化 2025-03-26 13:23:10 +08:00
6755206d2e Merge remote-tracking branch 'origin/1.0' into 1.0
# Conflicts:
#	src/main/resources/templates/department.html
2025-03-26 13:22:25 +08:00
3938301b66 门诊小票T2模板添加以class命名的文件,还原为内联样式 2025-03-26 13:21:55 +08:00
546f644ffd fix:pom文件中,移除对resource的排除 2025-03-26 13:11:03 +08:00
ffaa06f934 fix:恢复原始样式 2025-03-26 13:10:41 +08:00
20b25042b5 fix:修复图片分割出现黑边问题 2025-03-26 13:09:25 +08:00
503ff3a73e fix:修改异常提示 2025-03-26 13:01:53 +08:00
ee2115e367 门诊小票T2模板优化 2025-03-26 11:12:16 +08:00
c9592dfd90 feat:增加终端号配置 2025-03-26 11:08:05 +08:00
85b2b1bd87 Merge remote-tracking branch 'origin/1.0' into 1.0
# Conflicts:
#	src/main/resources/templates/department.html
2025-03-26 10:54:16 +08:00
6c126a6757 feat:新增门诊缴费-T2模板为完全动态生成 2025-03-26 10:53:04 +08:00
9e3608772f 门诊小票T2模板优化 2025-03-26 09:09:06 +08:00
32e17374dd 门诊小票T2模板改写 2025-03-25 22:59:08 +08:00
b2f2e8cd49 feat:增加门诊缴费模板-T2 2025-03-25 21:00:46 +08:00
e36ffeb994 fix:修复单文件打印字段问题 2025-03-25 15:47:56 +08:00
85909ab1f8 Merge remote-tracking branch 'origin/1.0' into 1.0 2025-03-25 15:24:59 +08:00
ac183aa473 fix:修复图片太长打印不出问题,需要配置台式机的GWI_Receipt_form.form 2025-03-25 15:24:44 +08:00
e945c2cb1f 配置修改 2025-03-24 22:12:01 +08:00
817450ea0b fix:去除最底部下划线 2025-03-24 10:25:49 +08:00
05cea4a7fc feat:增加门诊缴费小票模板 2025-03-24 09:52:46 +08:00
44ceb7e671 fix:修复住院缴费小票模板 2025-03-24 09:49:52 +08:00
77fa2397c0 feat:增加住院缴费模板 2025-03-24 09:47:47 +08:00
86bf02cf02 feat:修改模板,使用数字或者直接传模板名称进行选择 2025-03-24 09:42:37 +08:00
6df8da44a7 fix:修改自助挂号模板 2025-03-20 14:18:36 +08:00
101de5e6e6 feat:取消 2025-03-19 13:38:46 +08:00
2ba0e48256 feat:lib库和jar包分开打包,如果有更新maven库的话,注意更新lib库 2025-03-19 13:23:26 +08:00
18f0a0bb66 fix:优化 2025-03-19 12:49:35 +08:00
df17c286fe fix:修改模板渲染 2025-03-19 12:33:35 +08:00
da5c10b632 fix:修改默认图片渲染宽度高度 2025-03-19 10:50:30 +08:00
d3e775b720 fix:修复凭证打印机bug 2025-03-18 19:21:33 +08:00
f029719a42 删除无关模块 2025-03-18 15:15:20 +08:00
b2b9f15f79 fix:修复模板渲染器图片渲染问题 2025-03-17 10:27:47 +08:00
4fbe38b04a fix:修复ms439打印机逻辑 2025-03-17 10:08:19 +08:00
620bf524db feat:去除模板打印模拟数据 2025-03-14 16:39:19 +08:00
32db058c31 fix:优化自助挂号模板 2025-03-14 16:36:58 +08:00
9d01a5d498 feat:增加模板渲染失败提示 2025-03-14 16:33:03 +08:00
46810f941d fix:优化打印机逻辑 2025-03-14 16:02:00 +08:00
3f0d3e5026 fix:小票打印机纸张校验增加501错误码,直接返回错误信息 2025-03-13 09:58:23 +08:00
0601dbf25a fix:小票打印机纸张校验 2025-03-13 09:57:00 +08:00
4d5ee0c593 feat:小票打印机增加纸张校验 2025-03-13 09:55:35 +08:00
2ee5b3015a feat:ms439打印机相关接口,增加小票打印机获取接口 2025-03-13 09:43:16 +08:00
f4edd0189b feat:增加ms439打印机错误码 2025-03-06 14:14:03 +08:00
5c34a03e7f 密码键盘调用操作日志记录格式调整 2025-02-27 16:09:04 +08:00
f088a42aff 添加操作状态码,日志记录格式调整 2025-02-26 18:19:44 +08:00
7c55b7d2d2 身份证照片路径优化 2025-02-26 09:34:57 +08:00
6107203f9f 设备调用之前复位(初始化) 2025-02-25 17:54:20 +08:00
563bc271f3 Merge remote-tracking branch 'origin/1.0' into 1.0 2025-02-25 11:32:45 +08:00
787892924b 打包后调用出错(乱码问题) 2025-02-25 11:32:36 +08:00
40b26f94d8 配置修改 2025-02-24 16:12:26 +08:00
aec28f662a 调整请求方式 2025-02-18 15:24:46 +08:00
fd2baf7d33 密码输入优化 2025-02-17 14:09:48 +08:00
478d88f3c4 完成数字键盘取数据(密码数据) 2025-02-13 16:34:10 +08:00
63a9070f56 密码键盘、扫描仪-- 待测试 2025-02-10 17:41:16 +08:00
5a84be620d 身份证读取、社保卡读取 2025-02-10 15:08:18 +08:00
ed61a25ac4 fix:利盟服务返回值中的数据增加使用泛型进行接收 2025-02-08 17:20:50 +08:00
fc1e02264d feat:增加挂号打印模块 2025-02-08 15:41:29 +08:00
f966dd9387 fix:修复result显示bug 2025-02-08 15:40:47 +08:00
117f293e83 feat:增加利盟服务接口调用工具类 2025-02-08 14:45:56 +08:00
0ebf6d4258 feat:增加全局异常处理 2025-02-07 11:03:05 +08:00
9f3596abd6 feat:增加springMVC配置 2025-02-07 11:02:34 +08:00
52abbf6a8f feat:增加小票挂号html模板 2025-02-07 11:02:14 +08:00
0fdb14af2a feat:增加模板相关依赖 2025-02-07 11:01:16 +08:00
79 changed files with 5169 additions and 765 deletions

2
README.md Normal file
View File

@@ -0,0 +1,2 @@
# 台式机对接程序

175
pom.xml
View File

@@ -4,10 +4,11 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.dpkj</groupId>
<artifactId>银医通-澜沧中医院-DLL-台式机</artifactId>
<!-- 银医通-澜沧中医院-DLL-台式机 -->
<artifactId>yinyitong-dll-stand</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ems-express-call-dll</name>
<description>ems-express-call-dll</description>
<name>yinyitong-dll-stand</name>
<description>银医通-澜沧中医院-DLL-台式机</description>
<parent>
<groupId>org.springframework.boot</groupId>
@@ -34,11 +35,13 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -50,12 +53,14 @@
<artifactId>hutool-crypto</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- 调用DLL -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>${jna.version}</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
@@ -68,16 +73,70 @@
<artifactId>pdfbox</artifactId>
<version>${pdfbox.version}</version>
</dependency>
<!-- json处理-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</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>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.15</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>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
<!--打包时将resource下的文件一起打包-->
<resources>
<resource>
@@ -88,6 +147,102 @@
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<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>
<!-- 生成API文档插件 -->
<!--<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
</plugin>-->
<!--打jar包排除资源文件和依赖jar同时外部的lib、resource目录加入到classpath中 用命令java -jar运行jar时就不需要用-Dloader.path指定外部资源路径了 不需要spring-boot-maven-plugin插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<!--MANIFEST.MF 中 Class-Path 加入前缀用命令java -jar运行jar时就不用-Dloader.path指定外部资源路径了-->
<classpathPrefix>lib/</classpathPrefix>
<!--jar包名字是否包含唯一版本标识-->
<useUniqueVersions>false</useUniqueVersions>
<!--指定含main方法的主类入口-->
<mainClass>com.dpkj.Application</mainClass>
</manifest>
<manifestEntries>
<!--MANIFEST.MF 中 Class-Path 加入资源文件目录用命令java -jar时就不用-Dloader.path指定外部资源路径了 -->
<Class-Path>resources/</Class-Path>
</manifestEntries>
</archive>
<!-- 打包时从jar包里排除资源文件 -->
<excludes>
<!--<exclude>*.yml</exclude>
<exclude>*.xml</exclude>
<exclude>templates/**</exclude>
<exclude>win32-x86-64/**</exclude>
<exclude>win32-x86/**</exclude>-->
</excludes>
<!-- 指定项目打成jar包输出位置 -->
<outputDirectory>${project.build.directory}/output</outputDirectory>
</configuration>
</plugin>
<!-- 拷贝依赖jar包插件maven-jar-plugin只是打包排除文件 而把依赖jar包拷贝到外部lib目录就需要maven-dependency-plugin插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/output/lib/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!--拷贝资源文件! 插件maven-jar-plugin只负责打包时排除文件 而把资源文件拷贝到外部resource目录就需要maven-dependency-plugin插件-->
<!--<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
&lt;!&ndash;拷贝此目录下的所有文件到指定的外部目录。只负责拷贝而不是从jar包中排除&ndash;&gt;
<directory>src/main/resources</directory>
</resource>
</resources>
&lt;!&ndash; 把“<resource><directory>”指定目录中的文件输出到此处指定目录 &ndash;&gt;
<outputDirectory>${project.build.directory}/output/resources</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>-->
</plugins>
</build>

View File

@@ -0,0 +1,22 @@
package com.dpkj.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Auther: 萧道子
* @Date: 2024/4/28 14:55
* @Description:
*/
@Data
@Component
@ConfigurationProperties(prefix = "dpkj.chs")
public class ChsConfig {
/**
* 医保机构编码
*/
private String orgcode;
}

View File

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

View File

@@ -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;
}

View File

@@ -0,0 +1,36 @@
package com.dpkj.common.constant;
public interface ChsConst {
/**
* 电子凭证二维码解码接口
*/
String APITYPE_QRCODE = "ec.query";
/**
* 医保电子凭证密码核验接口
*/
String APITYPE_VERIFY = "cn.nhsa.ec.pwd";
/**
* 终端医保电子凭证码解码接口
*/
String APITYPE_DECODE = "cn.nhsa.qrcode.get";
/**
* 刷脸获取医保用户身份授权接口
*/
String APITYPE_AUTHORIZATION = "cn.nhsa.ec.auth";
/**
* 刷脸授权获取医保身份接口
*/
String APITYPE_CHECK = "cn.nhsa.auth.check";
/**
* 结算结果通知接口
*/
String APITYPE_Notify = "cn.nhsa.settle.notify";
}

View File

@@ -0,0 +1,264 @@
package com.dpkj.common.constant;
import java.util.HashMap;
import java.util.Map;
public final class ErrorCodeConstants {
private ErrorCodeConstants() {
throw new AssertionError("This class should not be instantiated.");
}
// 公共错误码
public static final Map<Integer, String> COMMON_ERROR_CODE;
static {
COMMON_ERROR_CODE = new HashMap<>();
COMMON_ERROR_CODE.put(-1, "应用已经调用过WFSStartUp并且之后并未调用WFSCleanUp");
COMMON_ERROR_CODE.put(-2, "应用要求的XFS API版本过高");
COMMON_ERROR_CODE.put(-3, "应用要求的XFS API版本过低");
COMMON_ERROR_CODE.put(-4, "请求被取消");
COMMON_ERROR_CODE.put(-5, "hKey并未对应当前打开的key(注册表项)");
COMMON_ERROR_CODE.put(-6, "在当前的key中不存在指定的lpszValueName(键名)");
COMMON_ERROR_CODE.put(-7, "在当前的key中不存在指定的lpszSubKey(子键)");
COMMON_ERROR_CODE.put(-8, "在当前的key中不存在指定的键值");
COMMON_ERROR_CODE.put(-9, "待删除的key包含子键,无法删除");
COMMON_ERROR_CODE.put(-10, "返回的键名字符串的长度超过了缓存的长度");
COMMON_ERROR_CODE.put(-11, "本项已经为最后一项,没有下一项");
COMMON_ERROR_CODE.put(-12, "返回的键值数据的长度超过了缓存的长度");
COMMON_ERROR_CODE.put(-13, "函数必须的设备访问,但设备尚未准备好或已超时");
COMMON_ERROR_CODE.put(-14, "函数必须的设备访问,但设备出错");
COMMON_ERROR_CODE.put(-15, "XFS子系统内发生的内部不一致或其他异常错误");
COMMON_ERROR_CODE.put(-16, "lpvOriginal地址参数错误,未指向上次申请的缓存");
COMMON_ERROR_CODE.put(-17, "无效的应用句柄(例如由不当的函数调用顺序引起)");
COMMON_ERROR_CODE.put(-18, "lpvData参数未指向一个申请的缓存结构");
COMMON_ERROR_CODE.put(-19, "当前的服务类(service class)不支持dwCategory参数");
COMMON_ERROR_CODE.put(-20, "当前的服务类不支持发出的dwCommand");
COMMON_ERROR_CODE.put(-21, "当前的服务类不支持dwEventClass指定的一个或多个事件类");
COMMON_ERROR_CODE.put(-22, "hService参数是无效的服务句柄");
COMMON_ERROR_CODE.put(-23, "hProvider参数是无效的Provier句柄");
COMMON_ERROR_CODE.put(-24, "hWnd参数是无效的窗口句柄");
COMMON_ERROR_CODE.put(-25, "hWndReg参数是无效的窗口句柄");
COMMON_ERROR_CODE.put(-26, "指针参数不指向可访问内存");
COMMON_ERROR_CODE.put(-27, "RequestID参数不符合对服务的未处理请求");
COMMON_ERROR_CODE.put(-28, "lpResult参数不是一个指向WFSRESULT结构的指针");
COMMON_ERROR_CODE.put(-29, "SP的文件无效或损坏");
COMMON_ERROR_CODE.put(-30, "hWnd与usTimerID没有对应当前活动的timer");
COMMON_ERROR_CODE.put(-31, "dwTraceLevel参数的值为无效值");
COMMON_ERROR_CODE.put(-32, "服务被另一个hService锁定");
COMMON_ERROR_CODE.put(-33, "调用被阻塞");
COMMON_ERROR_CODE.put(-34, "SP的文件不存在");
COMMON_ERROR_CODE.put(-35, "指定的线程不存在");
COMMON_ERROR_CODE.put(-36, "timer创建失败");
COMMON_ERROR_CODE.put(-37, "应用请求解锁,而此设备并未被加锁");
COMMON_ERROR_CODE.put(-38, "XFS卸载SP DLL失败");
COMMON_ERROR_CODE.put(-39, "应用程序此前还没有成功的执行WFSStartUp");
COMMON_ERROR_CODE.put(-40, "hWndReg窗口没有指定接收任何事件类");
COMMON_ERROR_CODE.put(-41, "在该线程上有一个阻塞操作正在进行此时只允许WFSCancelBlockingCall和WFSIsBlocking");
COMMON_ERROR_CODE.put(-42, "申请的空间不足");
COMMON_ERROR_CODE.put(-43, "逻辑名无效");
COMMON_ERROR_CODE.put(-44, "SPI版本过高");
COMMON_ERROR_CODE.put(-45, "SPI版本过低");
COMMON_ERROR_CODE.put(-46, "应用请求的服务版本过高");
COMMON_ERROR_CODE.put(-47, "应用请求的服务版本过低");
COMMON_ERROR_CODE.put(-48, "超时周期到期");
COMMON_ERROR_CODE.put(-49, "dwCategory虽然有效,但不被SP支持");
COMMON_ERROR_CODE.put(-50, "尽管发出的dwCommand对此服务类别有效但此服务提供程序或设备不支持发出的dwCommand");
COMMON_ERROR_CODE.put(-51, "服务中的两个模块版本不匹配");
COMMON_ERROR_CODE.put(-52, "输入参数中包含无效数据");
COMMON_ERROR_CODE.put(-53, "函数必须的配置信息访问,但软件出错");
COMMON_ERROR_CODE.put(-54, "与服务的连接丢失");
COMMON_ERROR_CODE.put(-55, "用户在阻止设备的正确操作");
COMMON_ERROR_CODE.put(-56, "尽管作为输入参数传递的数据结构对此服务类别有效,但此服务提供程序或设备不支持该数据结构");
COMMON_ERROR_CODE.put(-57, "一些设备能够识别试图欺骗获得重要信息或媒介的恶意物理攻击。在这些情况下,将返回此错误代码,显示用户试图对该设备采取欺骗行为");
COMMON_ERROR_CODE.put(-58, "此时被请求的操作无效,或在设备当时的状态下,被请求的操作无效");
}
// 社保卡相关错误码
public static final Map<Integer, String> SOCIAL_SECURITYCARD_ERROR_CODE;
static {
SOCIAL_SECURITYCARD_ERROR_CODE = new HashMap<>();
SOCIAL_SECURITYCARD_ERROR_CODE.put(-1, "卡类型不对");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-2, "无卡");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-3, "有卡未上电");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-4, "卡无应答");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-5, "加载动态库错");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-11, "读卡器连接错");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-12, "未建立连接");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-13, "(动态库)不支持该命令");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-14, "(发给动态库的)命令参数错");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-15, "信息校验和出错");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-20, "卡识别码格式错");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-21, "内部认证失败(用户卡不合法)");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-22, "传入数据与卡内不符");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-23, "传入数据不合法");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-24, "PSAM卡密钥级别不够");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-31, "用户取消密码输入");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-32, "密码输入操作超时");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-33, "输入密码长度错");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-34, "两次输入密码不一致");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-35, "(预留) 初始密码不能交易");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-36, "(预留) 不能改为初始密码");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-41, "运算数据含非法字符");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-42, "运算数据长度错");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-51, "PIN校验失败剩余次数N次根据卡返回信息");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-52, "PIN锁定");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-2201, "无PSAM卡");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-2202, "PSAM卡算法不支持即PSAM卡内没有SSF33算法或SM4算法");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-2203, "PSAM卡内没有RKSSSE密钥3.0卡读个人基本信息需要RKSSSE密钥外部认证");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-2204, "不需要加密机认证");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-25536, "外部认证失败剩余可尝试次数2次");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-25537, "外部认证失败剩余可尝试次数1次");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-25538, "外部认证失败剩余可尝试次数0次");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-26368, "Lc/Le不正确");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-26881, "命令不接受(无效状态)");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27009, "命令与文件结构不相符、当前文件非所需文件");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27010, "不满足安全条件");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27011, "密钥锁定(算法锁定)鉴别方法锁定");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27012, "引用数据无效、随机数无效");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27013, "不满足使用条件、应用被锁定、应用未选择、余额上溢");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27016, "安全报文数据项不正确、MAC不正确");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27264, "数据域参数不正确");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27265, "不支持该功能、卡中无MF、卡被锁定、应用锁定");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27266, "未找到文件、文件标识相重、SFI不正确");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27267, "未找到记录");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-27272, "未找到引用数据、未找到密钥");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-37634, "MAC无效");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-37635, "应用已被永久锁定、卡片锁定");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-37891, "PSAM卡不支持消费交易");
SOCIAL_SECURITYCARD_ERROR_CODE.put(-37894, "所需MAC或/和TAC不可用");
}
public static final Map<Integer, String> KEYPAD_ERROR_CODE;
static {
KEYPAD_ERROR_CODE = new HashMap<>();
KEYPAD_ERROR_CODE.put(400, "按键未找到");
KEYPAD_ERROR_CODE.put(401, "不支持的模式");
KEYPAD_ERROR_CODE.put(402, "访问拒绝");
KEYPAD_ERROR_CODE.put(403, "ID无效");
KEYPAD_ERROR_CODE.put(404, "Key已存在");
KEYPAD_ERROR_CODE.put(406, "Key没有加载");
KEYPAD_ERROR_CODE.put(407, "Key使用不对");
KEYPAD_ERROR_CODE.put(408, "键盘中没有密码");
KEYPAD_ERROR_CODE.put(409, "无效的Key长度");
KEYPAD_ERROR_CODE.put(410, "无效的按键");
KEYPAD_ERROR_CODE.put(411, "存在不支持的按键");
KEYPAD_ERROR_CODE.put(412, "没有激活任何按键");
KEYPAD_ERROR_CODE.put(413, "无效的key");
KEYPAD_ERROR_CODE.put(414, "没有终止键");
KEYPAD_ERROR_CODE.put(415, "最小密码长度无效");
KEYPAD_ERROR_CODE.put(416, "协议不支持");
KEYPAD_ERROR_CODE.put(417, "无效的数据");
KEYPAD_ERROR_CODE.put(418, "不允许进行操作");
KEYPAD_ERROR_CODE.put(419, "Key RAM空间不够");
KEYPAD_ERROR_CODE.put(420, "还没开始ChipTransaction");
KEYPAD_ERROR_CODE.put(421, "算法不支持");
KEYPAD_ERROR_CODE.put(422, "格式不支持");
KEYPAD_ERROR_CODE.put(423, "HSMSTATE无效");
KEYPAD_ERROR_CODE.put(424, "MAC无效");
KEYPAD_ERROR_CODE.put(425, "协议无效");
KEYPAD_ERROR_CODE.put(426, "格式无效");
KEYPAD_ERROR_CODE.put(427, "内容无效");
KEYPAD_ERROR_CODE.put(-999, "找不到设备服务,原因设备服务名不存在");
KEYPAD_ERROR_CODE.put(-998, "无法连接到设备服务,或设备服务未打开");
KEYPAD_ERROR_CODE.put(-1, "应用已经调用过WFSStartUp并且之后并未调用WFSCleanUp");
KEYPAD_ERROR_CODE.put(-2, "应用要求的XFS API版本过高");
KEYPAD_ERROR_CODE.put(-3, "应用要求的XFS API版本过低");
KEYPAD_ERROR_CODE.put(-4, "请求被取消");
KEYPAD_ERROR_CODE.put(-5, "hKey并未对应当前打开的key(注册表项)");
KEYPAD_ERROR_CODE.put(-6, "在当前的key中不存在指定的lpszValueName(键名)");
KEYPAD_ERROR_CODE.put(-7, "在当前的key中不存在指定的lpszSubKey(子键)");
KEYPAD_ERROR_CODE.put(-8, "在当前的key中不存在指定的键值");
KEYPAD_ERROR_CODE.put(-9, "待删除的key包含子键,无法删除");
KEYPAD_ERROR_CODE.put(-10, "返回的键名字符串的长度超过了缓存的长度");
KEYPAD_ERROR_CODE.put(-11, "本项已经为最后一项,没有下一项");
KEYPAD_ERROR_CODE.put(-12, "返回的键值数据的长度超过了缓存的长度");
KEYPAD_ERROR_CODE.put(-13, "函数必须的设备访问,但设备尚未准备好或已超时");
KEYPAD_ERROR_CODE.put(-14, "函数必须的设备访问,但设备出错");
KEYPAD_ERROR_CODE.put(-15, "XFS子系统内发生的内部不一致或其他异常错误");
KEYPAD_ERROR_CODE.put(-16, "lpvOriginal地址参数错误,未指向上次申请的缓存");
KEYPAD_ERROR_CODE.put(-17, "无效的应用句柄(例如由不当的函数调用顺序引起)");
KEYPAD_ERROR_CODE.put(-18, "lpvData参数未指向一个申请的缓存结构");
KEYPAD_ERROR_CODE.put(-19, "当前的服务类(service class)不支持dwCategory参数");
KEYPAD_ERROR_CODE.put(-20, "当前的服务类不支持发出的dwCommand");
KEYPAD_ERROR_CODE.put(-21, "当前的服务类不支持dwEventClass指定的一个或多个事件类");
KEYPAD_ERROR_CODE.put(-22, "hService参数是无效的服务句柄");
KEYPAD_ERROR_CODE.put(-23, "hProvider参数是无效的Provier句柄");
KEYPAD_ERROR_CODE.put(-24, "hWnd参数是无效的窗口句柄");
KEYPAD_ERROR_CODE.put(-25, "hWndReg参数是无效的窗口句柄");
KEYPAD_ERROR_CODE.put(-26, "指针参数不指向可访问内存");
KEYPAD_ERROR_CODE.put(-27, "RequestID参数不符合对服务的未处理请求");
KEYPAD_ERROR_CODE.put(-28, "lpResult参数不是一个指向WFSRESULT结构的指针");
KEYPAD_ERROR_CODE.put(-29, "SP的文件无效或损坏");
KEYPAD_ERROR_CODE.put(-30, "hWnd与usTimerID没有对应当前活动的timer");
KEYPAD_ERROR_CODE.put(-31, "dwTraceLevel参数的值为无效值");
KEYPAD_ERROR_CODE.put(-32, "服务被另一个hService锁定");
KEYPAD_ERROR_CODE.put(-33, "调用被阻塞");
KEYPAD_ERROR_CODE.put(-34, "SP的文件不存在");
KEYPAD_ERROR_CODE.put(-35, "指定的线程不存在");
KEYPAD_ERROR_CODE.put(-36, "timer创建失败");
KEYPAD_ERROR_CODE.put(-37, "应用请求解锁,而此设备并未被加锁");
KEYPAD_ERROR_CODE.put(-38, "XFS卸载SP DLL失败");
KEYPAD_ERROR_CODE.put(-39, "应用程序此前还没有成功的执行WFSStartUp");
KEYPAD_ERROR_CODE.put(-40, "hWndReg窗口没有指定接收任何事件类");
KEYPAD_ERROR_CODE.put(-41, "在该线程上有一个阻塞操作正在进行此时只允许WFSCancelBlockingCall和WFSIsBlocking");
KEYPAD_ERROR_CODE.put(-42, "申请的空间不足");
KEYPAD_ERROR_CODE.put(-43, "逻辑名无效");
KEYPAD_ERROR_CODE.put(-44, "SPI版本过高");
KEYPAD_ERROR_CODE.put(-45, "SPI版本过低");
KEYPAD_ERROR_CODE.put(-46, "应用请求的服务版本过高");
KEYPAD_ERROR_CODE.put(-47, "应用请求的服务版本过低");
KEYPAD_ERROR_CODE.put(-48, "超时周期到期");
KEYPAD_ERROR_CODE.put(-49, "dwCategory虽然有效,但不被SP支持");
KEYPAD_ERROR_CODE.put(-50, "尽管发出的dwCommand对此服务类别有效但此服务提供程序或设备不支持发出的dwCommand");
KEYPAD_ERROR_CODE.put(-51, "服务中的两个模块版本不匹配");
KEYPAD_ERROR_CODE.put(-52, "输入参数中包含无效数据");
KEYPAD_ERROR_CODE.put(-53, "函数必须的配置信息访问,但软件出错");
KEYPAD_ERROR_CODE.put(-54, "与服务的连接丢失");
KEYPAD_ERROR_CODE.put(-55, "用户在阻止设备的正确操作");
KEYPAD_ERROR_CODE.put(-56, "尽管作为输入参数传递的数据结构对此服务类别有效,但此服务提供程序或设备不支持该数据结构");
KEYPAD_ERROR_CODE.put(-57, "一些设备能够识别试图欺骗获得重要信息或媒介的恶意物理攻击。在这些情况下,将返回此错误代码,显示用户试图对该设备采取欺骗行为");
KEYPAD_ERROR_CODE.put(-58, "此时被请求的操作无效,或在设备当时的状态下,被请求的操作无效");
}
public static final Map<Integer, String> MS439_PRINT_ERROR_CODE;
static {
MS439_PRINT_ERROR_CODE = new HashMap<>();
MS439_PRINT_ERROR_CODE.put(-100, "FORM未找到");
MS439_PRINT_ERROR_CODE.put(-101, "FIELD未找到");
MS439_PRINT_ERROR_CODE.put(-102, "缺纸");
MS439_PRINT_ERROR_CODE.put(-103, "设备不支持读操作");
MS439_PRINT_ERROR_CODE.put(-104, "不支持FLUSH");
MS439_PRINT_ERROR_CODE.put(-105, "Media超域");
MS439_PRINT_ERROR_CODE.put(-106, "Field语法错误");
MS439_PRINT_ERROR_CODE.put(-107, "处理Field时发生错误");
MS439_PRINT_ERROR_CODE.put(-108, "Media未找到");
MS439_PRINT_ERROR_CODE.put(-109, "设备无法获得范围");
MS439_PRINT_ERROR_CODE.put(-110, "无效的Media");
MS439_PRINT_ERROR_CODE.put(-111, "无效的Form");
MS439_PRINT_ERROR_CODE.put(-112, "无效的Field");
MS439_PRINT_ERROR_CODE.put(-113, "Media错误");
MS439_PRINT_ERROR_CODE.put(-114, "回收箱满");
MS439_PRINT_ERROR_CODE.put(-115, "STACKE满");
MS439_PRINT_ERROR_CODE.put(-116, "设备不能翻页");
MS439_PRINT_ERROR_CODE.put(-117, "设备不能翻转介质");
MS439_PRINT_ERROR_CODE.put(-118, "Shutter执行命令失败");
MS439_PRINT_ERROR_CODE.put(-119, "卡纸");
MS439_PRINT_ERROR_CODE.put(-120, "文件读取失败");
MS439_PRINT_ERROR_CODE.put(-121, "字符集不一致");
MS439_PRINT_ERROR_CODE.put(-122, "卡纸");
MS439_PRINT_ERROR_CODE.put(-123, "缺纸");
MS439_PRINT_ERROR_CODE.put(-124, "墨水用完");
MS439_PRINT_ERROR_CODE.put(-125, "色带用完");
MS439_PRINT_ERROR_CODE.put(-126, "成象灯不可操作");
MS439_PRINT_ERROR_CODE.put(-127, "资源无效");
MS439_PRINT_ERROR_CODE.put(-128, "命令序列不对");
MS439_PRINT_ERROR_CODE.put(-129, "介质的长度不对");
}
}

View File

@@ -0,0 +1,27 @@
package com.dpkj.common.constant;
/**
* 利盟台式机 设备调用逻辑名
*/
public interface LexMarkConst {
/**
* 身份证读取
*/
String ID_CARD_READ = "RFIDCardReader";
/**
* 社保卡读取
*/
String CARD_READER = "CardReader";
/**
* 打印机
*/
String RECEIPT_PRINTER = "ReceiptPrinter";
/**
* 数字键盘
*/
String ENCRYPTOR = "Encryptor";
/**
* 扫描仪
*/
String BARCODE_READER = "BarcodeReader";
}

View File

@@ -0,0 +1,120 @@
package com.dpkj.common.constant;
/**
* 利盟台式机 中间件 方法名常量
*/
public interface MiddlewareFunctionsConst {
/**-------------------- 公共模块 ---------------------*/
/**
* 打开设备(异步)/ (同步)
*/
String OPEN_CONNECTION = "OpenConnection";
String OPEN_CONNECTION_SYNC = "OpenConnectionSync";
/**
* 关闭设备(异步)/ (同步)
*/
String CLOSE_CONNECTION = "CloseConnection";
String CLOSE_CONNECTION_SYNC = "CloseConnectionSync";
/**
* 复位(初始化)
*/
String RESET = "Reset";
/** ------------------- 读卡模块 --------------------*/
/**
* 进卡读卡(异步)/ (同步)
*/
String ACCEPT_AND_READ_TRACKS = "AcceptAndReadTracks";
String ACCEPT_AND_READ_TRACKS_SYNC = "AcceptAndReadTracksSync";
/**
* 同步取消进卡
*/
String CANCEL_ACCEPT = "CancelAccept";
/**
* 异步ChipIC卡片数据交互 / 同步ChipIC卡片数据交互
*/
String CHIP_IO = "ChipIo";
String CHIP_IO_SYNC = "ChipIoSync";
/**
* IC卡上电
*/
String CHIPPOWER = "ChipPower";
/**
* 退卡
*/
String EJECT = "Eject";
/**
* 获取属性
*/
String GET_CAPABILITIES = "GetCapabilities";
/**
* 获取状态 同步/异步
*/
String GET_STATUS = "GetStatus";
String GET_STATUS_SYNC = "GetStatusSync";
/**
* 社保卡读取 读基本信息
*/
String I_READ_CARD_BAS = "iReadCardBas";
/**
* 社保卡读取 基于加密机的读基本信息
*/
String I_READ_CARD_BAS_HSM_STEP1 = "iReadCardBas_HSM_Step1";
/**
* 社保卡读取 基于加密机的读基本信息
*/
String I_READ_CARD_BAS_HSM_STEP2 = "iReadCardBas_HSM_Step2";
/** -------------------- 数字键盘模块 -----------------*/
/**
* 明文输入
*/
String INPUT_DATA = "InputData";
/**
* 密文输入
*/
String INPUT_PIN = "InputPIN";
/**
* 取消输入 (明文或者密文)
*/
String CANCEL_INPUT = "CancelInput";
/** ------------------ 条码读取模块 -------------------*/
/**
* 扫描条码
*/
String SCAN_BARCODE = "ScanBarcode";
/**
* 取消扫描
*/
String CANCEL_SCAN = "CancelScan";
/**
* 获取扫描仪状态
*/
String GET_SCANNER_STATUS = "GetStatus";
/**
* 获取能力
*/
String GET_SCANNER_CAPABILITIES = "GetCapabilities";
}

View File

@@ -0,0 +1,63 @@
package com.dpkj.common.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
/**
* 利盟请求参数DTO
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-02-08 10:46:44
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class LexMarkDTO implements Serializable {
/**
* 默认异步
* 0:代表同步1:代表异步3:代表属性
*/
private Integer methodType = 1;
/**
* 默认com.gwi.device
* 插件名称如果是设备调用则固定填写com.gwi.device如果是插件调用此处填
* 写插件动态库文件名去掉文件名前面的lib和文件后缀名GWI_Plugin
*/
private String pluginName = "com.gwi.device";
/**
* 固定填写execute
*/
private String pluginMethod = "execute";
/**
* 设备名。如果是设备调用则填写设备名称需要和SP服务配置中的 DevList.ini 配置的设备名称一致;
* 如果是插件调用,可忽略此参数
*/
private String devName;
/**
* 调用id应用生成用于区分不同的调用
*/
private Integer callID;
/**
* 调用的函数名称,设备执行的动作名称,简称动作名
*/
private String actionName;
/**
* 设备执行的输入参数格式为json字符串注意此处为字符串不是json对象
*/
private String param;
}

View File

@@ -0,0 +1,115 @@
package com.dpkj.common.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
/**
* 利盟接口返回值DTO
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-02-08 11:03:06
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class LexMarkResultDTO <T> implements Serializable {
/**
* 对应发送请求中的devName
*/
private String devName;
/**
* 事件名由发送请求中的actionName+Over组成。
*/
private String msgName;
/**
* 对应发送请求的callID。
*/
private int callID;
/**
* 错误码0表示成功其他表示失败比如-4表示取消-48表示超时-14表示硬件故障
*/
private int result;
/**
* 发送请求中的actionName
*/
private String cmdName;
/**
* 详情描述
*/
private String desc;
/**
* 利盟服务返回的参数
*/
@JsonIgnore
private String param;
/**
* 实际返回的接受的参数对象通过param转换过后的
*/
private T data;
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Param {
/**
* 请求ID
*/
private int RequestID;
/**
* 命令编码
*/
private int dwCommandCode;
/**
* 事件名由发送请求中的actionName+Over组成。
*/
private String eventName;
/**
* 事件类型编码
*/
private int eventType;
/**
* 服务
*/
private int hService;
/**
* 对应发送请求中的devName
*/
private String cmdName;
/**
* 错误码0表示成功其他表示失败比如-4表示取消-48表示超时-14表示硬件故障
*/
private int result;
/**
* 详情描述
*/
private String desc;
}
}

View File

@@ -0,0 +1,58 @@
package com.dpkj.common.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
/**
* 利盟接口返回值DTO
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-02-08 11:03:06
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class LexMarkResultVO<T> implements Serializable {
/**
* 对应发送请求中的devName
*/
private String devName;
/**
* 事件名由发送请求中的actionName+Over组成。
*/
private String msgName;
/**
* 对应发送请求的callID。
*/
private int callID;
/**
* 错误码0表示成功其他表示失败比如-4表示取消-48表示超时-14表示硬件故障
*/
private int result;
/**
* 发送请求中的actionName
*/
private String cmdName;
/**
* 返回参数
*/
private T param;
/**
* 详情描述
*/
private String desc;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,350 @@
package com.dpkj.common.utils;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.exception.RRException;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.oned.Code128Writer;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.client.j2se.MatrixToImageWriter;
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 TemplateUtils {
/**
* 生成小票图片
*
* @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 > 2 ? Integer.parseInt(split[3]) : 100;
int height = split.length > 3 ? 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;
}
private String generateQRCode(int type, String content, int width, int height) {
try {
BufferedImage qrImage = null;
BitMatrix bitMatrix = null;
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 设置字符编码为 UTF-8
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // 设置纠错级别
// 设置外边距为 0 以去除白边
hints.put(EncodeHintType.MARGIN, 0);
if (type == 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) {
Code128Writer barcodeWriter = new Code128Writer();
bitMatrix = barcodeWriter.encode(content, BarcodeFormat.CODE_128, width, height, hints);
qrImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
}
if ( qrImage == null ){
throw new RRException("图片渲染失败");
}
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;
}
}
}

View File

@@ -0,0 +1,205 @@
package com.dpkj.common.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.dto.LexMarkDTO;
import com.dpkj.common.dto.LexMarkResultDTO;
import com.dpkj.common.exception.RRException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* 第三方服务,主要是调用打印机等
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-02-08 10:45:01
*/
@Slf4j
@Component
public class ThirdService {
@Value("${app.custom.lexMarkServiceIp}")
private String lexMarkServiceIp;
@Value("${app.custom.lexMarkServicePort}")
private String lexMarkServicePort;
private static final Map<String, String> devNameMap = new HashMap<>();
static {
devNameMap.put("HtmPrinter", "激光打印机");
devNameMap.put("ReceiptPrinter", "凭条打印机");
}
/**
* 利盟台式机-立体机 接口请求
*
* @param lexMarkDTO 请求DTO
* @param clazz 返回类型
* @return result
*/
public <T> LexMarkResultDTO<T> callDevice(LexMarkDTO lexMarkDTO, Class<T> clazz) {
try {
URL url = new URL(lexMarkServiceIp + ":" + lexMarkServicePort + "/CallDevice");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json");
// 将LexMarkDTO对象转换为JSON字符串
String jsonInputString = JSON.toJSONString(lexMarkDTO);
try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
wr.write(input, 0, input.length);
log.info(jsonInputString);
}
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
log.error("利盟服务请求失败,响应码:{},请求参数:{}", responseCode, lexMarkDTO);
throw new RRException("利盟服务请求失败,响应码:" + responseCode);
}
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// 将响应JSON字符串转换为LexMarkResultDTO对象
LexMarkResultDTO<T> lexMarkResultDTO = JSON.parseObject(response.toString(), LexMarkResultDTO.class);
// if (lexMarkResultDTO.getResult() != 0) {
// log.error("利盟服务请求出错:{}", lexMarkResultDTO);
// throw new RRException(lexMarkResultDTO.toString());
// }
// 实例化param位data
String param = lexMarkResultDTO.getParam();
if (!StringUtils.isEmpty(param)) {
try {
T t = JSON.parseObject(param, clazz);
lexMarkResultDTO.setData(t);
}catch (Exception e){
log.error("类型转换失败");
throw new RRException("转换param位data时类型与实际类型不匹配");
}
}
return lexMarkResultDTO;
} catch (Exception e) {
log.error("利盟服务请求失败:{}", lexMarkDTO, e);
if (e instanceof RRException) {
throw new RRException(((RRException) e).getCode(), e.getMessage());
}
throw new RRException("利盟服务请求失败");
}
}
/**
* 数据对象的data采用JSONObject进行接收
*
* @param lexMarkDTO 请求参数
* @return result
*/
public LexMarkResultDTO<JSONObject> callDevice(LexMarkDTO lexMarkDTO) {
return callDevice(lexMarkDTO, JSONObject.class);
}
/**
* 打开设备连接或者 链接+初始化
* @param devName 设备名称
* @param resterType 初始化类型,如果不设置,那么就不会进行初始化
*/
public void open(String devName, Integer resterType) {
// 优先自动连接对应的设备
LexMarkDTO lexMarkDTO = new LexMarkDTO();
lexMarkDTO.setActionName("OpenConnection");
lexMarkDTO.setCallID(19283);
lexMarkDTO.setDevName(devName);
lexMarkDTO.setPluginMethod("exec");
JSONObject param = new JSONObject();
param.put("", String.format("{\"ServiceName\":\"%s\",\"TimeOut\":90000}", devName));
lexMarkDTO.setParam(param.toString());
LexMarkResultDTO<JSONObject> jsonObjectLexMarkResultDTO = this.callDevice(lexMarkDTO, JSONObject.class);
JSONObject data = jsonObjectLexMarkResultDTO.getData();
if (data != null) {
if (!String.valueOf(data.get("result")).equals("0")) {
throw new RRException("设备: " + devNameMap.get(devName) + " 打开失败!");
}
}
// 打开后直接进行重置
if ( resterType != null && data != null) {
lexMarkDTO.setActionName("Reset");
JSONObject jsonObject = new JSONObject();
jsonObject.put("", String.format("{\"ResetAction\":%d,\"binNumber\":0}", resterType));
lexMarkDTO.setParam(jsonObject.toString());
LexMarkResultDTO<JSONObject> resultDTO = this.callDevice(lexMarkDTO, JSONObject.class);
JSONObject data1 = resultDTO.getData();
if (data1 != null) {
if (!String.valueOf(data1.get("result")).equals("0")) {
throw new RRException("设备: " + devNameMap.get(devName) + " _ " + resterType + " 初始化失败!");
}
}
}
}
/**
* 关闭设备练级
* @param devName 设备名称
*/
public void close(String devName) {
LexMarkDTO lexMarkDTO = new LexMarkDTO();
lexMarkDTO.setActionName("CloseConnection");
lexMarkDTO.setCallID(19283);
lexMarkDTO.setDevName(devName);
lexMarkDTO.setPluginMethod("exec");
LexMarkResultDTO<JSONObject> jsonObjectLexMarkResultDTO = this.callDevice(lexMarkDTO, JSONObject.class);
JSONObject data = jsonObjectLexMarkResultDTO.getData();
if (data != null) {
if (!String.valueOf(data.get("result")).equals("0")) {
throw new RRException("设备: " + devNameMap.get(devName) + " 关闭失败!");
}
}
}
/**
* 切纸但是目前凭条和ms439打印机都是可以自动切纸的
* @param devName 设备名称
* @param actionName action名称实际的动作函数名称
* @param mediaType 媒介类型
*/
public void cutPaper(String devName, String actionName, Integer mediaType){
LexMarkDTO lexMarkDTO = new LexMarkDTO();
lexMarkDTO.setActionName(actionName);
lexMarkDTO.setCallID(19283);
lexMarkDTO.setDevName(devName);
lexMarkDTO.setPluginMethod("exec");
JSONObject param = new JSONObject();
param.put("", String.format("{\"mediaCtrol\":%d}", mediaType));
lexMarkDTO.setParam(param.toString());
LexMarkResultDTO<JSONObject> jsonObjectLexMarkResultDTO = this.callDevice(lexMarkDTO, JSONObject.class);
JSONObject data = jsonObjectLexMarkResultDTO.getData();
if (data != null) {
if (!String.valueOf(data.get("result")).equals("0")) {
throw new RRException("设备: " + devNameMap.get(devName) + " 切纸失败!");
}
}
}
}

View File

@@ -0,0 +1,87 @@
package com.dpkj.common.utils;
import com.alibaba.fastjson.JSON;
import com.dpkj.common.dto.LexMarkDTO;
import com.dpkj.common.dto.LexMarkResultDTO;
import com.dpkj.common.dto.LexMarkResultVO;
import com.dpkj.common.exception.RRException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
/**
* 第三方服务,主要是调用打印机等
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-02-08 10:45:01
*/
@Slf4j
@Component
public class ThirdServiceUtil {
@Value("${app.custom.lexMarkServiceIp}")
private String lexMarkServiceIp;
@Value("${app.custom.lexMarkServicePort}")
private String lexMarkServicePort;
/**
* 利盟台式机-立体机 接口请求
* @param lexMarkDTO 请求DTO
* @return result
*/
public <T> T callDevice(LexMarkDTO lexMarkDTO, Class<T> responseType) {
try {
String urlStr = lexMarkServiceIp + ":" + lexMarkServicePort + "/CallDevice";
URL url = new URL(urlStr);
log.info("开始请求利盟服务URL: {}, 请求体: {}", urlStr, JSON.toJSONString(lexMarkDTO));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json");
// 将 LexMarkDTO 对象转换为 JSON 字符串
String jsonInputString = JSON.toJSONString(lexMarkDTO);
try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
wr.write(input, 0, input.length);
}
int responseCode = connection.getResponseCode();
log.info("利盟服务响应码: {}", responseCode);
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new RRException("利盟服务请求失败,响应码:" + responseCode);
}
// 指定编码为 UTF-8
try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
String responseStr = response.toString();
log.info("利盟服务响应内容: {}", responseStr);
// 将响应 JSON 字符串转换为泛型对象
T result = JSON.parseObject(responseStr, responseType);
if (result instanceof LexMarkResultVO && ((LexMarkResultVO) result).getResult() != 0) {
throw new RRException(result.toString());
}
return result;
}
} catch (Exception e) {
log.error("利盟服务请求发生异常", e);
if (e instanceof RRException) {
throw new RRException(((RRException) e).getCode(), e.getMessage());
}
throw new RRException("利盟服务请求失败");
}
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,34 @@
package com.dpkj.modules.barcode.controller;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.barcode.service.BarCodeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @BelongsProject: 银医通-澜沧中医院-DLL-台式机
* @BelongsPackage: com.dpkj.modules.keypad.controller
* @Author: wzc
* @Description: 条码读取
* @CreateTime: 2025-02-10 15:21
*/
@Slf4j
@RestController
@RequestMapping("barcode")
public class BarCodeController {
@Autowired
private BarCodeService barcodeService;
/**
* 条码扫描
*
* @return
*/
@PostMapping("scanBarcode")
public Result scanBarcode() {
return barcodeService.scanBarcode();
}
}

View File

@@ -0,0 +1,8 @@
package com.dpkj.modules.barcode.service;
import com.dpkj.common.vo.Result;
public interface BarCodeService {
Result scanBarcode();
}

View File

@@ -0,0 +1,61 @@
package com.dpkj.modules.barcode.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.constant.LexMarkConst;
import com.dpkj.common.constant.MiddlewareFunctionsConst;
import com.dpkj.common.dto.LexMarkDTO;
import com.dpkj.common.utils.ThirdServiceUtil;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.barcode.service.BarCodeService;
import com.dpkj.modules.readcard.vo.IDCardReadResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @BelongsProject: 银医通-澜沧中医院-DLL-台式机
* @BelongsPackage: com.dpkj.modules.keypad.service.impl
* @Author: wzc
* @Description:
* @CreateTime: 2025-02-10 15:20
*/
@Slf4j
@Service
public class BarCodeServiceImpl implements BarCodeService {
@Autowired
private ThirdServiceUtil thirdServiceUtil;
@Override
public Result scanBarcode() {
// 连接扫描仪
LexMarkDTO connect = new LexMarkDTO();
connect.setDevName(LexMarkConst.BARCODE_READER);
connect.setActionName(MiddlewareFunctionsConst.OPEN_CONNECTION);
connect.setCallID(1000);
JSONObject connectParam = new JSONObject();
connectParam.put("TimeOut", 90000);
connectParam.put("ServiceName", LexMarkConst.BARCODE_READER);
connect.setParam(connectParam.toJSONString());
IDCardReadResultVO connectResult = thirdServiceUtil.callDevice(connect, IDCardReadResultVO.class);
if (connectResult.getResult() == 0) {
// 扫描条码
LexMarkDTO scanBarcode = new LexMarkDTO();
scanBarcode.setDevName(LexMarkConst.BARCODE_READER);
scanBarcode.setActionName(MiddlewareFunctionsConst.SCAN_BARCODE);
scanBarcode.setCallID(1000);
JSONObject scanBarcodeParam = new JSONObject();
scanBarcodeParam.put("BarcodeDataMode", 0);
scanBarcodeParam.put("TimeOut", 0);
scanBarcode.setParam(scanBarcodeParam.toJSONString());
IDCardReadResultVO scanBarcodeResult = thirdServiceUtil.callDevice(scanBarcode, IDCardReadResultVO.class);
if (scanBarcodeResult.getResult() == 0) {
return Result.ok(scanBarcodeResult);
} else {
return Result.error("扫描异常!详情:" + connectResult.getDesc());
}
} else {
return Result.error("扫描仪设备连接失败!详情:" + connectResult.getDesc());
}
}
}

View File

@@ -0,0 +1,81 @@
package com.dpkj.modules.chs.controller;
import cn.hutool.core.lang.Console;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.config.ChsConfig;
import com.dpkj.common.constant.ChsConst;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.chs.dll.AlipayDll;
import com.dpkj.modules.chs.entity.AlipayEcRequestData;
import com.dpkj.modules.chs.service.IAlipayService;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.RestController;
/**
* @Auther: 萧道子
* @Date: 2025/3/22 16:25
* @Description: 医保模块-阿里设备
*/
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/chs/alipay")
public class AlipayController {
private final IAlipayService alipayService;
private final ChsConfig charsConfig;
@GetMapping("test")
public Result<?> test() {
try {
AlipayDll.Dll dll = AlipayDll.instance();
// Native.load("libeay32", AlipayDll.Dll.class);
// Native.load("ssleay32", AlipayDll.Dll.class);
JSONObject data = new JSONObject();
AlipayEcRequestData alipayEcRequestData = new AlipayEcRequestData()
.setOrgId(charsConfig.getOrgcode())
.setTransType(ChsConst.APITYPE_DECODE)
.setData(data);
String dataJson = JSONObject.toJSONString(alipayEcRequestData);
Pointer resultStr = new Memory(1024 * 10);
String rs = dll.NationEcTrans(
"http://172.16.11.13:5946/api/chs/qrCodeQuery",
dataJson,
resultStr
);
String rsStrString = resultStr.getString(0, "GB18030");
Console.log(rsStrString);
Console.log(rs);
return Result.ok("成功", rsStrString);
} catch (Exception e) {
e.printStackTrace();
return Result.error("失败");
}
}
@PostMapping("qrCodeQuery")
public Result<?> qrCodeQuery(@RequestBody JSONObject data) {
try {
Console.log(data);
return Result.ok("成功", null);
} catch (Exception e) {
e.printStackTrace();
return Result.error("失败");
}
}
}

View File

@@ -0,0 +1,50 @@
package com.dpkj.modules.chs.controller;
import cn.hutool.core.lang.Console;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.config.ChsConfig;
import com.dpkj.common.constant.ChsConst;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.chs.dll.AlipayDll;
import com.dpkj.modules.chs.entity.AlipayEcRequestData;
import com.dpkj.modules.chs.service.IAlipayService;
import com.dpkj.modules.chs.service.IHispayService;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.RestController;
/**
* @Auther: 萧道子
* @Date: 2025/3/22 16:25
* @Description: 医保模块-HIS医保
*/
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/chs/hispay")
public class HispayController {
private final IHispayService hispayService;
private final ChsConfig charsConfig;
@GetMapping("test")
public Result<?> test() {
try {
return Result.ok("成功", null);
} catch (Exception e) {
e.printStackTrace();
return Result.error("失败");
}
}
}

View File

@@ -0,0 +1,59 @@
package com.dpkj.modules.chs.dll;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
/**
* @Auther: 萧道子
* @Date: 2025/3/22 17:48
* @Description:
*/
@Slf4j
public class AlipayDll {
/**
* 获取 AlipayDll 实例,同时注册 AlipayDll 控件。
*
* @return AlipayDll 实例
* @throws DllRegistrationException 如果注册控件失败,抛出此异常
*/
public static Dll instance() throws DllRegistrationException {
try {
return Native.load("NationECCode", Dll.class);
} catch (UnsatisfiedLinkError e) {
log.info("[AlipayDll][getPrintSDK][医保动态库] SDK注册失败{}", e.getMessage());
throw new DllRegistrationException("Failed to load AlipayDll library: ", e);
}
}
/**
* 定义自定义异常类,用于表示注册控件时发生的错误
*/
public static class DllRegistrationException extends Exception {
public DllRegistrationException(String message) {
super(message);
}
public DllRegistrationException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* 定义接口映射本地库中的函数。
*/
public interface Dll extends Library {
/**
* 设置打印端口和波特率。
*
* @return 返回操作结果代码
*/
String NationEcTrans(String strUrl, String InData, Pointer OutData);
}
}

View File

@@ -0,0 +1,59 @@
package com.dpkj.modules.chs.dll;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
/**
* @Auther: 萧道子
* @Date: 2025/3/22 17:48
* @Description:
*/
@Slf4j
public class HispayDll {
/**
* 获取 AlipayDll 实例,同时注册 AlipayDll 控件。
*
* @return AlipayDll 实例
* @throws DllRegistrationException 如果注册控件失败,抛出此异常
*/
public static Dll instance() throws DllRegistrationException {
try {
return Native.load("PayClient", Dll.class);
} catch (UnsatisfiedLinkError e) {
log.info("[HispayDll][getPrintSDK][医保动态库] SDK注册失败{}", e.getMessage());
throw new DllRegistrationException("Failed to load AlipayDll library: ", e);
}
}
/**
* 定义自定义异常类,用于表示注册控件时发生的错误
*/
public static class DllRegistrationException extends Exception {
public DllRegistrationException(String message) {
super(message);
}
public DllRegistrationException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* 定义接口映射本地库中的函数。
*/
public interface Dll extends Library {
/**
* 设置打印端口和波特率。
*
* @return 返回操作结果代码
*/
String NationEcTrans(String strUrl, String InData, Pointer OutData);
}
}

View File

@@ -0,0 +1,44 @@
package com.dpkj.modules.chs.entity;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @Auther: 萧道子
* @Date: 2025/3/23 11:56
* @Description: 医保请求
*/
@Data
@Accessors(chain = true)
public class AlipayEcRequestData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 机构 ID 必填
*/
private String orgId;
/**
* 交易类型 必填
* ec.query: 电子凭证二维码解码接口
* cn.nhsa.qrcode.get: 终端医保电子凭证码解码接口
* cn.nhsa.auth.check刷脸授权获取医保身份接口
* cn.nhsa.ec.pwd 医保电子凭证密码核验接口
*/
private String transType;
/**
* 接口请求参数 JSON格式字符串 必填
*/
private JSONObject data;
/**
* 扩展参数 JSON格式字符串
*/
private JSONObject extra;
}

View File

@@ -0,0 +1,4 @@
package com.dpkj.modules.chs.service;
public interface IAlipayService {
}

View File

@@ -0,0 +1,4 @@
package com.dpkj.modules.chs.service;
public interface IHispayService {
}

View File

@@ -0,0 +1,38 @@
package com.dpkj.modules.chs.service.impl;
import cn.hutool.core.lang.Console;
import com.dpkj.modules.chs.dll.AlipayDll;
import com.dpkj.modules.chs.service.IAlipayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
/**
* @Auther: 萧道子
* @Date: 2025/3/22 16:29
* @Description:
*/
@Slf4j
@Service
public class AlipayServiceImpl implements IAlipayService {
private AlipayDll.Dll dll = AlipayDll.instance();
public AlipayServiceImpl() throws AlipayDll.DllRegistrationException {
}
@PostConstruct
public void postConstruct() {
log.info("[AlipayServiceImpl][postConstruct][医保DLL] 初始化动态链接库");
initPrinter();
}
private void initPrinter() {
Console.log(dll);
}
}

View File

@@ -0,0 +1,38 @@
package com.dpkj.modules.chs.service.impl;
import cn.hutool.core.lang.Console;
import com.dpkj.modules.chs.dll.HispayDll;
import com.dpkj.modules.chs.service.IHispayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
/**
* @Auther: 萧道子
* @Date: 2025/3/22 16:29
* @Description:
*/
@Slf4j
@Service
public class HispayServiceImpl implements IHispayService {
private HispayDll.Dll dll = HispayDll.instance();
public HispayServiceImpl() throws HispayDll.DllRegistrationException {
}
@PostConstruct
public void postConstruct() {
log.info("[HispayServiceImpl][postConstruct][医保DLL-HIS] 初始化动态链接库");
initPrinter();
}
private void initPrinter() {
Console.log(dll);
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,45 @@
package com.dpkj.modules.keypad.controller;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.keypad.service.KeypadService;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @BelongsProject: 银医通-澜沧中医院-DLL-台式机
* @BelongsPackage: com.dpkj.modules.keypad.controller
* @Author: wzc
* @Description: 数字键盘
* @CreateTime: 2025-02-10 15:21
*/
@Slf4j
@RestController
@RequestMapping("keypad")
public class KeypadController {
@Autowired
private KeypadService keypadService;
/**
* 数字键盘 明文输入
*
* @return
*/
@PostMapping("inputData")
public Result inputData() {
return keypadService.inputData();
}
/**
* 取消输入 (明文或者密文)
* @return
*/
@PostMapping("cancelInput")
public Result CancelInput() {
return keypadService.cancelInput();
}
}

View File

@@ -0,0 +1,9 @@
package com.dpkj.modules.keypad.service;
import com.dpkj.common.vo.Result;
public interface KeypadService {
Result inputData();
Result cancelInput();
}

View File

@@ -0,0 +1,199 @@
package com.dpkj.modules.keypad.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.constant.ErrorCodeConstants;
import com.dpkj.common.constant.LexMarkConst;
import com.dpkj.common.constant.MiddlewareFunctionsConst;
import com.dpkj.common.dto.LexMarkDTO;
import com.dpkj.common.utils.ThirdServiceUtil;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.keypad.service.KeypadService;
import com.dpkj.modules.keypad.util.WebSocketClient;
import com.dpkj.modules.readcard.vo.IDCardReadResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* @BelongsProject: 银医通-澜沧中医院-DLL-台式机
* @BelongsPackage: com.dpkj.modules.keypad.service.impl
* @Author: wzc
* @Description:
* @CreateTime: 2025-02-10 15:20
*/
@Slf4j
@Service
public class KeypadServiceImpl implements KeypadService {
@Autowired
private ThirdServiceUtil thirdServiceUtil;
/**
* 数字键盘 明文输入
* 1. 连接数字键盘
* 2. 明文输入
*
* @return
*/
@Override
public Result inputData() {
Result result = null;
// 获取设备状态
LexMarkDTO status = new LexMarkDTO();
status.setDevName(LexMarkConst.ENCRYPTOR);
status.setCallID(0);
status.setActionName(MiddlewareFunctionsConst.GET_STATUS_SYNC);
JSONObject statusParam = new JSONObject();
statusParam.put("timeout", 0);
log.info("[KeypadServiceImpl][inputData][50]: 获取设备状态,参数: {}", status);
IDCardReadResultVO statusResult = thirdServiceUtil.callDevice(status, IDCardReadResultVO.class);
log.info("[KeypadServiceImpl][inputData][52]: 获取设备状态结果: {}", statusResult);
if (statusResult.getResult() == 0) {
// 连接状态,初始化
LexMarkDTO reset = new LexMarkDTO();
reset.setDevName(LexMarkConst.ENCRYPTOR);
reset.setCallID(0);
reset.setActionName(MiddlewareFunctionsConst.RESET);
JSONObject resetParam = new JSONObject();
resetParam.put("ResetAction", 1);
log.info("[KeypadServiceImpl][inputData][61]: 调用复位(初始化)方法,参数: {}", reset);
IDCardReadResultVO resetResult = thirdServiceUtil.callDevice(reset, IDCardReadResultVO.class);
log.info("[KeypadServiceImpl][inputData][63]: 设备复位(初始化)结果: {}", resetResult);
if (resetResult.getResult() == 0) {
// 复位成功
result = connectAndInputData(result);
} else {
result = Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(resetResult.getResult()));
log.info("[KeypadServiceImpl][inputData][69]: 密码键盘(初始化)异常,详情:{}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(resetResult.getResult()));
}
} else {
// 设备未连接,无需复位(初始化)
result = connectAndInputData(result);
}
log.info("[KeypadServiceImpl][inputData][76]: 密码输入流程结束,最终返回结果: {}", result);
return result;
}
public Result connectAndInputData(Result result){
log.info("[KeypadServiceImpl][connectAndInputData][81]: {}", "开始执行密码输入流程");
// 连接数字键盘
log.info("[KeypadServiceImpl][connectAndInputData][84]: {}", "准备连接数字键盘设备");
LexMarkDTO connect = new LexMarkDTO();
connect.setDevName(LexMarkConst.ENCRYPTOR);
connect.setActionName(MiddlewareFunctionsConst.OPEN_CONNECTION);
connect.setCallID(1000);
JSONObject connectParam = new JSONObject();
connectParam.put("TimeOut", 30000);
connectParam.put("ServiceName", LexMarkConst.ENCRYPTOR);
connect.setParam(connectParam.toJSONString());
log.info("[KeypadServiceImpl][connectAndInputData][93]: {}", "调用设备连接方法,参数: {}", connect);
IDCardReadResultVO connectResult = thirdServiceUtil.callDevice(connect, IDCardReadResultVO.class);
log.info("[KeypadServiceImpl][connectAndInputData][95]: 设备连接结果: {}", connectResult);
WebSocketClient webSocketClient = null;
try {
if (connectResult.getResult() == 0) {
log.info("[KeypadServiceImpl][connectAndInputData][100]: {}", "数字键盘设备连接成功,准备进行明文输入");
// 明文输入
LexMarkDTO plaintext = new LexMarkDTO();
plaintext.setDevName(LexMarkConst.ENCRYPTOR);
plaintext.setActionName(MiddlewareFunctionsConst.INPUT_DATA);
plaintext.setCallID(1000);
JSONObject plaintextParam = new JSONObject();
// 密码最小长度
plaintextParam.put("MINLength", 0);
// 密码最大长度
plaintextParam.put("MAXLength", 6);
// 是否自动结束
plaintextParam.put("bAutoEnd", 1);
// 超时时间
plaintextParam.put("TimeOut", 80000);
plaintextParam.put("ActiveKeys", 81919);
plaintextParam.put("TerminateKeys", 1024);
plaintext.setParam(plaintextParam.toJSONString());
log.info("[KeypadServiceImpl][connectAndInputData][120]: 调用明文输入方法,参数: {}", plaintext);
// 创建 WebSocket 客户端
log.info("[KeypadServiceImpl][connectAndInputData][123]: {}", "创建 WebSocket 客户端,连接地址: ws://127.0.0.1:12347");
webSocketClient = new WebSocketClient("ws://127.0.0.1:12347");
IDCardReadResultVO plaintextResult = thirdServiceUtil.callDevice(plaintext, IDCardReadResultVO.class);
log.info("[KeypadServiceImpl][connectAndInputData][127]: 明文输入操作结果: {}", plaintextResult);
if (plaintextResult.getResult() == 0) {
log.info("[KeypadServiceImpl][connectAndInputData][130]: {}", "明文输入成功,开始等待确认");
try {
// 适当延长等待时间
while (true) {
TimeUnit.SECONDS.sleep(1);
Boolean confirm = webSocketClient.getConfirm();
if (confirm) {
log.info("[KeypadServiceImpl][connectAndInputData][137]: {}", "收到确认信息,结束等待");
break;
}
}
} catch (InterruptedException e) {
log.info("[KeypadServiceImpl][connectAndInputData][142]: 等待确认信息时被中断{}", e);
e.printStackTrace();
}
String password = webSocketClient.getPassword();
log.info("[KeypadServiceImpl][connectAndInputData][146]: 密码输入完成,密码为: {}", password);
result = Result.ok("密码输入完成!", password);
} else {
log.info("[KeypadServiceImpl][connectAndInputData][149]: 密码输入异常!详情:{}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(plaintextResult.getResult()));
result = Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(plaintextResult.getResult()));
}
} else {
log.info("[KeypadServiceImpl][connectAndInputData][154]: 数字键盘设备连接失败!详情:{}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(connectResult.getResult()));
result = Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(connectResult.getResult()));
}
} finally {
if (webSocketClient != null) {
log.info("[KeypadServiceImpl][connectAndInputData][160]: {}", "关闭 WebSocket 客户端");
webSocketClient.close();
}
// 关闭身份证读卡设备
log.info("[KeypadServiceImpl][connectAndInputData][164]: {}", "准备关闭数字键盘设备");
LexMarkDTO close = new LexMarkDTO();
close.setDevName(LexMarkConst.ENCRYPTOR);
close.setCallID(0);
close.setActionName(MiddlewareFunctionsConst.CLOSE_CONNECTION);
log.info("[KeypadServiceImpl][connectAndInputData][169]: 调用设备关闭方法,参数: {}", close);
IDCardReadResultVO closeResult = thirdServiceUtil.callDevice(close, IDCardReadResultVO.class);
log.info("[KeypadServiceImpl][connectAndInputData][171]: 设备关闭结果: {}", closeResult);
if (closeResult.getResult() != 0) {
log.info("[KeypadServiceImpl][connectAndInputData][173]: 数字键盘设备关闭异常!详情:{}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(closeResult.getResult()));
}
}
return result;
}
/**
* 取消输入 (明文或者密文)
* @return
*/
@Override
public Result cancelInput() {
LexMarkDTO cancelInput = new LexMarkDTO();
cancelInput.setDevName(LexMarkConst.ENCRYPTOR);
cancelInput.setActionName(MiddlewareFunctionsConst.CANCEL_INPUT);
cancelInput.setCallID(1000);
cancelInput.setParam("{\"\"}");
IDCardReadResultVO connectResult = thirdServiceUtil.callDevice(cancelInput, IDCardReadResultVO.class);
if (connectResult.getResult() == 0) {
return Result.ok("取消输入 (明文或者密文)完成!");
} else {
return Result.error(connectResult.getDesc());
}
}
}

View File

@@ -0,0 +1,115 @@
package com.dpkj.modules.keypad.util;
import com.alibaba.fastjson.JSONObject;
import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
@ClientEndpoint
public class WebSocketClient {
private Session session;
// 密码
private String password = "";
// 是否确认
private boolean confirm = false;
public WebSocketClient(String uri) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
session = container.connectToServer(this, new URI(uri));
} catch (Exception e) {
e.printStackTrace();
}
}
@OnOpen
public void onOpen(Session session) {
System.out.println("Connected to WebSocket server");
this.session = session;
}
@OnMessage
public void onMessage(String message) {
System.out.println("Received message: " + message);
// 解析外层 JSON 对象
JSONObject outerObj = JSONObject.parseObject(message);
// 获取 "param" 字段对应的 JSON 对象
JSONObject paramObj = outerObj.getJSONObject("param");
// 从 "param" 对象中获取 "Key" 的值
Integer keyValue = paramObj.getInteger("Key");
if (keyValue != null) {
// 更正
if (keyValue == 8) {
password = password.substring(0, password.length() - 1);
// 取消
} else if (keyValue == 27) {
password = "";
// 确认
} else if (keyValue == 13) {
confirm = true;
} else if (keyValue == 2) {
password += "00";
} else {
password += KEYPAD_MAPPING.get("0x" + Integer.toHexString(keyValue));
}
} else {
System.out.println("未找到 Key 字段或其值为空。");
}
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("Connection closed: " + closeReason.getReasonPhrase());
}
public String getPassword() {
return password;
}
public Boolean getConfirm() {
return confirm;
}
public void close() {
try {
if (session != null && session.isOpen()) {
session.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static final Map<String, String> KEYPAD_MAPPING = new HashMap<>();
static {
// 初始化映射关系
KEYPAD_MAPPING.put("0x30", "0");
KEYPAD_MAPPING.put("0x31", "1");
KEYPAD_MAPPING.put("0x32", "2");
KEYPAD_MAPPING.put("0x33", "3");
KEYPAD_MAPPING.put("0x34", "4");
KEYPAD_MAPPING.put("0x35", "5");
KEYPAD_MAPPING.put("0x36", "6");
KEYPAD_MAPPING.put("0x37", "7");
KEYPAD_MAPPING.put("0x38", "8");
KEYPAD_MAPPING.put("0x39", "9");
KEYPAD_MAPPING.put("0x08", "BACKSPACE"); // 更正按键
KEYPAD_MAPPING.put("0x10", "CLEAR");
KEYPAD_MAPPING.put("0x0D", "ENTER"); // 确认按键
KEYPAD_MAPPING.put("0x1B", "CANCEL"); // 取消
KEYPAD_MAPPING.put("0xA1", "HELP"); // 帮助
KEYPAD_MAPPING.put("0x2E", "DECPOINT");
KEYPAD_MAPPING.put("0x02", "00");
}
}

View File

@@ -0,0 +1,39 @@
package com.dpkj.modules.print.controller;
import com.dpkj.common.dto.LexMarkResultDTO;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.print.request.MS439Request;
import com.dpkj.modules.print.service.MS439PrintService;
import com.dpkj.modules.print.vo.PrinterStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* MS439控制层利盟打印机
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-03-03 10:47:06
*/
@RestController
@RequestMapping("/ms439")
public class MS439Controller {
@Autowired
private MS439PrintService ms439PrintService;
@PostMapping("/print")
public Result<LexMarkResultDTO<?>> print(@Validated @RequestBody MS439Request request){
return Result.ok(ms439PrintService.printImage(request));
}
@PostMapping("/getStatus")
public Result<String> print(@RequestParam(defaultValue = "A4") String papersource){
this.ms439PrintService.getStatus(papersource);
return Result.ok();
}
}

View File

@@ -0,0 +1,64 @@
package com.dpkj.modules.print.controller;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.dto.LexMarkResultDTO;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.print.request.ReceiptPrintRequest;
import com.dpkj.modules.print.service.PrintService;
import com.dpkj.modules.print.vo.PrinterStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/**
* 挂号controller
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @since 2025-02-08 11:49:46
*/
@RestController
@RequestMapping("/receipt")
public class RegisterController {
@Resource(name = "registerService")
private PrintService printService;
/**
* 挂号
*/
@PostMapping("/register/{templateName}")
public Result<LexMarkResultDTO> register(@RequestParam String jsonData,
@PathVariable String templateName,
@RequestParam(defaultValue = "600") Integer width,
@RequestParam(defaultValue = "950") Integer height
){
return Result.ok((LexMarkResultDTO)printService.printImage(JSONObject.parseObject(jsonData), templateName, width, height));
}
/**
* 挂号,通过文件路径
*/
@PostMapping("/print")
public Result<LexMarkResultDTO> registerByFilePath(@RequestBody @Validated ReceiptPrintRequest request) throws UnsupportedEncodingException {
// 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\"}";
// 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_2_500_160\":\"maby this is a Base64 code data if has\",\"terminalNumber\":\"12092794\",\"printTime\":\"2025-01-15 13:10:08\"}";
return Result.ok((LexMarkResultDTO)printService.printImage(
JSONObject.parseObject(URLDecoder.decode(request.getTemplateFillData(), "UTF-8")),
request.getTemplateName(),
request.getWidth(),
request.getHeight(),
request.getFileDir()));
}
/**
* 获取小票打印机状态
*/
@PostMapping("/getStatus")
public Result<String> getStatus(){
this.printService.getStatus();
return Result.ok();
}
}

View File

@@ -0,0 +1,69 @@
package com.dpkj.modules.print.enums;
import lombok.Getter;
/**
* ms439打印机设备状态枚举
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-03-06 11:31:25
*/
@Getter
public enum MS439DeviceStatusEnum {
/**
* 打印机正常
*/
HEALTHY(439200, "正常", "HEALTHY"),
/**
* 打印机忙碌,请稍后
*/
BUSY(439201, "打印机忙碌,请稍后", "BUSY"),
/**
* 打印机发生故障
*/
FATAL(439500, "打印机发生故障", "FATAL"),
/**
* 未发现打印机
*/
NODEVICE(439501, "未发现打印机", "NODEVICE"),
;
private final Integer code;
private final String msg;
private final String printCode;
MS439DeviceStatusEnum(Integer code, String msg, String printCode) {
this.code = code;
this.msg = msg;
this.printCode = printCode;
}
public static String getMessage(String printCode){
for (MS439DeviceStatusEnum value : MS439DeviceStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.msg;
}
}
return "打印机未知的错误";
}
public static int getPCode(String printCode){
for (MS439DeviceStatusEnum value : MS439DeviceStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.code;
}
}
return 500;
}
}

View File

@@ -0,0 +1,75 @@
package com.dpkj.modules.print.enums;
import lombok.Getter;
/**
* MS439打印机墨盒状态
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-03-06 11:39:35
*/
@Getter
public enum MS439InkStatusEnum {
/**
* 墨盒满
*/
INKFULL(439200, "墨盒满", "INKFULL"),
/**
* 墨盒少
*/
INKLOW(439203, "墨盒少", "INKLOW"),
/**
* 缺墨盒
*/
INKOUT(439511, "缺墨盒", "INKOUT"),
/**
* 不支持墨盒检测
*/
INKNOTSUPP(439512, "不支持墨盒检测", "INKNOTSUPP"),
/**
* 墨盒状态未知
*/
INKUNKNOWN(439513, "墨盒未知", "INKUNKNOWN");
;
private final Integer code;
private final String msg;
private final String printCode;
MS439InkStatusEnum(Integer code, String msg, String printCode) {
this.code = code;
this.msg = msg;
this.printCode = printCode;
}
public static String getMessage(String printCode){
for (MS439InkStatusEnum value : MS439InkStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.msg;
}
}
return "打印机未知的错误";
}
public static int getPCode(String printCode){
for (MS439InkStatusEnum value : MS439InkStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.code;
}
}
return 500;
}
}

View File

@@ -0,0 +1,87 @@
package com.dpkj.modules.print.enums;
import lombok.Getter;
/**
* ms439打印机介质枚举
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-03-06 11:38:08
*/
@Getter
public enum MS439MediaStatusEnum {
/**
* 介质在通道
*/
PRESENT(439200, "介质在通道", "PRESENT"),
/**
* 无介质
*/
NOTPRESENT(439502, "无介质", "NOTPRESENT"),
/**
* 卡介质
*/
JAMMED(439503, "卡介质", "JAMMED"),
/**
* 不支持
*/
NOTSUPP(439504, "不支持", "NOTSUPP"),
/**
* 未知
*/
UNKNOWN(439505, "未知", "UNKNOWN"),
/**
* 介质在出口
*/
EXITING(439506, "介质在出口", "EXITING"),
/**
* 介质被回收(复位时)
*/
RETRACTED(439507, "介质被回收(复位时)", "RETRACTED"),
;
private final Integer code;
private final String msg;
private final String printCode;
MS439MediaStatusEnum(Integer code, String msg, String printCode) {
this.code = code;
this.msg = msg;
this.printCode = printCode;
}
public static String getMessage(String printCode){
for (MS439MediaStatusEnum value : MS439MediaStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.msg;
}
}
return "打印机未知的错误";
}
public static int getPCode(String printCode){
for (MS439MediaStatusEnum value : MS439MediaStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.code;
}
}
return 500;
}
}

View File

@@ -0,0 +1,80 @@
package com.dpkj.modules.print.enums;
import lombok.Getter;
/**
* MS439打印机纸盒状态枚举
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-03-06 11:53:21
*/
@Getter
public enum MS439PaperStatusEnum {
/**
* 纸满
*/
PAPERFULL(439200, "纸满", "PAPERFULL"),
/**
* 少纸
*/
PAPERLOW(439204, "少纸", "PAPERLOW"),
/**
* 缺纸
*/
PAPEROUT(439514, "缺纸", "PAPEROUT"),
/**
* 不支持纸箱检测
*/
PAPERNOTSUPP(439515, "不支持纸箱检测", "PAPERNOTSUPP"),
/**
* 卡纸
*/
PAPERJAMMED(439516, "卡纸", "PAPERJAMMED"),
/**
* 纸箱未知
*/
PAPERUNKNOWN(439517, "纸箱未知", "PAPERUNKNOWN");
;
private final Integer code;
private final String msg;
private final String printCode;
MS439PaperStatusEnum(Integer code, String msg, String printCode) {
this.code = code;
this.msg = msg;
this.printCode = printCode;
}
public static String getMessage(String printCode){
for (MS439PaperStatusEnum value : MS439PaperStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.msg;
}
}
return "打印机未知的错误";
}
public static int getPCode(String printCode){
for (MS439PaperStatusEnum value : MS439PaperStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.code;
}
}
return 500;
}
}

View File

@@ -0,0 +1,71 @@
package com.dpkj.modules.print.enums;
import lombok.Getter;
/**
* 盖章机状态枚举
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-03-06 15:13:58
*/
@Getter
public enum MS439StampStatusEnum {
/**
* 盖章机正常
*/
HEALTHY(439200, "正常", "HEALTHY"),
/**
* 盖章机忙碌,请稍后
*/
BUSY(439201, "盖章机忙碌,请稍后", "BUSY"),
/**
* 盖章机发生故障
*/
FATAL(439500, "盖章机发生故障", "FATAL"),
/**
* 未发现盖章机
*/
NODEVICE(439501, "未发现盖章机", "NODEVICE"),
;
private final Integer code;
private final String msg;
private final String printCode;
MS439StampStatusEnum(Integer code, String msg, String printCode) {
this.code = code;
this.msg = msg;
this.printCode = printCode;
}
public static String getMessage(String printCode){
for (MS439StampStatusEnum value : MS439StampStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.msg;
}
}
return "盖章机未知的错误";
}
public static int getPCode(String printCode){
for (MS439StampStatusEnum value : MS439StampStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.code;
}
}
return 500;
}
}

View File

@@ -0,0 +1,75 @@
package com.dpkj.modules.print.enums;
import lombok.Getter;
/**
* MS439打印机 磁带状态
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-03-06 11:38:48
*/
@Getter
public enum MS439TonerStatusEnum {
/**
* 磁带满
*/
TONERFULL(439200, "碳带满", "TONERFULL"),
/**
* 碳带少
*/
TONERLOW(439202, "碳带少", "TONERLOW"),
/**
* 缺碳带
*/
TONEROUT(439508, "缺碳带", "TONEROUT"),
/**
* 不支持碳带检测
*/
TONERNOTSUPP(439509, "不支持碳带检测", "TONERNOTSUPP"),
/**
* 碳带状态未知
*/
TONERUNKNOWN(439510, "碳带未知", "TONERUNKNOWN")
;
private final Integer code;
private final String msg;
private final String printCode;
MS439TonerStatusEnum(Integer code, String msg, String printCode) {
this.code = code;
this.msg = msg;
this.printCode = printCode;
}
public static String getMessage(String printCode){
for (MS439TonerStatusEnum value : MS439TonerStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.msg;
}
}
return "打印机未知的错误";
}
public static int getPCode(String printCode){
for (MS439TonerStatusEnum value : MS439TonerStatusEnum.values()) {
if ( value.getPrintCode().equals(printCode)){
return value.code;
}
}
return 500;
}
}

View File

@@ -0,0 +1,67 @@
package com.dpkj.modules.print.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;
}
}

View File

@@ -0,0 +1,62 @@
package com.dpkj.modules.print.request;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @since 2025-02-28 16:29:27
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class MS439Request implements Serializable {
/**
* 什么类型的纸张默认A4
*/
private String pagesource = "A4";
/**
* 份数,默认1
*/
@Min(value = 1, message = "份数最少要为一份")
private Integer copies = 1;
/**
* 文件的路径
*/
@NotBlank(message = "文件路径不能为空")
private String fileDir;
/**
* 是否盖章,默认不盖章 1盖章0不盖章
*/
private Integer stamp = 0;
/**
* 单面打印,或者双面 duplex 1/2,默认1
*/
private Integer duplex = 1;
/**
* 颜色 0黑白1彩色,默认是黑白的
*/
private Integer color = 0;
/**
* 打印方向 direction 0纵向/1横向
*/
private Integer direction = 0;
}

View File

@@ -0,0 +1,59 @@
package com.dpkj.modules.print.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_1_120_120字段解析qrCodeBase64为字段名称
* 第一个1为打印类型1为二维码2为条形码
* _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;
/**
* 生成的模板的高度默认为1200
*/
private Integer height = 1200;
}

View File

@@ -0,0 +1,13 @@
package com.dpkj.modules.print.service;
import com.dpkj.common.dto.LexMarkResultDTO;
import com.dpkj.modules.print.request.MS439Request;
import com.dpkj.modules.print.vo.PrinterStatus;
import javassist.compiler.Lex;
public interface MS439PrintService {
LexMarkResultDTO<?> printImage(MS439Request request);
LexMarkResultDTO<PrinterStatus> getStatus(String papersource);
}

View File

@@ -0,0 +1,39 @@
package com.dpkj.modules.print.service;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.dto.LexMarkResultDTO;
import com.dpkj.modules.print.vo.PrinterStatus;
/**
* 打印服务接口
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-02-08 10:35:27
*/
public interface PrintService {
/**
* 生成小票图片
*
* @param data json数据用来填充模板
* @param template 模板html字符串或者模板名称
* @param width 图片宽度
* @param height 图片高度
* @param saveDir 图片的保存路径,如果为空,那么不进行图片的保存
*/
Object printImage(JSONObject data, String template, int width, int height, String saveDir);
/**
* 默认实现,给定一个默认可用的图片保存路径
*/
default Object printImage(JSONObject data, String template, int width, int height) {
return printImage(data, template, width, height, "D:\\images");
}
LexMarkResultDTO<PrinterStatus> getStatus();
}

View File

@@ -0,0 +1,147 @@
package com.dpkj.modules.print.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.dto.LexMarkDTO;
import com.dpkj.common.dto.LexMarkResultDTO;
import com.dpkj.common.exception.RRException;
import com.dpkj.common.utils.ThirdService;
import com.dpkj.modules.print.enums.*;
import com.dpkj.modules.print.request.MS439Request;
import com.dpkj.modules.print.service.MS439PrintService;
import com.dpkj.modules.print.vo.PrinterStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Objects;
/**
* 利盟MS439打印机服务
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-02-10 11:49:26
*/
@Slf4j
@Service
public class MS439PrintServiceImpl implements MS439PrintService {
@Resource
private ThirdService thirdService;
@Override
public LexMarkResultDTO<?> printImage(MS439Request request) {
LexMarkResultDTO<PrinterStatus> status = this.getStatus(request.getPagesource());
PrinterStatus ms439 = status.getData();
// 如果是要盖章,校验盖章机器是否正常
if ( request.getStamp() == 1) {
String deviceExStatus = ms439.getStDeviceExStatus();
if (deviceExStatus == null || deviceExStatus.equals("")) {
throw new RRException("获取打印机设备状态出问题");
}
String[] devices = deviceExStatus.split("\\|");
if (devices.length < 2) {
throw new RRException("打印机默认设备数量不对");
}
String device = devices[1];
// 校验盖章机是否正常,除了HEALTHY都抛异常
if (!device.equals(MS439StampStatusEnum.HEALTHY.getPrintCode()) ) {
throw new RRException(MS439StampStatusEnum.getPCode(device),
MS439StampStatusEnum.getMessage(device));
}
}
LexMarkDTO lexMarkDTO = new LexMarkDTO();
lexMarkDTO.setActionName("PrintText");
lexMarkDTO.setCallID(19283);
lexMarkDTO.setDevName("HtmPrinter");
lexMarkDTO.setPluginMethod("exec");
JSONObject param = new JSONObject();
// printType 选择箱子
// param.put("prtData", String.format("PrintType=%d;pagesource=%s;copies=%d;file[0]=%s;stamp=%d;duplex=%d;color=%d;direction=%d",
// request.getPagesource(), request.getCopies(), request.getFileDir(), request.getStamp(),
// request.getDuplex(), request.getColor(), request.getDirection()));
param.put("prtData", String.format("PaperNum=%d;PrintType=%d;Stamp=%d;File[0]=%s;WaitNum=%d;copies=%d;stamp=%d;duplex=%d;color=%d;direction=%d",
request.getCopies(), // 盖章事件分数,和打印份数一致
Objects.equals(request.getPagesource(), "A4") ? 1 : 2, // 打印类型1-A4或者2-A5
request.getStamp(), // 是否盖章
request.getFileDir(), // 要打印的文件的路径
request.getCopies(), // 盖章事件分数,和打印份数一致
request.getCopies(), // 实际要打印的份数
request.getStamp(), // 是否盖章 0-不盖章 1-盖章
request.getDuplex(), // 单面还是双面打印 1-单面 2-双面
request.getColor(), // 打印的颜色0-黑色1-彩色
request.getDirection()));
lexMarkDTO.setParam(param.toString());
LexMarkResultDTO<LexMarkResultDTO.Param> paramLexMarkResultDTO = thirdService.callDevice(lexMarkDTO, LexMarkResultDTO.Param.class);
// thirdService.close("HtmPrinter");
return paramLexMarkResultDTO;
}
@Override
public LexMarkResultDTO<PrinterStatus> getStatus(String papersource) {
LexMarkDTO lexMarkDTO = new LexMarkDTO();
lexMarkDTO.setActionName("GetStatus");
lexMarkDTO.setCallID(19283);
lexMarkDTO.setDevName("HtmPrinter");
lexMarkDTO.setPluginMethod("exec");
LexMarkResultDTO<PrinterStatus> status = thirdService.callDevice(lexMarkDTO, PrinterStatus.class);
if ( status.getResult() != 0){
thirdService.open("HtmPrinter", 0);
status = thirdService.callDevice(lexMarkDTO, PrinterStatus.class);
}
String stPaperEx = status.getData().getStPaperEx();
if (stPaperEx == null || stPaperEx.equals("")) {
throw new RRException("获取打印机纸张状态出问题");
}
String[] papers = stPaperEx.split("\\|");
if ( papers.length < 2) {
throw new RRException("打印机纸盒数量不对");
}
String paperStatus = "";
// 如果是A4,当前是取第一层纸盒作为A4
if (papersource.equals("A4")) {
paperStatus = papers[0];
}
// 如果是A5当前是取第二层纸盒只作为A5
if (papersource.equals("A5")) {
paperStatus = papers[1];
}
if ( !(paperStatus.equals(MS439PaperStatusEnum.PAPERFULL.getPrintCode()) || paperStatus.equals(MS439PaperStatusEnum.PAPERLOW.getPrintCode()))){
throw new RRException(500, paperStatus);
}
PrinterStatus ms439 = status.getData();
// 校验打印机是否正常,除了HEALTHY都抛异常
if (!ms439.getStDeviceStatus().equals(MS439DeviceStatusEnum.HEALTHY.getPrintCode()) ) {
throw new RRException(500, ms439.getStDeviceStatus());
}
// 校验介质 介质在通道才放行
if (!ms439.getStMedia().equals(MS439MediaStatusEnum.PRESENT.getPrintCode())) {
throw new RRException(500, ms439.getStMedia());
}
// 校验磁带 满或者少才放行
if ( !(ms439.getStToner().equals(MS439TonerStatusEnum.TONERFULL.getPrintCode()) || ms439.getStToner().equals(MS439TonerStatusEnum.TONERLOW.getPrintCode()))){
throw new RRException(500, ms439.getStToner());
}
// 校验墨盒 满或者少才放行,当前获取为未知
if ( false && !(ms439.getStInk().equals(MS439InkStatusEnum.INKFULL.getPrintCode()) || ms439.getStInk().equals(MS439InkStatusEnum.INKLOW.getPrintCode())
|| ms439.getStInk().equals(MS439InkStatusEnum.INKUNKNOWN.getPrintCode()))){
throw new RRException(500, ms439.getStInk());
}
return status;
}
}

View File

@@ -0,0 +1,248 @@
package com.dpkj.modules.print.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.config.PrinterConfig;
import com.dpkj.common.dto.LexMarkDTO;
import com.dpkj.common.dto.LexMarkResultDTO;
import com.dpkj.common.exception.RRException;
import com.dpkj.common.utils.TemplateUtils;
import com.dpkj.common.utils.ThirdService;
import com.dpkj.modules.print.enums.*;
import com.dpkj.modules.print.service.PrintService;
import com.dpkj.modules.print.vo.PrinterStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 挂号服务打印
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-02-08 10:36:25
*/
@Slf4j
@Service("registerService")
public class RegisterServiceImpl implements PrintService {
@Resource
private PrinterConfig printerConfig;
@Resource
private ThirdService thirdService;
private static final int MAX_HEIGHT = 1000;
private static final int FIXED_WIDTH = 730;
@Override
public LexMarkResultDTO<LexMarkResultDTO.Param> printImage(JSONObject data, String template, int width, int height, String saveDir) {
if ( height <= 0){
width = FIXED_WIDTH;
}
if ( StringUtils.isEmpty(printerConfig.getTimeType())){
throw new RRException("获取打印时间格式出错:" + printerConfig.getTimeType());
}
if ( StringUtils.isEmpty(printerConfig.getTerminalNumber())){
throw new RRException("获取终端号出错:" + printerConfig.getTerminalNumber());
}
// 强行设置终端号
data.put("terminalNumber", printerConfig.getTerminalNumber());
// 强行设置终端号和打印时间
SimpleDateFormat sdf = new SimpleDateFormat(printerConfig.getTimeType());
String formattedDate = sdf.format( new Date());
data.put("printTime", formattedDate);
// 获取模板
String templateName = ReceiptTemplateEnum.getTemplateName(template);
int dinyHeight = 0;
if (templateName.equals("department")) {
// 由于是使用的门诊小票-T2那么默认的高度为1000强行设置通过动态修改渲染的图片的高度
height = 1000;
// 单行最大长度为10
int singleLineMaxLength = 10;
// 这里的长度取自于department.html模板中的项目单个tr高度并且略高于该高度
int singleLineHeight = 40;
// 动态计算长度
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 += 35;
}
// 计算是否有就诊医生
String doctor = data.getString("doctor");
if ( !StringUtils.isEmpty(doctor)){
dinyHeight += 35;
}
// 计算是否有就诊科室
String department = data.getString("department");
if ( !StringUtils.isEmpty(department)){
dinyHeight += 35;
}
}
height += dinyHeight;
this.getStatus();
StringBuilder filePath = new StringBuilder(saveDir);
// 校验是否选中了模板,如果没选中模板的话则不需要另外生成了
if ( !StringUtils.isEmpty(templateName) && !StringUtils.isEmpty(saveDir)){
byte[] image = new TemplateUtils().generateReceiptImage(data, templateName, width, height, filePath);
}else {
throw new RRException("模板渲染错误");
}
// 计算切割的块数
int numPieces = (int) Math.ceil((double) height / MAX_HEIGHT);
String[] deletePathList = new String[numPieces + 1];
deletePathList[0] = filePath.toString();
String[] filePathList = new String[numPieces];
// 对图片进行分块处理,当前台式打印机最大参数配置 宽度690高度1200
if ( height > MAX_HEIGHT){
try {
// 读取输入图片
BufferedImage originalImage = ImageIO.read(new File(filePath.toString()));
// 循环切割图片并保存每一块
for (int i = 0; i < numPieces; i++) {
int startY = i * MAX_HEIGHT;
int pieceHeight = Math.min(MAX_HEIGHT, height - startY);
// 创建一个新的 BufferedImage 对象,用于存储切割的部分
BufferedImage piece = new BufferedImage(width, pieceHeight, BufferedImage.TYPE_INT_RGB);
piece.getGraphics().drawImage(originalImage, 0, 0, width, pieceHeight, 0, startY, width, startY + pieceHeight, null);
// 保存切割的图片块
File outputFile = new File(saveDir + "/output_" + (i + 1) + ".jpg");
filePathList[i] = "LOGO" + (i + 1) + "=" + saveDir + "/output_" + (i + 1) + ".jpg";
deletePathList[i + 1] = saveDir + "/output_" + (i + 1) + ".jpg";
ImageIO.write(piece, "jpg", outputFile);
}
} catch (IOException e) {
log.error("模板切割失败");
e.printStackTrace();
}
}else {
filePathList[0] = "LOGO1=" + filePath;
}
LexMarkDTO lexMarkDTO = new LexMarkDTO();
lexMarkDTO.setActionName("PrintForm");
lexMarkDTO.setCallID(19256);
lexMarkDTO.setDevName("ReceiptPrinter");
JSONObject param = new JSONObject();
param.put("TimeOut", 30000);
param.put("formName", "GWIReceiptForm_example"); // 表单名从
param.put("mediaName", "GWIReceiptMedia_example"); // 媒介类型
param.put("alignment", 0); // 对齐方式
param.put("offsetX", 0); // x
param.put("offsetY", 0); // y
param.put("resolution", 1); // 1\2\4\6\8打印的清晰度
param.put("mediaCtrl", 1); // 媒介类型,当前为:打印缓冲区 + 弹出 + 剪纸
param.put("fields", StringUtils.join(filePathList, "&"));
lexMarkDTO.setParam(param.toJSONString());
LexMarkResultDTO<LexMarkResultDTO.Param> paramLexMarkResultDTO = thirdService.callDevice(lexMarkDTO, LexMarkResultDTO.Param.class);
for (String path : deletePathList) {
if ( path != null && !path.isEmpty()) {
File file = new File(path);
// 检查文件是否存在
if (file.exists()) {
// 尝试删除文件
if (file.delete()) {
log.info("文件删除成功: " + path);
} else {
log.info("文件删除失败: " + path);
}
} else {
log.info("文件不存在: " + path);
}
}
}
return paramLexMarkResultDTO;
}
@Override
public LexMarkResultDTO<PrinterStatus> getStatus() {
LexMarkDTO lexMarkDTO = new LexMarkDTO();
lexMarkDTO.setActionName("GetStatus");
lexMarkDTO.setCallID(11225);
lexMarkDTO.setDevName("ReceiptPrinter");
lexMarkDTO.setPluginMethod("getInfo");
// lexMarkDTO.setMethodType(0);
LexMarkResultDTO<PrinterStatus> status = thirdService.callDevice(lexMarkDTO, PrinterStatus.class);
if ( status.getResult() != 0){
thirdService.open("ReceiptPrinter", 1);
status = thirdService.callDevice(lexMarkDTO, PrinterStatus.class);
}
PrinterStatus ms439 = status.getData();
// 获取打印机纸张
String stPaperEx = ms439.getStPaperEx();
if (stPaperEx == null || stPaperEx.isEmpty()) {
throw new RRException("获取打印机纸张状态出问题");
}
String[] papers = stPaperEx.split("\\|");
if ( papers.length < 1) {
throw new RRException("打印机纸盒数量不对");
}
String paperStatus = papers[0];
if ( !(paperStatus.equals(MS439PaperStatusEnum.PAPERFULL.getPrintCode()) || paperStatus.equals(MS439PaperStatusEnum.PAPERLOW.getPrintCode()))){
throw new RRException(500, paperStatus);
}
// 校验打印机是否正常,除了HEALTHY都抛异常
if (!ms439.getStDeviceStatus().equals(MS439DeviceStatusEnum.HEALTHY.getPrintCode()) ) {
throw new RRException(500, ms439.getStDeviceStatus());
}
// 校验介质 介质在通道才放行
if (!ms439.getStMedia().equals(MS439MediaStatusEnum.PRESENT.getPrintCode())) {
throw new RRException(500, ms439.getStMedia());
}
// 校验磁带 满或者少才放行
if ( false && !(ms439.getStToner().equals(MS439TonerStatusEnum.TONERFULL.getPrintCode()) || ms439.getStToner().equals(MS439TonerStatusEnum.TONERLOW.getPrintCode()))){
throw new RRException(500, ms439.getStToner());
}
// 校验墨盒 满或者少才放行,当前获取为未知
if ( false && !(ms439.getStInk().equals(MS439InkStatusEnum.INKFULL.getPrintCode()) || ms439.getStInk().equals(MS439InkStatusEnum.INKLOW.getPrintCode())
|| ms439.getStInk().equals(MS439InkStatusEnum.INKUNKNOWN.getPrintCode()))){
throw new RRException(500, ms439.getStInk());
}
return status;
}
}

View File

@@ -0,0 +1,68 @@
package com.dpkj.modules.print.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;
}

View File

@@ -0,0 +1,49 @@
package com.dpkj.modules.readcard.controller;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.readcard.service.ReadCardService;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("readCard")
public class ReadCardController {
@Autowired
private ReadCardService readCardService;
/**
* 身份证读取
*
* @return
*/
@PostMapping("IDCardReader")
public Result IDCardReader() {
return readCardService.IDCardReader();
}
/**
* 社保卡读取
*
* @return
*/
@PostMapping("SocialSecurityCardReader")
public Result SocialSecurityCardReader() {
return readCardService.SocialSecurityCardReader();
}
/**
* 社保卡退卡
*
* @return
*/
@PostMapping("cardRefund")
public Result cardRefund() {
return readCardService.cardRefund();
}
}

View File

@@ -0,0 +1,12 @@
package com.dpkj.modules.readcard.service;
import com.dpkj.common.vo.Result;
public interface ReadCardService {
Result IDCardReader();
Result SocialSecurityCardReader();
Result cardRefund();
}

View File

@@ -0,0 +1,394 @@
package com.dpkj.modules.readcard.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.constant.ErrorCodeConstants;
import com.dpkj.common.constant.LexMarkConst;
import com.dpkj.common.constant.MiddlewareFunctionsConst;
import com.dpkj.common.dto.LexMarkDTO;
import com.dpkj.common.utils.ThirdService;
import com.dpkj.common.utils.ThirdServiceUtil;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.readcard.service.ReadCardService;
import com.dpkj.modules.readcard.vo.IDCardReadResultVO;
import com.dpkj.modules.readcard.vo.SocialSecurityCardInfoVO;
import com.dpkj.modules.readcard.vo.UserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.stream.Stream;
/**
* @BelongsProject: 银医通-澜沧中医院-DLL-台式机
* @BelongsPackage: com.dpkj.modules.readcard.service.impl
* @Author: wzc
* @Description:
* @CreateTime: 2025-02-07 10:21
*/
@Slf4j
@Service
public class ReadCardServiceImpl implements ReadCardService {
@Autowired
private ThirdService thirdService;
@Autowired
private ThirdServiceUtil thirdServiceUtil;
private String getLogPrefix() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
String methodName = stackTrace[3].getMethodName();
return "[" + this.getClass().getSimpleName() + "#" + methodName + "] ";
}
/**
* 身份证读取
* 1. 获取设备状态
* 2. 设备连接状态需初始化
* 3. 连接身份证读取设备
* 4. 非接触身份证读取
* 5. 关闭设备
*
* @return
*/
@Override
public Result IDCardReader() {
// String logPrefix = getLogPrefix();
Result result = null;
log.info("[ReadCardServiceImpl][IDCardReader][60]: {}", "开始执行身份证读取流程");
try {
// 获取设备状态
LexMarkDTO status = new LexMarkDTO();
status.setDevName(LexMarkConst.ID_CARD_READ);
status.setCallID(0);
status.setActionName(MiddlewareFunctionsConst.GET_STATUS_SYNC);
JSONObject statusParam = new JSONObject();
statusParam.put("timeout", 0);
log.info("[ReadCardServiceImpl][IDCardReader][69]: 获取设备状态,参数:{}", status);
IDCardReadResultVO statusResult = thirdServiceUtil.callDevice(status, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][IDCardReader][71]: 获取设备状态结果{}", statusResult);
if (statusResult.getResult() == 0) {
// 连接状态,初始化
LexMarkDTO reset = new LexMarkDTO();
reset.setDevName(LexMarkConst.ID_CARD_READ);
reset.setCallID(0);
reset.setActionName(MiddlewareFunctionsConst.RESET);
JSONObject resetParam = new JSONObject();
resetParam.put("ResetAction", 1);
log.info("[ReadCardServiceImpl][IDCardReader][80]: 调用复位(初始化)方法,参数:{}", reset);
IDCardReadResultVO resetResult = thirdServiceUtil.callDevice(reset, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][IDCardReader][82]: 设备复位(初始化)结果: {}", resetResult);
if (resetResult.getResult() == 0) {
// 复位成功
result = connectAndReadIDCard(result);
} else {
result = Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(resetResult.getResult()), resetResult.getResult());
log.info("[ReadCardServiceImpl][IDCardReader][88]: 身份证读取设备(初始化)异常,详情:{}", ErrorCodeConstants.COMMON_ERROR_CODE.get(resetResult.getResult()));
}
} else {
// 设备未连接,无需复位(初始化)
result = connectAndReadIDCard(result);
}
} finally {
log.info("[ReadCardServiceImpl][IDCardReader][95]: {}", "准备关闭身份证读卡设备");
// 关闭身份证读卡设备
LexMarkDTO close = new LexMarkDTO();
close.setDevName(LexMarkConst.ID_CARD_READ);
close.setCallID(0);
close.setActionName(MiddlewareFunctionsConst.CLOSE_CONNECTION);
log.info("[ReadCardServiceImpl][IDCardReader][101]: 调用设备关闭方法,参数: {}", close);
IDCardReadResultVO closeResult = thirdServiceUtil.callDevice(close, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][IDCardReader][103]: 设备关闭结果: {}", closeResult);
if (closeResult.getResult() != 0) {
log.info("[ReadCardServiceImpl][IDCardReader][105]: {}", "身份证读卡设备关闭异常!");
}
}
log.info("[ReadCardServiceImpl][IDCardReader][108]: 身份证读取流程结束,最终返回结果: {}", result);
return result;
}
private Result connectAndReadIDCard(Result result) {
// String logPrefix = getLogPrefix();
log.info("[ReadCardServiceImpl][connectAndReadIDCard][114]: {}", "准备连接非接身份证读卡模块");
// 连接非接身份证读卡模块
LexMarkDTO connect = new LexMarkDTO();
connect.setActionName(MiddlewareFunctionsConst.OPEN_CONNECTION);
connect.setCallID(19256);
connect.setDevName(LexMarkConst.ID_CARD_READ);
JSONObject connectParam = new JSONObject();
connectParam.put("TimeOut", 30000);
connectParam.put("ServiceName", LexMarkConst.ID_CARD_READ);
connect.setParam(connectParam.toJSONString());
log.info("[ReadCardServiceImpl][connectAndReadIDCard][124]: 调用设备连接方法,参数: {}", connect);
IDCardReadResultVO connectResult = thirdServiceUtil.callDevice(connect, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][connectAndReadIDCard][126]: 设备连接结果: {}", connectResult);
if (connectResult.getResult() == 0) {
log.info("[ReadCardServiceImpl][connectAndReadIDCard][128]: {}", "设备连接成功,准备读取身份证");
// 身份证读取
LexMarkDTO read = new LexMarkDTO();
read.setActionName(MiddlewareFunctionsConst.ACCEPT_AND_READ_TRACKS);
read.setCallID(19256);
read.setDevName(LexMarkConst.ID_CARD_READ);
JSONObject readParam = new JSONObject();
readParam.put("TimeOut", 80000);
readParam.put("TrackMap", 776);
read.setParam(readParam.toJSONString());
log.info("[ReadCardServiceImpl][connectAndReadIDCard][138]: 调用设备读取方法,参数: {}", read);
IDCardReadResultVO readResult = thirdServiceUtil.callDevice(read, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][connectAndReadIDCard][140]: 设备读取结果: {}", readResult);
if (readResult.getResult() == 0) {
log.info("[ReadCardServiceImpl][connectAndReadIDCard][142]: {}", "身份证读取成功,开始解析身份证信息");
IDCardReadResultVO.Param resultParam = JSON.parseObject(readResult.getParam().toString(), IDCardReadResultVO.Param.class);
if (resultParam.getResult() == 0) {
log.info("[ReadCardServiceImpl][connectAndReadIDCard][145]: {}", "身份证信息解析成功,开始封装用户信息");
UserInfoVO userInfoVO = new UserInfoVO();
// 身份证基本信息 解析
String[] array = Stream.of(resultParam.getChipdata().getDatas().split("\\|"))
.filter(pair -> pair.contains("="))
.map(pair -> pair.split("=")[1])
.toArray(String[]::new);
userInfoVO.setName(array[0]);
userInfoVO.setSex(array[1]);
userInfoVO.setNation(array[2]);
userInfoVO.setBorn(array[3]);
userInfoVO.setAddress(array[4]);
userInfoVO.setIDCardNo(array[5]);
userInfoVO.setGrantDept(array[6]);
userInfoVO.setUserLifeBegin(array[7]);
userInfoVO.setUserLifeEnd(array[8]);
userInfoVO.setIDhead(array[9].replace("\\\\", "/"));
// 正面
userInfoVO.setFrontimage(resultParam.getFrontimage().getDatas().replace("\\\\", "/"));
// 反面
userInfoVO.setBackimage(resultParam.getBackimage().getDatas().replace("\\\\", "/"));
result = Result.ok(userInfoVO);
log.info("[ReadCardServiceImpl][connectAndReadIDCard][167]: 用户信息封装成功,返回结果: {}", result);
} else {
result = Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(resultParam.getResult()), resultParam.getResult());
log.info("[ReadCardServiceImpl][connectAndReadIDCard][170]: 身份证信息解析失败,错误信息: {}, 返回结果: {}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(resultParam.getResult()), result);
}
} else {
result = Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(readResult.getResult()), readResult.getResult());
log.info("[ReadCardServiceImpl][connectAndReadIDCard][175]: 身份证读取失败,错误信息: {}, 返回结果: {}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(readResult.getResult()), result);
}
} else {
result = Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(connectResult.getResult()), connectResult.getResult());
log.info("[ReadCardServiceImpl][connectAndReadIDCard][180]: 设备连接失败,错误信息: {}, 返回结果: {}", connectResult, result);
}
return result;
}
/**
* 社保卡读取
* 1. 获取社保卡读取设备状态
* 2. 连接状态需复位(初始化)
* 3. 连接社保卡读取设备
* 4. 进卡读卡
* 5. 读卡器上电
* 6. 社保卡信息读取
*
* @return
*/
@Override
public Result SocialSecurityCardReader() {
// String logPrefix = getLogPrefix();
Result result = null;
// 获取设备状态
LexMarkDTO status = new LexMarkDTO();
status.setDevName(LexMarkConst.CARD_READER);
status.setCallID(0);
status.setActionName(MiddlewareFunctionsConst.GET_STATUS_SYNC);
JSONObject statusParam = new JSONObject();
statusParam.put("timeout", 0);
log.info("[ReadCardServiceImpl][SocialSecurityCardReader][208]: 获取设备状态,参数: {}", status);
IDCardReadResultVO statusResult = thirdServiceUtil.callDevice(status, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][SocialSecurityCardReader][210]: 获取设备状态结果: {}", statusResult);
if (statusResult.getResult() == 0) {
// 连接状态,初始化
LexMarkDTO reset = new LexMarkDTO();
reset.setDevName(LexMarkConst.CARD_READER);
reset.setCallID(0);
reset.setActionName(MiddlewareFunctionsConst.RESET);
JSONObject resetParam = new JSONObject();
resetParam.put("ResetAction", 1);
log.info("[ReadCardServiceImpl][SocialSecurityCardReader][219]: 调用复位(初始化)方法,参数: {}", reset);
IDCardReadResultVO resetResult = thirdServiceUtil.callDevice(reset, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][SocialSecurityCardReader][221]: 设备复位(初始化)结果: {}", resetResult);
if (resetResult.getResult() == 0) {
// 复位成功
result = connectAndSocialSecurityCardReader(result);
} else {
result = Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(resetResult.getResult()), resetResult.getResult());
log.info("[ReadCardServiceImpl][SocialSecurityCardReader][227]: 社保卡读取设备(初始化)异常,详情:{}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(resetResult.getResult()));
}
} else {
// 设备未连接,无需复位(初始化)
result = connectAndSocialSecurityCardReader(result);
}
log.info("[ReadCardServiceImpl][SocialSecurityCardReader][234]: 社保卡读取流程结束,最终返回结果: {}", result);
return result;
}
public Result connectAndSocialSecurityCardReader(Result result) {
// String logPrefix = getLogPrefix();
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][240]: {}", "开始执行社保卡读取流程");
// 社保卡读卡设备连接
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][243]: {}", "准备连接社保卡读卡设备");
LexMarkDTO connect = new LexMarkDTO();
connect.setActionName(MiddlewareFunctionsConst.OPEN_CONNECTION);
connect.setCallID(19256);
connect.setDevName(LexMarkConst.CARD_READER);
JSONObject connectParam = new JSONObject();
connectParam.put("TimeOut", 30000);
connectParam.put("ServiceName", LexMarkConst.CARD_READER);
connect.setParam(connectParam.toJSONString());
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][252]: 调用设备连接方法,参数: {}", connect);
IDCardReadResultVO connectResult = thirdServiceUtil.callDevice(connect, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][254]: 设备连接结果: {}", connectResult);
if (connectResult.getResult() == 0) {
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][256]: {}", "社保卡读卡设备连接成功,准备进卡");
// 进卡
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][259]: {}", "准备执行进卡操作");
LexMarkDTO enterTheCard = new LexMarkDTO();
enterTheCard.setActionName(MiddlewareFunctionsConst.ACCEPT_AND_READ_TRACKS);
enterTheCard.setCallID(19256);
enterTheCard.setDevName(LexMarkConst.CARD_READER);
JSONObject enterTheCardParam = new JSONObject();
enterTheCardParam.put("DevType", 5);
enterTheCardParam.put("TrackMap", 11);
enterTheCardParam.put("TimeOut", 80000);
enterTheCard.setParam(enterTheCardParam.toJSONString());
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][269]: 调用进卡方法,参数: {}", enterTheCard);
IDCardReadResultVO enterTheCardResult = thirdServiceUtil.callDevice(enterTheCard, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][271]: 进卡操作结果: {}", enterTheCardResult);
if (enterTheCardResult.getResult() == 0) {
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][273]: {}", "进卡操作成功,准备上电");
// 上电
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][276]: {}", "准备执行上电操作");
LexMarkDTO powerOn = new LexMarkDTO();
powerOn.setActionName(MiddlewareFunctionsConst.CHIPPOWER);
powerOn.setCallID(19256);
powerOn.setDevName(LexMarkConst.CARD_READER);
JSONObject powerOnParam = new JSONObject();
powerOnParam.put("PsamNo", 1);
powerOnParam.put("ChipAction", 2);
powerOn.setParam(powerOnParam.toJSONString());
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][285]: 调用上电方法,参数: {}", powerOn);
IDCardReadResultVO powerOnResult = thirdServiceUtil.callDevice(powerOn, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][287]: 上电操作结果: {}", powerOnResult);
if (powerOnResult.getResult() == 0) {
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][289]: {}", "上电操作成功,准备读取社保卡信息");
// 社保卡信息读取
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][292]: {}", "准备读取社保卡信息");
LexMarkDTO socialSecurityCardReader = new LexMarkDTO();
socialSecurityCardReader.setActionName(MiddlewareFunctionsConst.I_READ_CARD_BAS);
socialSecurityCardReader.setCallID(19256);
socialSecurityCardReader.setDevName(LexMarkConst.CARD_READER);
JSONObject socialSecurityCardReaderParam = new JSONObject();
socialSecurityCardReaderParam.put("iType", 3);
socialSecurityCardReader.setParam(socialSecurityCardReaderParam.toJSONString());
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][300]: 调用读取社保卡信息方法,参数: {}", socialSecurityCardReader);
IDCardReadResultVO socialSecurityCardReaderResult = thirdServiceUtil.callDevice(socialSecurityCardReader, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][302]: 读取社保卡信息结果: {}", socialSecurityCardReaderResult);
if (socialSecurityCardReaderResult.getResult() == 0) {
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][304]: {}", "社保卡信息读取成功,开始解析信息");
// 社保信息
IDCardReadResultVO.SocialSecurityCard resultParam = JSON.parseObject(socialSecurityCardReaderResult.getParam().toString(), IDCardReadResultVO.SocialSecurityCard.class);
String[] split = resultParam.getRerurnData().split("\\|");
SocialSecurityCardInfoVO socialSecurityCardInfoVO = new SocialSecurityCardInfoVO();
// 区号代码截取
socialSecurityCardInfoVO.setAreaCode(split[0].substring(split[0].length() - 6));
socialSecurityCardInfoVO.setSocialSecurityNo(split[1]);
socialSecurityCardInfoVO.setCardNumber(split[2]);
socialSecurityCardInfoVO.setIdentificationCode(split[3]);
socialSecurityCardInfoVO.setName(split[4]);
socialSecurityCardInfoVO.setCardResetInformation(split[5]);
socialSecurityCardInfoVO.setSpecificationVersion(split[6]);
socialSecurityCardInfoVO.setIssuanceDate(split[7]);
socialSecurityCardInfoVO.setExpireDate(split[8]);
socialSecurityCardInfoVO.setTerminalNumber(split[9]);
socialSecurityCardInfoVO.setTerminalDeviceNumber(split[10]);
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][322]: {}", "社保卡信息解析完成,返回成功结果");
return Result.ok(socialSecurityCardInfoVO);
} else {
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][325]: 社保卡信息读取失败,详情:{}",
ErrorCodeConstants.SOCIAL_SECURITYCARD_ERROR_CODE.get(socialSecurityCardReaderResult.getResult()));
return Result.error(ErrorCodeConstants.SOCIAL_SECURITYCARD_ERROR_CODE.get(socialSecurityCardReaderResult.getResult()), socialSecurityCardReaderResult.getResult());
}
} else {
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][330]: 社保卡读取设备上电失败,详情:{}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(powerOnResult.getResult()));
return Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(powerOnResult.getResult()), powerOnResult.getResult());
}
} else {
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][335]: 社保卡读取设备进卡失败,详情:{}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(enterTheCardResult.getResult()));
return Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(enterTheCardResult.getResult()), enterTheCardResult.getResult());
}
} else {
log.info("[ReadCardServiceImpl][connectAndSocialSecurityCardReader][340]: 社保卡读取设备连接失败,详情:{}",
ErrorCodeConstants.COMMON_ERROR_CODE.get(connectResult.getResult()));
return Result.error(ErrorCodeConstants.COMMON_ERROR_CODE.get(connectResult.getResult()), connectResult.getResult());
}
}
/**
* 退卡并关闭设备
*
* @return
*/
@Override
public Result cardRefund() {
// String logPrefix = getLogPrefix();
log.info("[ReadCardServiceImpl][cardRefund][354]: {}", "开始执行社保卡退卡流程");
// 社保卡退卡
log.info("[ReadCardServiceImpl][cardRefund][357]: {}", "准备发起社保卡退卡请求");
LexMarkDTO cardRefund = new LexMarkDTO();
cardRefund.setDevName(LexMarkConst.CARD_READER);
cardRefund.setCallID(19256);
cardRefund.setActionName(MiddlewareFunctionsConst.EJECT);
JSONObject cardRefundParam = new JSONObject();
cardRefundParam.put("position", 1);
cardRefundParam.put("ejectpos", 1);
cardRefund.setParam(cardRefundParam.toJSONString());
log.info("[ReadCardServiceImpl][cardRefund][366]: 调用设备退卡方法,参数: {}", cardRefund);
IDCardReadResultVO cardRefundResult = thirdServiceUtil.callDevice(cardRefund, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][cardRefund][368]: 设备退卡操作结果: {}", cardRefundResult);
if (cardRefundResult.getResult() == 0) {
log.info("[ReadCardServiceImpl][cardRefund][371]: {}", "社保卡退卡成功,准备关闭社保卡读卡设备");
// 退卡成功再关闭社保卡读卡设备
LexMarkDTO close = new LexMarkDTO();
close.setDevName(LexMarkConst.CARD_READER);
close.setCallID(0);
close.setActionName(MiddlewareFunctionsConst.CLOSE_CONNECTION);
log.info("[ReadCardServiceImpl][cardRefund][378]: {}", "调用设备关闭方法,参数: {}", close);
IDCardReadResultVO closeResult = thirdServiceUtil.callDevice(close, IDCardReadResultVO.class);
log.info("[ReadCardServiceImpl][cardRefund][380]: 设备关闭操作结果: {}", closeResult);
if (closeResult.getResult() != 0) {
log.info("[ReadCardServiceImpl][cardRefund][383]: 社保卡读卡设备关闭异常!详情:{}",
ErrorCodeConstants.SOCIAL_SECURITYCARD_ERROR_CODE.get(closeResult.getResult()), closeResult.getResult());
}
log.info("[ReadCardServiceImpl][cardRefund][386]: {}", "社保卡退卡流程完成,退卡成功");
return Result.ok("退卡成功!");
} else {
log.info("[ReadCardServiceImpl][cardRefund][389]: 退卡失败!请联系工作人员!失败原因:{}",
ErrorCodeConstants.SOCIAL_SECURITYCARD_ERROR_CODE.get(cardRefundResult.getResult()));
return Result.error(ErrorCodeConstants.SOCIAL_SECURITYCARD_ERROR_CODE.get(cardRefundResult.getResult()), cardRefundResult.getResult());
}
}
}

View File

@@ -0,0 +1,211 @@
package com.dpkj.modules.readcard.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
/**
* 身份证读取模块返回值
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class IDCardReadResultVO<T> implements Serializable {
/**
* 对应发送请求中的devName
*/
private String devName;
/**
* 事件名由发送请求中的actionName+Over组成。
*/
private String msgName;
/**
* 对应发送请求的callID。
*/
private int callID;
/**
* 错误码0表示成功其他表示失败比如-4表示取消-48表示超时-14表示硬件故障
*/
private int result;
/**
* 发送请求中的actionName
*/
private String cmdName;
/**
* 返回参数
*/
private T param;
/**
* 详情描述
*/
private String desc;
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Param {
/**
* 请求ID
*/
private int RequestID;
/**
* 身份证反面图像数据
*/
private Backimage backimage;
/**
* 身份证基础信息
*/
private Chipdata chipdata;
/**
* 指令ID
*/
private int dwCommandCode;
/**
* 结束事件名称
*/
private String AcceptAndReadTracksOver;
/**
* 事件ID
*/
private int eventType;
/**
* 身份证正面图像数据
*/
private Frontimage frontimage;
/**
* 服务ID
*/
private int hService;
/**
* 读取结果 0成功 <0失败
*/
private int result;
/**
* 调用的方法名称(操作函数)
*/
private String cmdName;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Backimage {
/**
* 身份证反面图像存储路径
*/
private String datas;
/**
* 数据长度
*/
private int len;
/**
* 返回的数据状态
* DATAOK 读取正常
* BLANK 空
* INVALID 无效
* NOTREAD 未读取
*/
private String status;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Frontimage {
/**
* 身份证正面图像存储路径
*/
private String datas;
/**
* 数据长度
*/
private int len;
/**
* 返回的数据状态
* DATAOK 读取正常
* BLANK 空
* INVALID 无效
* NOTREAD 未读取
*/
private String status;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Chipdata {
/**
* 身份证基本信息
*/
private String datas;
/**
* 数据长度
*/
private int len;
/**
* 返回的数据状态
* DATAOK 读取正常
* BLANK 空
* INVALID 无效
* NOTREAD 未读取
*/
private String status;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class SocialSecurityCard {
/**
* 社保卡基本信息
*/
private String RerurnData;
/**
* 社保卡读取响应信息
*/
private String msgerror;
/**
* 社保卡读取响应结果 0成功 <0失败
*/
private int result;
/**
* 调用的方法
*/
private String cmdName;
}
}

View File

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

View File

@@ -0,0 +1,35 @@
package com.dpkj.modules.readcard.vo;
import lombok.Data;
/**
* 身份证读取信息
*/
@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;
// 头像照片存储位置
private String IDhead;
// 证件正面照存储位置
private String frontimage;
// 证件反面面照存储位置
private String backimage;
}

View File

@@ -1,4 +1,11 @@
server:
port: 5946
dpkj:
# 医保配置
chs:
# 医保机构编码
orgcode: H53082800070
file:
# 文件保存地址
path: G:\Temp\img
@@ -6,8 +13,18 @@ dpkj:
printer:
# 打印机连接方式 USB: usb连接 | BTMS串口连接
connection-type: USB
# 终端号设置
terminal-Number: PORT-000000001
# 打印时间格式化
time-Type: yyyy-MM-dd HH:mm:ss
# 打印端口 串口连接下使用
port-name:
# 波特率 串口连接下使用
baud-rate:
# 自定义app参数
app:
custom:
lexMarkServiceIp: http://127.0.0.1
lexMarkServicePort: 12346

View File

@@ -1,4 +1,11 @@
server:
port: 5946
dpkj:
# 医保配置
chs:
# 医保机构编码
orgcode: H53082800070
file:
# 文件保存地址
path: D:\Project\Express\upload
@@ -6,8 +13,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:
# 自定义app参数
app:
custom:
lexMarkServiceIp: http://127.0.0.1
lexMarkServicePort: 12346

View File

@@ -1,18 +1,4 @@
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
resource:
static-locations: classpath:/static/,classpath:/public/
application:
name: ems-express-bridge
profiles:
#active: '@profile.name@'
active: pro
server:
port: 5946
servlet:
context-path: /api
tomcat:
@@ -25,3 +11,15 @@ server:
enabled: true
min-response-size: 1024
mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
resource:
static-locations: classpath:/static/,classpath:/public/
application:
name: ems-express-bridge
profiles:
active: pro

View File

@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>门诊缴费凭证</title>
</head>
<body>
<div style="font-weight: 700;">
<div style="text-align: center;margin-top: 40px;">
<div><span style="font-size: 28px;">******<span th:text="${hospitalName}"></span>******</span></div>
<div style="font-size: 38px; margin-top: 20px;"><span th:text="${registeTerminalName}"></span></div>
<div style="font-size: 38px; margin-top: 10px;"><span th:text="${registeType}"></span></div>
</div>
<div style="text-align: center; font-size: 32px;">
-----------------------------------------------------------------------------
</div>
<div style="font-size: 35px; margin: 10px 20px">
<div style="display: flex">
<div style="width: 38.3%;word-break: break-all;">
<span>姓名:</span><span th:text="${name}"></span>
</div>
<div style="width: 28.3%;word-break: break-all;"><span>性别:</span><span th:text="${gender}"></span></div>
<div style="width: 33.3%;word-break: break-all;"><span>年龄:</span><span th:text="${age}"></span></div>
</div>
<div style="display: flex;margin-top: 10px;">
<div style="width: 50%;word-break: break-all;" th:if="${outpatientNumber}">
<span>门诊号:</span><span th:text="${outpatientNumber}"></span></div>
<div style="width: 50%;word-break: break-all;" th:if="${doctor}">
<span>就诊医生:</span><span th:text="${doctor}"></span>
</div>
</div>
<div style="margin-top: 10px;" th:if="${department}"><span>就诊科室:</span><span th:text="${department}"></span></div>
</div>
<div style="text-align: center; font-size: 32px;">
-----------------------------------------------------------------------------
</div>
<div style="font-size: 35px; margin: 10px 20px">
<div style="display: flex;">
<div style="flex: 1;"><span>费用总额:</span><span th:text="${totalFee}"></span></div>
<div style="flex: 1;"><span>个人支付:</span><span th:text="${personalPayment}"></span></div>
</div>
<div style="margin-top: 10px;"><span>实收金额:</span><span th:text="${actualReceiptAmount}"></span></div>
<div style="margin-top: 10px;"><span>实收金额:</span><span th:text="${actualReceiptAmountChinese}"></span></div>
</div>
<div style="text-align: center; font-size: 32px;">
-----------------------------------------------------------------------------
</div>
<div style="margin: 10px 20px">
<table style="width: 100%; table-layout: fixed; border-collapse: collapse;">
<tr style="font-size: 35px;">
<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: 30px;" th:each="item : ${items}">
<td style="text-align: left;width: 300px;word-break: break-all;" 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="text-align: center; font-weight: 700; font-size: 32px;">
-----------------------------------------------------------------------------
</div>
<div style="margin-left: 20px; font-weight: 700; font-size: 35px;">
<div>终端编号:<span th:text="${terminalNumber}"></span></div>
<div>打印时间:<span th:text="${printTime}"></span></div>
</div>
<div style="margin-left: 20px; font-size: 35px; font-weight: 700; margin-top: 20px;">
<span style="margin-top: 20px;">温馨提示</span><br>
<span>1.请取走全部凭条、并妥善保管</span><br>
<span>2.如果对缴费结算存在疑问,请到人工窗口咨询</span>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,188 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>门诊缴费凭证</title>
<style>
.bold-text {
font-weight: 700;
}
.text-35 {
font-size: 35px;
}
.text-30 {
font-size: 30px;
}
.center-text {
text-align: center;
}
.margin-top-10 {
margin-top: 10px;
}
.margin-top-40 {
margin-top: 40px;
}
.header-title {
font-size: 28px;
}
.header-terminal {
font-size: 38px;
margin-top: 20px;
}
.header-receipt {
font-size: 38px;
margin-top: 10px;
}
.divider {
text-align: center;
font-weight: 700;
font-size: 32px;
}
.patient-info {
font-size: 35px;
margin: 10px 20px;
}
.patient-info .info-item {
flex: 1;
}
.patient-info .info-row {
display: flex;
}
.payment-info {
font-size: 35px;
margin: 10px 20px;
}
.payment-info .info-item {
flex: 1;
}
.payment-info .info-row {
display: flex;
}
.table-container {
margin: 10px 20px;
}
.table-container table {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
}
.table-container .first-th {
width: 300px;
text-align: left;
}
.table-container .df-th {
width: calc(25% - 5px);
text-align: center;
}
.table-container .first-td {
text-align: left;
width: 300px;
word-break: break-all;
}
.table-container .df-td {
text-align: center;
}
.terminal-info {
margin-left: 20px;
font-weight: 700;
font-size: 35px;
}
.tips {
margin-left: 20px;
font-size: 35px;
font-weight: 700;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="bold-text">
<div class="center-text margin-top-40">
<div><span class="header-title">******<span th:text="${hospitalName}"></span>******</span></div>
<div class="header-terminal"><span th:text="${registeTerminalName}"></span></div>
<div class="header-receipt"><span th:text="${registeType}"></span></div>
</div>
<div class="divider">
-----------------------------------------------------------------------------
</div>
<div class="patient-info">
<div class="info-row">
<div class="info-item">姓名:<span th:text="${name}"></span></div>
<div class="info-item">性别:<span th:text="${gender}"></span></div>
<div class="info-item">年龄:<span th:text="${age}"></span></div>
</div>
<div class="info-row margin-top-10">
<div class="info-item" th:if="${outpatientNumber}">门诊号:<span th:text="${outpatientNumber}"></span></div>
<div class="info-item" th:if="${doctor}">就诊医生:<span th:text="${doctor}"></span></div>
</div>
<div class="margin-top-10" th:if="${department}">就诊科室:<span th:text="${department}"></span></div>
</div>
<div class="divider">
-----------------------------------------------------------------------------
</div>
<div class="payment-info">
<div class="info-row">
<div class="info-item">费用总额:<span th:text="${totalFee}"></span></div>
<div class="info-item">个人支付:<span th:text="${personalPayment}"></span></div>
</div>
<div class="margin-top-10">实收金额:<span th:text="${actualReceiptAmount}"></span></div>
<div class="margin-top-10">实收金额:<span th:text="${actualReceiptAmountChinese}"></span></div>
</div>
<div class="divider">
-----------------------------------------------------------------------------
</div>
<div class="table-container">
<table>
<tr class="text-35">
<th class="first-th">项目名称</th>
<th class="df-th">数量</th>
<th class="df-th">单价</th>
<th class="df-th">小计</th>
</tr>
<tr class="text-30" th:each="item : ${items}">
<td class="first-td" th:text="${item.name}"></td>
<td class="df-td" th:text="${item.quantity}"></td>
<td class="df-td" th:text="${item.unitPrice}"></td>
<td class="df-td" th:text="${item.subtotal}"></td>
</tr>
</table>
</div>
<div class="divider">
-----------------------------------------------------------------------------
</div>
<div class="terminal-info">
<div>终端编号:<span th:text="${terminalNumber}"></span></div>
<div>打印时间:<span th:text="${printTime}"></span></div>
</div>
<div class="tips">
<span>温馨提示</span><br>
<span>1.请取走全部凭条、并妥善保管</span><br>
<span>2.如果对缴费结算存在疑问,请到人工窗口咨询</span>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>挂号单</title>
</head>
<body>
<div style="font-size: 32px; margin-top: 20px;">
<div style="">
<div style="text-align: center; font-size: 40px; font-weight: 700;"><span th:text="${hospitalName}"></span></div>
<div style="text-align: center; font-weight: 700; font-size: 28px;">****<span th:text="${registeTerminalName}"></span></span>****</div>
<div style="text-align: center; font-weight: 700; margin-bottom: -10px; margin-top: -10px;">-----------------------------------------------------------------------------</div>
<div style="text-align: center; font-size: 40px; font-weight: 700;"><span th:text="${registeType}"></span></div>
<div style="text-align: center; font-weight: 700; margin-top: -15px;">-----------------------------------------------------------------------------</div>
</div>
<div style="font-weight: 700; font-size: 37px;">
<div style="margin-left: 35px;">&emsp;&emsp;名:<span th:text="${name}"></span></div>
<div style="margin-left: 35px;">&emsp;&emsp;别:<span th:text="${gender}"></span></div>
<div style="margin-left: 35px;">&emsp;&emsp;龄:<span th:text="${age}"></span></div>
<div style="margin-left: 35px;">出生日期:<span th:text="${birthDate}"></span></div>
<div style="margin-left: 35px;">&ensp;&ensp;号:<span th:text="${outpatientNumber}"></span></div>
<div style="margin-left: 35px;">入院科室:<span th:text="${department}"></span></div>
<div style="margin-left: 35px;">&ensp;&ensp;用:<span th:text="${totalFee}"></span>&thinsp;</div>
<div style="margin-left: 35px;">支付方式:<span th:text="${paymentMethod}"></span></div>
<div style="margin-left: 35px;">交易流水:<span style="font-size: 35px;" th:text="${transactionNumber}"></span></div>
</div>
<div style="font-weight: 700; margin-top: 8px;">
<!-- <div style="margin-top: -40px; text-align: center; ">-&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;</div>-->
<div style="font-size: 36px;margin-left: 35px; ">备注:缴费凭证,请妥善保管!</div>
<div style="font-size: 36px;margin-left: 35px;">&ensp;&ensp;号:<span th:text="${terminalNumber}"></span></div>
<div style="font-size: 36px;margin-left: 35px;">打印时间:<span th:text="${printTime}"></span></div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>门诊缴费</title>
</head>
<body>
<div style="font-size: 32px; margin-top: 20px;">
<div style="">
<div style="text-align: center; font-size: 40px; font-weight: 700;"><span th:text="${hospitalName}"></span></div>
<div style="text-align: center; font-weight: 700; font-size: 28px;">****<span th:text="${registeTerminalName}"></span></span>****</div>
<div style="text-align: center; font-weight: 700; margin-bottom: -10px; margin-top: -10px;">-----------------------------------------------------------------------------</div>
<div style="text-align: center; font-size: 40px; font-weight: 700;"><span th:text="${registeType}"></span></div>
<div style="text-align: center; font-weight: 700; margin-top: -15px;">-----------------------------------------------------------------------------</div>
</div>
<div style="font-weight: 700; font-size: 37px;">
<div style="margin-left: 35px;">&emsp;&emsp;名:<span th:text="${name}"></span></div>
<div style="margin-left: 35px;">&emsp;&emsp;别:<span th:text="${gender}"></span></div>
<div style="margin-left: 35px;">&emsp;&emsp;龄:<span th:text="${age}"></span></div>
<div style="margin-left: 35px;">出生日期:<span th:text="${birthDate}"></span></div>
<div style="margin-left: 35px;">&ensp;&ensp;用:<span th:text="${totalFee}"></span>&thinsp;</div>
<div style="margin-left: 35px;">支付方式:<span th:text="${paymentMethod}"></span></div>
<div style="margin-left: 35px;">交易流水:<span style="font-size: 35px;" th:text="${transactionNumber}"></span></div>
</div>
<div style="font-weight: 700; margin-top: 8px;">
<!-- <div style="margin-top: -40px; text-align: center; ">-&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;</div>-->
<div style="font-size: 36px;margin-left: 35px; ">备注:缴费凭证,请妥善保管!</div>
<div style="font-size: 36px;margin-left: 35px;">&ensp;&ensp;号:<span th:text="${terminalNumber}"></span></div>
<div style="font-size: 36px;margin-left: 35px;">打印时间:<span th:text="${printTime}"></span></div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>挂号单</title>
</head>
<style>
.x-center {
text-align: center;
}
.x-start {
text-align: left;
}
.x-end {
text-align: right;
}
.x-bold {
font-weight: 700;
}
</style>
<body>
<div style="font-size: 32px; margin-top: 20px;">
<div style="">
<div style="text-align: center; font-size: 40px; font-weight: 700;"><span th:text="${hospitalName}"></span></div>
<div style="text-align: center; font-weight: 700; font-size: 28px;">****<span th:text="${registeTerminalName}"></span></span>****</div>
<div style="text-align: center; font-weight: 700; margin-bottom: -10px; margin-top: -10px;">-----------------------------------------------------------------------------</div>
<div style="text-align: center; font-size: 40px; font-weight: 700;"><span th:text="${registeType}"></span></div>
<div style="text-align: center; font-weight: 700; margin-top: -15px;">-----------------------------------------------------------------------------</div>
</div>
<div style="font-weight: 700; font-size: 37px;">
<div style="margin-left: 35px;">&emsp;&emsp;名:<span th:text="${name}"></span></div>
<div style="margin-left: 35px;">&emsp;&emsp;别:<span th:text="${gender}"></span></div>
<div style="margin-left: 35px;">&emsp;&emsp;龄:<span th:text="${age}"></span></div>
<div style="margin-left: 35px;">出生日期:<span th:text="${birthDate}"></span></div>
<!-- <div style="margin-left: 35px;">卡&emsp;&emsp;号:<span th:text="${cardNumber}"></span></div>-->
<div style="margin-left: 35px;">&ensp;&ensp;号:<span th:text="${outpatientNumber}"></span></div>
<div style="margin-left: 35px;">就诊科室:<span th:text="${department}"></span></div>
<div style="margin-left: 35px;">出诊级别:<span th:text="${visitLevel}"></span></div>
<div style="margin-left: 35px;">就诊医生:<span th:text="${doctor}"></span></div>
<div style="margin-left: 35px;">&emsp;&emsp;序:<span th:text="${sequence}"></span></div>
<div style="margin-left: 35px;">挂号日期:<span th:text="${registerDate}"></span></div>
<div style="margin-left: 35px;">&ensp;&ensp;用:<span th:text="${totalFee}"></span>&thinsp;</div>
<div style="margin-left: 35px;">支付方式:<span th:text="${paymentMethod}"></span></div>
<!-- <div style="margin-left: 35px;">订&ensp;单&ensp;号:<span style="font-size: 36px;" th:text="${orderNumber}"></span></div>-->
<div style="margin-left: 35px;">交易流水:<span style="font-size: 35px;"
th:text="${transactionNumber}"></span></div>
<div style="width: 100%; text-align: center; margin-bottom: -20px;">
<img style="display: inline-block; "
th:src="${qrCodeBase64}"
alt="QR Code" />
</div>
</div>
<div style="font-weight: 700; margin-top: 8px;">
<div style="margin-top: -40px; text-align: center; ">-----------------------------------------------------------------------------</div>
<div style="font-size: 36px;margin-left: 35px; ">备注:凭此条退费,请妥善保管!</div>
<div style="font-size: 36px;margin-left: 35px;">&ensp;&ensp;号:<span th:text="${terminalNumber}"></span></div>
<div style="font-size: 36px;margin-left: 35px;">打印时间:<span th:text="${printTime}"></span></div>
</div>
</div>
</body>
</html>

Binary file not shown.

Binary file not shown.

View File

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

View File

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