Compare commits

...

16 Commits

34 changed files with 2613 additions and 5 deletions

2
README.md Normal file
View File

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

113
pom.xml
View File

@@ -4,10 +4,11 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.dpkj</groupId> <groupId>com.dpkj</groupId>
<artifactId>银医通-澜沧中医院-DLL-台式机</artifactId> <!-- 银医通-澜沧中医院-DLL-台式机 -->
<artifactId>yinyitong-dll-stand</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>ems-express-call-dll</name> <name>yinyitong-dll-stand</name>
<description>ems-express-call-dll</description> <description>银医通-澜沧中医院-DLL-台式机</description>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -34,11 +35,13 @@
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
@@ -50,12 +53,14 @@
<artifactId>hutool-crypto</artifactId> <artifactId>hutool-crypto</artifactId>
<version>${hutool.version}</version> <version>${hutool.version}</version>
</dependency> </dependency>
<!-- 调用DLL --> <!-- 调用DLL -->
<dependency> <dependency>
<groupId>net.java.dev.jna</groupId> <groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId> <artifactId>jna</artifactId>
<version>${jna.version}</version> <version>${jna.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.java.dev.jna</groupId> <groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId> <artifactId>jna-platform</artifactId>
@@ -68,15 +73,115 @@
<artifactId>pdfbox</artifactId> <artifactId>pdfbox</artifactId>
<version>${pdfbox.version}</version> <version>${pdfbox.version}</version>
</dependency> </dependency>
<!-- json处理-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>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>
<!-- 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>
</dependencies> </dependencies>
<build> <build>
<finalName>ems-express-call-dll</finalName> <finalName>${project.artifactId}</finalName>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> </plugin>
<!-- 跳过单元测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<!-- 不需要分开打包的就将下面的插件全部注释-->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-dependency-plugin</artifactId>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <id>copy-dependencies</id>-->
<!-- <phase>prepare-package</phase>-->
<!-- <goals>-->
<!-- <goal>copy-dependencies</goal>-->
<!-- </goals>-->
<!-- <configuration>-->
<!-- &lt;!&ndash; 拷贝项目依赖包到lib/目录下 &ndash;&gt;-->
<!-- <outputDirectory>${project.build.directory}/lib</outputDirectory>-->
<!-- <overWriteReleases>false</overWriteReleases>-->
<!-- <overWriteSnapshots>false</overWriteSnapshots>-->
<!-- <overWriteIfNewer>true</overWriteIfNewer>-->
<!-- </configuration>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<!-- <plugin>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- <configuration>-->
<!-- <layout>ZIP</layout>-->
<!-- <includes>-->
<!-- <include>-->
<!-- <groupId>nothing</groupId>-->
<!-- <artifactId>nothing</artifactId>-->
<!-- </include>-->
<!-- </includes>-->
<!-- </configuration>-->
<!-- </plugin>-->
</plugins> </plugins>
<!--打包时将resource下的文件一起打包--> <!--打包时将resource下的文件一起打包-->
<resources> <resources>

View File

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

View File

@@ -0,0 +1,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,119 @@
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 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 RESET = "Reset";
/**
* 扫描条码
*/
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.getMessage());
return Result.error(ErrorEnum.NULL_POINTER_EXCEPTION);
}
/**
* 所有的运行时异常,抛出异常
* @param throwable 异常
* @return Result
*/
@ExceptionHandler(value = Throwable.class)
public Result<String> handleException(Throwable throwable) {
log.error("异常类型: {}, {}, 信息为: {}", throwable.getCause(), throwable.getClass(), throwable.getMessage());
if (throwable instanceof RRException){
RRException rrException = (RRException) throwable;
return Result.error(rrException.getCode(), rrException.getMsg());
}
return Result.error(ErrorEnum.RUNTIME_EXCEPTION);
}
/**
* http信息无可读
* @param e 异常
* @return Result
*/
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public Result<String> httpMessageNotReadAbleException(HttpMessageNotReadableException e){
log.warn("异常类型: {} 无可读信息: {}", e.getClass(), e.getMessage());
return Result.error(ErrorEnum.HTTP_MESSAGE_NOT_READABLE_EXCEPTION);
}
/**
* 运行时异常
* @param e 运行异常对象
* @return Result
*/
@ExceptionHandler(value = RuntimeException.class)
public Result<String> runtimeException(RuntimeException e){
log.error("运行时异常:{}", e.getMessage());
if (e instanceof RRException){
RRException rrException = (RRException) e;
return Result.error(rrException.getCode(), rrException.getMsg());
}
return Result.error(ErrorEnum.RUNTIME_EXCEPTION);
}
/**
* 请求不支持
* @return Result
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public Result<String> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e){
log.warn("暂不支持该请求: {}", e.getMessage());
return Result.error("暂不支持此请求方式");
}
/**
* 参数类型错误
* @return Result
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public Result<String> methodArgument(MethodArgumentTypeMismatchException e){
log.warn("参数发生错误: {}", e.getMessage());
return Result.error("参数发生错误");
}
/**
* 缺少请求参数
* @param e 缺少请求参数异常
* @return Result
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public Result<String> exception(MissingServletRequestParameterException e){
log.warn("缺少请求参数: {}", e.getMessage());
return Result.error("缺少请求参数");
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,114 @@
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;
/**
* 第三方服务,主要是调用打印机等
*
* @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;
/**
* 利盟台式机-立体机 接口请求
*
* @param lexMarkDTO 请求DTO
* @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);
}
}

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

View File

@@ -0,0 +1,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,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,156 @@
package com.dpkj.modules.keypad.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.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() {
log.info("开始执行密码输入流程");
// 连接数字键盘
log.info("准备连接数字键盘设备");
LexMarkDTO connect = new LexMarkDTO();
connect.setDevName(LexMarkConst.ENCRYPTOR);
connect.setActionName(MiddlewareFunctionsConst.OPEN_CONNECTION);
connect.setCallID(1000);
JSONObject connectParam = new JSONObject();
connectParam.put("TimeOut", 90000);
connectParam.put("ServiceName", LexMarkConst.ENCRYPTOR);
connect.setParam(connectParam.toJSONString());
log.info("调用设备连接方法,参数: {}", connect);
IDCardReadResultVO connectResult = thirdServiceUtil.callDevice(connect, IDCardReadResultVO.class);
log.info("设备连接结果: {}", connectResult);
WebSocketClient webSocketClient = null;
try {
if (connectResult.getResult() == 0) {
log.info("数字键盘设备连接成功,准备进行明文输入");
// 明文输入
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", 30000);
plaintextParam.put("ActiveKeys", 81919);
plaintextParam.put("TerminateKeys", 1024);
plaintext.setParam(plaintextParam.toJSONString());
log.info("调用明文输入方法,参数: {}", plaintext);
// 创建 WebSocket 客户端
log.info("创建 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("明文输入操作结果: {}", plaintextResult);
if (plaintextResult.getResult() == 0) {
log.info("明文输入成功,开始等待确认");
try {
// 适当延长等待时间
while (true) {
TimeUnit.SECONDS.sleep(1);
Boolean confirm = webSocketClient.getConfirm();
if (confirm) {
log.info("收到确认信息,结束等待");
break;
}
}
} catch (InterruptedException e) {
log.error("等待确认信息时被中断", e);
e.printStackTrace();
}
String password = webSocketClient.getPassword();
log.info("密码输入完成,密码为: {}", password);
return Result.ok("密码输入完成!", password);
} else {
log.error("密码输入异常!详情:{}", connectResult.getDesc());
return Result.error("密码输入异常!详情:" + connectResult.getDesc());
}
} else {
log.error("数字键盘设备连接失败!详情:{}", connectResult.getDesc());
return Result.error("数字键盘设备连接失败!详情:" + connectResult.getDesc());
}
} finally {
if (webSocketClient != null) {
log.info("关闭 WebSocket 客户端");
webSocketClient.close();
}
// 关闭身份证读卡设备
log.info("准备关闭数字键盘设备");
LexMarkDTO close = new LexMarkDTO();
close.setDevName(LexMarkConst.ENCRYPTOR);
close.setCallID(0);
close.setActionName(MiddlewareFunctionsConst.CLOSE_CONNECTION);
log.info("调用设备关闭方法,参数: {}", close);
IDCardReadResultVO closeResult = thirdServiceUtil.callDevice(close, IDCardReadResultVO.class);
log.info("设备关闭结果: {}", closeResult);
if (closeResult.getResult() != 0) {
log.error("数字键盘设备关闭异常!");
}
}
}
/**
* 取消输入 (明文或者密文)
* @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,44 @@
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.service.PrintService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 挂号controller
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @since 2025-02-08 11:49:46
*/
@RestController
@RequestMapping("/print")
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("/register")
public Result<LexMarkResultDTO> registerByFilePath(@RequestParam(defaultValue = "D:\\images") String filePath){
return Result.ok((LexMarkResultDTO)printService.printImage(null, null, 0, 0, filePath));
}
}

