Compare commits

..

4 Commits

Author SHA1 Message Date
王志成 f36e6703a3 删除用户身份证实体,调整与台式机一致 2025-02-17 17:06:20 +08:00
王志成 27d8b818c0 Merge remote-tracking branch 'origin/1.0' into 1.0
# Conflicts:
#	src/main/java/com/dpkj/modules/cardReader/service/impl/CardReaderServiceImpl.java
#	src/main/java/com/dpkj/modules/cardReader/utils/CardReaderSdk.java
2025-02-17 17:03:44 +08:00
王志成 c0b1e80525 身份证读取(完成)、社保卡读取(待调试) 2025-02-17 17:00:27 +08:00
王志成 47c9d8dc5a 身份证读取(完成)、社保卡读取(待调试) 2025-02-17 16:58:54 +08:00
20 changed files with 7124 additions and 269 deletions

View File

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

View File

@ -14,20 +14,19 @@ import org.springframework.web.bind.annotation.RestController;
*/ */
@Slf4j @Slf4j
@RestController @RestController
@RequestMapping("cardReader") @RequestMapping("readCard")
public class CardReaderController { public class CardReaderController {
@Autowired @Autowired
private CardReaderService cardReaderService; private CardReaderService cardReaderService;
/** /**
* 连接设备并读取身份证信息 * 连接设备并读取身份证信息
*
* @return * @return
*/ */
@GetMapping("connectedDevice") @GetMapping("IDCardReader")
public Result<?> connectedDevice(@RequestParam(name = "devName") String devName) { public Result IDCardReader() {
try { try {
return cardReaderService.connectedDevice(devName); return cardReaderService.IDCardReader();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
log.info("设备连接失败 {}", e.getMessage()); log.info("设备连接失败 {}", e.getMessage());
@ -38,13 +37,12 @@ public class CardReaderController {
/** /**
* 连接设备并读取社保卡信息 * 连接设备并读取社保卡信息
*
* @return * @return
*/ */
@GetMapping("readSocialSecurityCard") @GetMapping("SocialSecurityCardReader")
public Result<?> readSocialSecurityCard(@RequestParam(name = "devName") String devName) { public Result<?> SocialSecurityCardReader() {
try { try {
return cardReaderService.readSocialSecurityCard(devName); return cardReaderService.SocialSecurityCardReader();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
log.info("连接设备并读取社保卡信息失败 {}", e.getMessage()); log.info("连接设备并读取社保卡信息失败 {}", e.getMessage());

View File

@ -5,7 +5,7 @@ import com.dpkj.common.vo.Result;
public interface CardReaderService { public interface CardReaderService {
Result<?> connectedDevice(String devName); Result IDCardReader();
Result<?> readSocialSecurityCard(String devName); Result SocialSecurityCardReader();
} }

View File

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

View File

@ -273,7 +273,7 @@ public interface CardReaderSdk extends Library {
* @param ReaderHandle ICCReaderOpen函数所返回的值 * @param ReaderHandle ICCReaderOpen函数所返回的值
* @return 返回错误信息 = 0 表示成功,非0失败 * @return 返回错误信息 = 0 表示成功,非0失败
*/ */
Long PICC_Reader_ReadIDCard(Long ReaderHandle, byte[] errMsg); Long PICC_Reader_ReadIDCard(Long ReaderHandle);
/** 以下函数须在 “PICC_Reader_ReadIDCard” 函数执行成功之后调用,否则获取不到有效信息 */ /** 以下函数须在 “PICC_Reader_ReadIDCard” 函数执行成功之后调用,否则获取不到有效信息 */
/** /**

View File

@ -1,181 +0,0 @@
package com.dpkj.modules.cardReader.utils;
import com.dpkj.common.exception.RRException;
import lombok.Data;
import java.io.UnsupportedEncodingException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.function.Consumer;
/**
* 有关身份证的基本信息
*/
@Data
public class IdCardInfo {
/**
* 名字
*/
private String name;
/**
* 性别
*/
private String sex;
/**
* 民族
*/
private String nation;
/**
* 出生日期
*/
private LocalDateTime brith;
/**
* 住址
*/
private String address;
/**
* 身份证号码
*/
private String certNo;
/**
* 签发机关
*/
private String department;
/**
* 有效日期
*/
private LocalDateTime effectDate;
/**
* 截至日期
*/
private LocalDateTime expireDate;
/**
* 外国人姓名
*/
private String enName;
/**
* 外国人国籍
*/
private String nationalityCode;
/**
* 注册过的sdk实例
*/
private CardReaderSdk cardReaderSdk;
/**
* 是否有指纹
*/
private Boolean fingerExist;
/**
* 指纹数据
*/
private byte[] fingerData;
/**
* 港澳台通行证号码
*/
private String TXZHM;
/**
* 港澳台通行证签发次数
*/
private Integer TXZQFCS;
/**
* 外国人换证次数
*/
private Integer HZCS;
public IdCardInfo(CardReaderSdk cardReaderSdk, Long handle){
if ( cardReaderSdk == null ){
throw new RRException("sdk初始化失败");
}
this.cardReaderSdk = cardReaderSdk;
byte[] errMsg = new byte[999];
cardReaderSdk.PICC_Reader_ReadIDCard(handle, errMsg);
try {
String gbk = new String(errMsg, "GBK").trim();
if (!gbk.equals("")) {
throw new RRException();
}
} catch (Exception e) {
throw new RRException("身份证信息读取失败");
}
this.name = this.convert2Str(this.cardReaderSdk::GetName, "姓名转换失败");
this.sex = this.convert2Str(this.cardReaderSdk::GetSex, "性别转换失败");
this.nation = this.convert2Str(this.cardReaderSdk::GetNation, "民族转换失败");
String br = this.convert2Str(this.cardReaderSdk::GetBirth, "出生日期转换失败");
if ( br != null && br != "" ){
this.brith = this.str2LocalDateTime(br);
}
this.address = this.convert2Str(this.cardReaderSdk::GetAddress, "住址转换失败");
this.certNo = this.convert2Str(this.cardReaderSdk::GetCertNo, "身份证转换失败");
this.department = this.convert2Str(this.cardReaderSdk::GetDepartemt, "签发机关转换失败");
String eff = this.convert2Str(this.cardReaderSdk::GetEffectDate, "过期时间转换失败");
if ( eff != null && eff != "" ){
this.effectDate = this.str2LocalDateTime(eff);
}
String exp = this.convert2Str(this.cardReaderSdk::GetExpireDate, "截至日期转换失败");
if ( exp != null && exp != "" ){
this.expireDate = this.str2LocalDateTime(exp);
}
this.enName = this.convert2Str(this.cardReaderSdk::GetEnName, "外国人姓名转换失败");
this.nationalityCode = this.convert2Str(this.cardReaderSdk::GetNationalityCode, "外国人国籍转换失败");
// this.fingerExist = this.cardReaderSdk.IsFingerExist() != 0;
// if ( this.fingerExist ) {
// this.fingerData = this.convert2Str(this.cardReaderSdk::GetFingerprint, "指纹数据转换失败").getBytes(StandardCharsets.UTF_8);
// }
}
private String convert2Str(Consumer<byte[]> function, String tip) {
byte[] param = new byte[256];
function.accept(param);
try {
// 检查字节数组是否为空或者只包含值为 0 的字节
boolean isAllZero = true;
for (byte b : param) {
if (b != 0) {
isAllZero = false;
break;
}
}
if (isAllZero) {
return null;
}
return new String(param, "GBK").trim();
}catch (Exception e){
throw new RRException(tip == null ? "参数转换失败" : tip);
}
}
private LocalDateTime str2LocalDateTime(String str){
// 定义日期格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
// 将字符串解析为 LocalDate
LocalDate localDate = LocalDate.parse(str, formatter);
// LocalDate 转换为 LocalDateTime设置时间为 00:00:00
return localDate.atStartOfDay();
}
}

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.