View File

@@ -0,0 +1,34 @@
package com.dpkj.modules.print.service;
import com.alibaba.fastjson.JSONObject;
/**
* 打印服务接口
*
* @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");
}
}

View File

@@ -0,0 +1,55 @@
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.utils.TemplateUtils;
import com.dpkj.common.utils.ThirdService;
import com.dpkj.modules.print.service.PrintService;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;
import javax.annotation.Resource;
/**
* 挂号服务打印
*
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
* @version 1.0
* @since 2025-02-08 10:36:25
*/
@Service("registerService")
public class RegisterServiceImpl implements PrintService {
@Resource
private ThirdService thirdService;
@Override
public LexMarkResultDTO<LexMarkResultDTO.Param> printImage(JSONObject data, String template, int width, int height, String saveDir) {
StringBuilder filePath = new StringBuilder(saveDir);
// 校验是否选中了模板,如果没选中模板的话则不需要另外生成了
if ( !StringUtils.isEmpty(template) && !StringUtils.isEmpty(saveDir)){
byte[] image = new TemplateUtils().generateReceiptImage(data, template, width, height, 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", "ReceiptForm");
param.put("mediaName", "ReceiptMedia");
param.put("alignment", 0);
param.put("offsetX", 0);
param.put("offsetY", 0);
param.put("resolution", 1);
param.put("mediaCtrl", 1);
param.put("fields", "LOGO=" + filePath);
lexMarkDTO.setParam(param.toJSONString());
return thirdService.callDevice(lexMarkDTO, LexMarkResultDTO.Param.class);
}
}

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,295 @@
package com.dpkj.modules.readcard.service.impl;
import com.alibaba.fastjson.JSON;
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.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;
/**
* 身份证读取
* 1. 连接身份证读取设备
* 2. 非接触身份证读取
* 3. 关闭设备
*
* @return
*/
@Override
public Result IDCardReader() {
Result result = null;
log.info("开始执行身份证读取流程");
try {
log.info("准备连接非接身份证读卡模块");
// 连接非接身份证读卡模块
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("调用设备连接方法,参数: {}", connect);
IDCardReadResultVO connectResult = thirdServiceUtil.callDevice(connect, IDCardReadResultVO.class);
log.info("设备连接结果: {}", connectResult);
if (connectResult.getResult() == 0) {
log.info("设备连接成功,准备读取身份证");
// 身份证读取
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", 0);
readParam.put("TrackMap", 776);
read.setParam(readParam.toJSONString());
log.info("调用设备读取方法,参数: {}", read);
IDCardReadResultVO readResult = thirdServiceUtil.callDevice(read, IDCardReadResultVO.class);
log.info("设备读取结果: {}", readResult);
if (readResult.getResult() == 0) {
log.info("身份证读取成功,开始解析身份证信息");
IDCardReadResultVO.Param resultParam = JSON.parseObject(readResult.getParam().toString(), IDCardReadResultVO.Param.class);
if (resultParam.getResult() == 0) {
log.info("身份证信息解析成功,开始封装用户信息");
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]);
// 正面
userInfoVO.setFrontimage(resultParam.getFrontimage().getDatas());
// 反面
userInfoVO.setBackimage(resultParam.getBackimage().getDatas());
result = Result.ok(userInfoVO);
log.info("用户信息封装成功,返回结果: {}", result);
} else {
result = Result.error("身份证读取失败!");
log.error("身份证信息解析失败,返回结果: {}", result);
}
} else {
result = Result.error(readResult.getDesc());
log.error("身份证读取失败,错误信息: {}, 返回结果: {}", readResult.getDesc(), result);
}
} else {
result = Result.error(connectResult.getDesc());
log.error("设备连接失败,错误信息: {}, 返回结果: {}", connectResult.getDesc(), result);
}
} finally {
log.info("准备关闭身份证读卡设备");
// 关闭身份证读卡设备
LexMarkDTO close = new LexMarkDTO();
close.setDevName(LexMarkConst.ID_CARD_READ);
close.setCallID(0);
close.setActionName(MiddlewareFunctionsConst.CLOSE_CONNECTION);
log.info("调用设备关闭方法,参数: {}", close);
IDCardReadResultVO closeResult = thirdServiceUtil.callDevice(close, IDCardReadResultVO.class);
log.info("设备关闭结果: {}", closeResult);
if (closeResult.getResult() != 0) {
log.error("身份证读卡设备关闭异常!");
}
}
log.info("身份证读取流程结束,最终返回结果: {}", result);
return result;
}
/**
* 社保卡读取
* 1. 连接社保卡读取设备
* 2. 进卡读卡
* 3. 读卡器上电
* 4. 社保卡信息读取
*
* @return
*/
@Override
public Result SocialSecurityCardReader() {
log.info("开始执行社保卡读取流程");
// 社保卡读卡设备连接
log.info("准备连接社保卡读卡设备");
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("调用设备连接方法,参数: {}", connect);
IDCardReadResultVO connectResult = thirdServiceUtil.callDevice(connect, IDCardReadResultVO.class);
log.info("设备连接结果: {}", connectResult);
if (connectResult.getResult() == 0) {
log.info("社保卡读卡设备连接成功,准备进卡");
// 进卡
log.info("准备执行进卡操作");
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", 0);
enterTheCard.setParam(enterTheCardParam.toJSONString());
log.info("调用进卡方法,参数: {}", enterTheCard);
IDCardReadResultVO enterTheCardResult = thirdServiceUtil.callDevice(enterTheCard, IDCardReadResultVO.class);
log.info("进卡操作结果: {}", enterTheCardResult);
if (enterTheCardResult.getResult() == 0) {
log.info("进卡操作成功,准备上电");
// 上电
log.info("准备执行上电操作");
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("调用上电方法,参数: {}", powerOn);
IDCardReadResultVO powerOnResult = thirdServiceUtil.callDevice(powerOn, IDCardReadResultVO.class);
log.info("上电操作结果: {}", powerOnResult);
if (powerOnResult.getResult() == 0) {
log.info("上电操作成功,准备读取社保卡信息");
// 社保卡信息读取
log.info("准备读取社保卡信息");
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("调用读取社保卡信息方法,参数: {}", socialSecurityCardReader);
IDCardReadResultVO socialSecurityCardReaderResult = thirdServiceUtil.callDevice(socialSecurityCardReader, IDCardReadResultVO.class);
log.info("读取社保卡信息结果: {}", socialSecurityCardReaderResult);
if (socialSecurityCardReaderResult.getResult() == 0) {
log.info("社保卡信息读取成功,开始解析信息");
// 社保信息
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("社保卡信息解析完成,返回成功结果");
return Result.ok(socialSecurityCardInfoVO);
} else {
log.error("社保卡信息读取失败,详情:{}", socialSecurityCardReaderResult.getDesc());
return Result.error("社保卡信息读取失败!详情:" + socialSecurityCardReaderResult.getDesc());
}
} else {
log.error("社保卡读取设备上电失败,详情:{}", powerOnResult.getDesc());
return Result.error("社保卡读取设备上电失败!详情:" + powerOnResult.getDesc());
}
} else {
log.error("社保卡读取设备进卡失败,详情:{}", enterTheCardResult.getDesc());
return Result.error("社保卡读取设备进卡失败!详情:" + enterTheCardResult.getDesc());
}
} else {
log.error("社保卡读取设备连接失败,详情:{}", connectResult.getDesc());
return Result.error("社保卡读取设备连接失败!详情:" + connectResult.getDesc());
}
}
/**
* 退卡并关闭设备
*
* @return
*/
@Override
public Result cardRefund() {
log.info("开始执行社保卡退卡流程");
// 社保卡退卡
log.info("准备发起社保卡退卡请求");
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("调用设备退卡方法,参数: {}", cardRefund);
IDCardReadResultVO cardRefundResult = thirdServiceUtil.callDevice(cardRefund, IDCardReadResultVO.class);
log.info("设备退卡操作结果: {}", cardRefundResult);
if (cardRefundResult.getResult() == 0) {
log.info("社保卡退卡成功,准备关闭社保卡读卡设备");
// 退卡成功再关闭社保卡读卡设备
LexMarkDTO close = new LexMarkDTO();
close.setDevName(LexMarkConst.CARD_READER);
close.setCallID(0);
close.setActionName(MiddlewareFunctionsConst.CLOSE_CONNECTION);
log.info("调用设备关闭方法,参数: {}", close);
IDCardReadResultVO closeResult = thirdServiceUtil.callDevice(close, IDCardReadResultVO.class);
log.info("设备关闭操作结果: {}", closeResult);
if (closeResult.getResult() != 0) {
log.error("社保卡读卡设备关闭异常!");
}
log.info("社保卡退卡流程完成,退卡成功");
return Result.ok("退卡成功!");
} else {
log.error("退卡失败!请联系工作人员!");
return Result.error("退卡失败!请联系工作人员!");
}
}
}

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

@@ -25,3 +25,12 @@ server:
enabled: true enabled: true
min-response-size: 1024 min-response-size: 1024
mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/* mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*
# 自定义app参数
app:
custom:
lexMarkServiceIp: http://127.0.0.1
lexMarkServicePort: 12346

View File

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