瀏覽代碼

活体识别

haogh 8 月之前
父節點
當前提交
93623c3d0f
共有 24 個文件被更改,包括 1459 次插入12 次删除
  1. 49 0
      sql/update.sql
  2. 23 0
      src/cn/hmsoft/art/constants/Constants.java
  3. 11 0
      src/cn/hmsoft/art/data/dao/live/TxLivenessDetailDao.java
  4. 10 0
      src/cn/hmsoft/art/data/dao/live/TxStdLiveLogDao.java
  5. 5 0
      src/cn/hmsoft/art/data/dao/std/StdRegDao.java
  6. 192 0
      src/cn/hmsoft/art/data/model/live/TxLivenessDetail.java
  7. 72 0
      src/cn/hmsoft/art/data/model/live/TxStdLiveLog.java
  8. 56 0
      src/cn/hmsoft/art/data/model/live/txStdMaterialLog.java
  9. 34 0
      src/cn/hmsoft/art/data/response/live/DetectAuthBean.java
  10. 56 0
      src/cn/hmsoft/art/data/response/live/DetectInfoBean.java
  11. 20 0
      src/cn/hmsoft/art/data/response/live/DetectInfoBestFrame.java
  12. 163 0
      src/cn/hmsoft/art/data/response/live/DetectInfoDetail.java
  13. 20 0
      src/cn/hmsoft/art/data/response/live/DetectInfoResult.java
  14. 69 0
      src/cn/hmsoft/art/data/response/live/DetectInfoText.java
  15. 20 0
      src/cn/hmsoft/art/data/response/live/DetectInfoVideoData.java
  16. 53 0
      src/cn/hmsoft/art/data/vo/live/DetectAuthVO.java
  17. 42 0
      src/cn/hmsoft/art/data/vo/live/DetectInfoVO.java
  18. 76 0
      src/cn/hmsoft/art/enrol/control/tencent/LiveRecognitionControl.java
  19. 10 0
      src/cn/hmsoft/art/helper/ArtParamHelper.java
  20. 28 0
      src/cn/hmsoft/art/helper/GsonHelper.java
  21. 59 12
      src/cn/hmsoft/art/helper/ImageUtil.java
  22. 118 0
      src/cn/hmsoft/art/helper/TencentLiveHelper.java
  23. 77 0
      src/cn/hmsoft/art/helper/VideoHelper.java
  24. 196 0
      src/cn/hmsoft/art/service/live/LiveRecognitionService.java

+ 49 - 0
sql/update.sql

@@ -0,0 +1,49 @@
+-- 2024-10-24
+INSERT INTO `cf_enrol_param`(`PARAM_TYPE`, `PARAM_NAME`, `PARAM_VALUE`, `PARAM_NOTE`) VALUES ('Enrol', 'TencentLiveInfoType', '0', '活体识别获取结果类型');
+INSERT INTO `cf_enrol_param`(`PARAM_TYPE`, `PARAM_NAME`, `PARAM_VALUE`, `PARAM_NOTE`) VALUES ('Enrol', 'TencentLiveRedirectUrl', 'https://10047.qmth.com.cn/bk/wechat/wechat.jsp', '跳转地址');
+INSERT INTO `cf_enrol_param`(`PARAM_TYPE`, `PARAM_NAME`, `PARAM_VALUE`, `PARAM_NOTE`) VALUES ('Enrol', 'TencentLiveRuleId', '1', '活体识别业务唯一标识');
+INSERT INTO `cf_enrol_param`(`PARAM_TYPE`, `PARAM_NAME`, `PARAM_VALUE`, `PARAM_NOTE`) VALUES ('Enrol', 'TencentLiveSecretId', 'AKIDEJCwMdidMr8C7pYFpDTbcgYSSXP8fyAB', '活体识别ID');
+INSERT INTO `cf_enrol_param`(`PARAM_TYPE`, `PARAM_NAME`, `PARAM_VALUE`, `PARAM_NOTE`) VALUES ('Enrol', 'TencentLiveSecretKey', 'ymTyLWcXEhzBD6rfbSDoIZhjkgeV9f4J', '活体识别秘钥');
+
+
+CREATE TABLE `tx_liveness_detail`
+(
+    `id`               int(0)                                                  NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `biz_token`        varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'token',
+    `req_time`         bigint(0)                                               NULL DEFAULT NULL COMMENT '请求时间',
+    `seq`              varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求的唯一标记',
+    `id_card`          varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL DEFAULT NULL COMMENT '身份证号',
+    `name`             varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL DEFAULT NULL COMMENT '姓名',
+    `sim`              varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL DEFAULT NULL COMMENT '相似度',
+    `need_charge`      bit(1)                                                  NULL DEFAULT NULL COMMENT '是否需要收费0:不收费,1:收费',
+    `error_code`       int(0)                                                  NULL DEFAULT NULL COMMENT '本次活体一比一最终结果',
+    `error_msg`        varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '本次活体一比一最终结果描述',
+    `live_status`      int(0)                                                  NULL DEFAULT NULL COMMENT '本次活体结果',
+    `live_msg`         varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '本次活体结果描述',
+    `compare_status`   int(0)                                                  NULL DEFAULT NULL COMMENT '本次一比一结果',
+    `compare_msg`      varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL DEFAULT NULL COMMENT '本次一比一结果描述',
+    `compare_lib_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL DEFAULT NULL COMMENT '比对库源类型',
+    `liveness_mode`    int(0)                                                  NULL DEFAULT NULL COMMENT '活体检测类型',
+    PRIMARY KEY (`id`) USING BTREE
+) COMMENT = '活体识别对账流水记录表';
+
+CREATE TABLE `tx_std_live_log`
+(
+    `id`          int(0)                                                  NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `std_id`      int(0)                                                  NULL DEFAULT NULL COMMENT '考生ID',
+    `biz_token`   varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '腾讯活体识别token',
+    `create_time` datetime(0)                                             NULL DEFAULT NULL COMMENT '创建时间',
+    `pull_flag`   varchar(4) CHARACTER SET utf8 COLLATE utf8_general_ci   NULL DEFAULT NULL COMMENT '是否拉取标志:0:未拉取,1:已成功拉取',
+    PRIMARY KEY (`id`) USING BTREE
+) COMMENT = '考生请求token记录表';
+
+CREATE TABLE `tx_std_material_log`
+(
+    `id`          int(0)                                          NOT NULL AUTO_INCREMENT,
+    `std_id`      int(0)                                          NULL DEFAULT NULL COMMENT '考生ID',
+    `std_photo`   text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '考生base64图片',
+    `create_time` datetime(0)                                     NULL DEFAULT NULL COMMENT '创建时间',
+    PRIMARY KEY (`id`) USING BTREE
+) COMMENT = '考生失败材料记录表';
+
+

+ 23 - 0
src/cn/hmsoft/art/constants/Constants.java

@@ -0,0 +1,23 @@
+package cn.hmsoft.art.constants;
+
+/**
+ * @Description 系统常量
+ * @Author haoguanghui
+ * @date 2024/10/23
+ */
+public class Constants {
+
+    public final static String SYSTEM_BUSY = "系统繁忙,请稍后重试";
+
+    //False标志
+    public final static String FALSE_FLAG = "0";
+
+    //True标志
+    public final static String TRUE_FLAG = "1";
+
+    // 图片后缀
+    public static final String PHOTO_SUFFIX = ".jpg";
+
+    //图片后缀不带.
+    public static final String PHOTO_SUFFIX_WITHOUT_DOT = "jpg";
+}

+ 11 - 0
src/cn/hmsoft/art/data/dao/live/TxLivenessDetailDao.java

@@ -0,0 +1,11 @@
+package cn.hmsoft.art.data.dao.live;
+
+import cn.hmsoft.art.data.model.live.TxLivenessDetail;
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import org.springframework.stereotype.Repository;
+
+
+@Repository
+public class TxLivenessDetailDao extends PlatformDaoSupport<TxLivenessDetail> {
+
+}

+ 10 - 0
src/cn/hmsoft/art/data/dao/live/TxStdLiveLogDao.java

@@ -0,0 +1,10 @@
+package cn.hmsoft.art.data.dao.live;
+
+import cn.hmsoft.art.data.model.live.TxStdLiveLog;
+import cn.hmsoft.jdbc.core.PlatformDaoSupport;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class TxStdLiveLogDao extends PlatformDaoSupport<TxStdLiveLog> {
+
+}

+ 5 - 0
src/cn/hmsoft/art/data/dao/std/StdRegDao.java

@@ -353,5 +353,10 @@ public class StdRegDao extends PlatformDaoSupport<StdReg> {
 		String sql = "select std_id,cert_id from std_reg ";
 		String sql = "select std_id,cert_id from std_reg ";
 		return this.listBySql(sql);
 		return this.listBySql(sql);
 	}
 	}
+
+	public StdReg findStdRegByCertAndName(String cert_id, String std_name) {
+		String sql = "select std_id,cert_id,std_name from std_reg where cert_id=? and std_name=? ";
+		return this.findBySql(sql, cert_id, std_name);
+	}
 	
 	
 }
 }

+ 192 - 0
src/cn/hmsoft/art/data/model/live/TxLivenessDetail.java

@@ -0,0 +1,192 @@
+package cn.hmsoft.art.data.model.live;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+import java.io.Serializable;
+
+/**
+ * @Description 活体识别对账流水记录表
+ * @Author haoguanghui
+ * @date 2024/10/24
+ */
+@Table(tableName = "tx_liveness_detail", keyColumn = "id", sequenceName = "")
+public class TxLivenessDetail implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    // ID主键
+    private Integer id;
+
+    //活体识别token
+    private String biz_token;
+
+    // 请求时间戳
+    private String req_time;
+
+    // 本次活体一比一请求的唯一标记
+    private String seq;
+
+    // 参与本次活体一比一的身份证号
+    private String id_card;
+
+    //参与本次活体一比一的姓名
+    private String name;
+
+    //本次活体一比一的相似度
+    private String sim;
+
+    //本次活体一比一是否收费
+    private Boolean need_charge;
+
+    // 本次活体一比一最终结果。0为成功
+    private Integer error_code;
+
+    // 本次活体一比一最终结果描述
+    private String error_msg;
+
+    //本次活体结果。0为成功
+    private Integer live_status;
+
+    //本次活体结果描述
+    private String live_msg;
+
+    //本次一比一结果。0为成功
+    private Integer compare_status;
+
+    //本次一比一结果描述
+    private String compare_msg;
+
+    //比对库源类型
+    private String compare_lib_type;
+
+    //活体检测类型(0:未知 1:数字活体 2:动作活体 3:静默活体 4:一闪活体(动作+光线))
+    private Integer liveness_mode;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getBiz_token() {
+        return biz_token;
+    }
+
+    public void setBiz_token(String biz_token) {
+        this.biz_token = biz_token;
+    }
+
+    public String getReq_time() {
+        return req_time;
+    }
+
+    public void setReq_time(String req_time) {
+        this.req_time = req_time;
+    }
+
+    public String getSeq() {
+        return seq;
+    }
+
+    public void setSeq(String seq) {
+        this.seq = seq;
+    }
+
+    public String getId_card() {
+        return id_card;
+    }
+
+    public void setId_card(String id_card) {
+        this.id_card = id_card;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getSim() {
+        return sim;
+    }
+
+    public void setSim(String sim) {
+        this.sim = sim;
+    }
+
+    public Boolean getNeed_charge() {
+        return need_charge;
+    }
+
+    public void setNeed_charge(Boolean need_charge) {
+        this.need_charge = need_charge;
+    }
+
+    public Integer getError_code() {
+        return error_code;
+    }
+
+    public void setError_code(Integer error_code) {
+        this.error_code = error_code;
+    }
+
+    public String getError_msg() {
+        return error_msg;
+    }
+
+    public void setError_msg(String error_msg) {
+        this.error_msg = error_msg;
+    }
+
+    public Integer getLive_status() {
+        return live_status;
+    }
+
+    public void setLive_status(Integer live_status) {
+        this.live_status = live_status;
+    }
+
+    public String getLive_msg() {
+        return live_msg;
+    }
+
+    public void setLive_msg(String live_msg) {
+        this.live_msg = live_msg;
+    }
+
+    public Integer getCompare_status() {
+        return compare_status;
+    }
+
+    public void setCompare_status(Integer compare_status) {
+        this.compare_status = compare_status;
+    }
+
+    public String getCompare_msg() {
+        return compare_msg;
+    }
+
+    public void setCompare_msg(String compare_msg) {
+        this.compare_msg = compare_msg;
+    }
+
+    public String getCompare_lib_type() {
+        return compare_lib_type;
+    }
+
+    public void setCompare_lib_type(String compare_lib_type) {
+        this.compare_lib_type = compare_lib_type;
+    }
+
+    public Integer getLiveness_mode() {
+        return liveness_mode;
+    }
+
+    public void setLiveness_mode(Integer liveness_mode) {
+        this.liveness_mode = liveness_mode;
+    }
+}

+ 72 - 0
src/cn/hmsoft/art/data/model/live/TxStdLiveLog.java

@@ -0,0 +1,72 @@
+package cn.hmsoft.art.data.model.live;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @Description 考生活体识别-请求token记录表
+ * @Author haoguanghui
+ * @date 2024/10/24
+ */
+@Table(tableName = "tx_std_live_log", keyColumn = "id", sequenceName = "")
+public class TxStdLiveLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    // ID主键
+    private Integer id;
+
+    //考生ID
+    private Integer std_id;
+
+    //活体识别token
+    private String biz_token;
+
+    //创建时间
+    private LocalDateTime create_time;
+
+    //是否拉取标志:0:未拉取,1:已成功拉取
+    private String pull_flag;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getStd_id() {
+        return std_id;
+    }
+
+    public void setStd_id(Integer std_id) {
+        this.std_id = std_id;
+    }
+
+    public String getBiz_token() {
+        return biz_token;
+    }
+
+    public void setBiz_token(String biz_token) {
+        this.biz_token = biz_token;
+    }
+
+    public LocalDateTime getCreate_time() {
+        return create_time;
+    }
+
+    public void setCreate_time(LocalDateTime create_time) {
+        this.create_time = create_time;
+    }
+
+    public String getPull_flag() {
+        return pull_flag;
+    }
+
+    public void setPull_flag(String pull_flag) {
+        this.pull_flag = pull_flag;
+    }
+}

+ 56 - 0
src/cn/hmsoft/art/data/model/live/txStdMaterialLog.java

@@ -0,0 +1,56 @@
+package cn.hmsoft.art.data.model.live;
+
+import cn.hmsoft.jdbc.entity.Table;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Table(tableName = "tx_std_material_log", keyColumn = "id", sequenceName = "")
+public class txStdMaterialLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    // ID主键
+    private Integer id;
+
+    //考生ID
+    private Integer std_id;
+
+    //考生照片
+    private String std_photo;
+
+    //创建时间
+    private LocalDateTime create_time;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getStd_id() {
+        return std_id;
+    }
+
+    public void setStd_id(Integer std_id) {
+        this.std_id = std_id;
+    }
+
+    public String getStd_photo() {
+        return std_photo;
+    }
+
+    public void setStd_photo(String std_photo) {
+        this.std_photo = std_photo;
+    }
+
+    public LocalDateTime getCreate_time() {
+        return create_time;
+    }
+
+    public void setCreate_time(LocalDateTime create_time) {
+        this.create_time = create_time;
+    }
+}

+ 34 - 0
src/cn/hmsoft/art/data/response/live/DetectAuthBean.java

@@ -0,0 +1,34 @@
+package cn.hmsoft.art.data.response.live;
+
+public class DetectAuthBean {
+
+    private String url;
+
+    private String bizToken;
+
+    private String requestId;
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getBizToken() {
+        return bizToken;
+    }
+
+    public void setBizToken(String bizToken) {
+        this.bizToken = bizToken;
+    }
+
+    public String getRequestId() {
+        return requestId;
+    }
+
+    public void setRequestId(String requestId) {
+        this.requestId = requestId;
+    }
+}

文件差異過大導致無法顯示
+ 56 - 0
src/cn/hmsoft/art/data/response/live/DetectInfoBean.java


+ 20 - 0
src/cn/hmsoft/art/data/response/live/DetectInfoBestFrame.java

@@ -0,0 +1,20 @@
+package cn.hmsoft.art.data.response.live;
+
+/**
+ * @Description 最佳帧截图
+ * @Author haoguanghui
+ * @date 2024/10/23
+ */
+public class DetectInfoBestFrame {
+
+    // 活体比对最佳帧Base64编码
+    private String BestFrame;
+
+    public String getBestFrame() {
+        return BestFrame;
+    }
+
+    public void setBestFrame(String bestFrame) {
+        BestFrame = bestFrame;
+    }
+}

+ 163 - 0
src/cn/hmsoft/art/data/response/live/DetectInfoDetail.java

@@ -0,0 +1,163 @@
+package cn.hmsoft.art.data.response.live;
+
+/**
+ * @Description 流水
+ * @Author haoguanghui
+ * @date 2024/10/23
+ */
+public class DetectInfoDetail {
+
+    // 请求时间戳
+    private String ReqTime;
+
+    // 本次活体一比一请求的唯一标记
+    private String Seq;
+
+    // 参与本次活体一比一的身份证号
+    private String Idcard;
+
+    //参与本次活体一比一的姓名
+    private String Name;
+
+    //本次活体一比一的相似度
+    private String Sim;
+
+    //本次活体一比一是否收费
+    private Boolean IsNeedCharge;
+
+    // 本次活体一比一最终结果。0为成功
+    private Integer Errcode;
+
+    // 本次活体一比一最终结果描述
+    private String Errmsg;
+
+    //本次活体结果。0为成功
+    private Integer Livestatus;
+
+    //本次活体结果描述
+    private String Livemsg;
+
+    //本次一比一结果。0为成功
+    private Integer Comparestatus;
+
+    //本次一比一结果描述
+    private String Comparemsg;
+
+    //比对库源类型
+    private String CompareLibType;
+
+    //活体检测类型(0:未知 1:数字活体 2:动作活体 3:静默活体 4:一闪活体(动作+光线))
+    private Integer LivenessMode;
+
+    public String getReqTime() {
+        return ReqTime;
+    }
+
+    public void setReqTime(String reqTime) {
+        ReqTime = reqTime;
+    }
+
+    public String getSeq() {
+        return Seq;
+    }
+
+    public void setSeq(String seq) {
+        Seq = seq;
+    }
+
+    public String getIdcard() {
+        return Idcard;
+    }
+
+    public void setIdcard(String idcard) {
+        Idcard = idcard;
+    }
+
+    public String getName() {
+        return Name;
+    }
+
+    public void setName(String name) {
+        Name = name;
+    }
+
+    public String getSim() {
+        return Sim;
+    }
+
+    public void setSim(String sim) {
+        Sim = sim;
+    }
+
+    public Boolean getNeedCharge() {
+        return IsNeedCharge;
+    }
+
+    public void setNeedCharge(Boolean needCharge) {
+        IsNeedCharge = needCharge;
+    }
+
+    public Integer getErrcode() {
+        return Errcode;
+    }
+
+    public void setErrcode(Integer errcode) {
+        Errcode = errcode;
+    }
+
+    public String getErrmsg() {
+        return Errmsg;
+    }
+
+    public void setErrmsg(String errmsg) {
+        Errmsg = errmsg;
+    }
+
+    public Integer getLivestatus() {
+        return Livestatus;
+    }
+
+    public void setLivestatus(Integer livestatus) {
+        Livestatus = livestatus;
+    }
+
+    public String getLivemsg() {
+        return Livemsg;
+    }
+
+    public void setLivemsg(String livemsg) {
+        Livemsg = livemsg;
+    }
+
+    public Integer getComparestatus() {
+        return Comparestatus;
+    }
+
+    public void setComparestatus(Integer comparestatus) {
+        Comparestatus = comparestatus;
+    }
+
+    public String getComparemsg() {
+        return Comparemsg;
+    }
+
+    public void setComparemsg(String comparemsg) {
+        Comparemsg = comparemsg;
+    }
+
+    public String getCompareLibType() {
+        return CompareLibType;
+    }
+
+    public void setCompareLibType(String compareLibType) {
+        CompareLibType = compareLibType;
+    }
+
+    public Integer getLivenessMode() {
+        return LivenessMode;
+    }
+
+    public void setLivenessMode(Integer livenessMode) {
+        LivenessMode = livenessMode;
+    }
+}

+ 20 - 0
src/cn/hmsoft/art/data/response/live/DetectInfoResult.java

@@ -0,0 +1,20 @@
+package cn.hmsoft.art.data.response.live;
+
+/**
+ * @Description  活体识别结果
+ * @Author haoguanghui
+ * @date 2024/10/24
+ */
+public class DetectInfoResult {
+
+    //识别结果
+    private String result;
+
+    public String getResult() {
+        return result;
+    }
+
+    public void setResult(String result) {
+        this.result = result;
+    }
+}

+ 69 - 0
src/cn/hmsoft/art/data/response/live/DetectInfoText.java

@@ -0,0 +1,69 @@
+package cn.hmsoft.art.data.response.live;
+
+import java.util.List;
+
+/**
+ * @Description 文本类信息
+ * @Author haoguanghui
+ * @date 2024/10/23
+ */
+public class DetectInfoText {
+
+    //本次验证使用的身份证号
+    private String IdCard;
+
+    //本次验证使用的姓名
+    private String Name;
+
+    //用户认证时使用的证件号码类型 (可能返回 null)
+    //0:二代身份证的证件号码
+    //1:港澳台居住证的证件号码
+    //2:其他(核验使用的证件号码非合法身份号码)
+    private Integer UseIDType;
+
+    //本次流程活体一比一的分数,取值范围 [0.00, 100.00]。相似度大于等于70时才判断为同一人
+    private String Sim;
+
+    //本次流程进行的活体一比一流水
+    private List<DetectInfoDetail> LivenessDetail;
+
+    public String getIdCard() {
+        return IdCard;
+    }
+
+    public void setIdCard(String idCard) {
+        IdCard = idCard;
+    }
+
+    public String getName() {
+        return Name;
+    }
+
+    public void setName(String name) {
+        Name = name;
+    }
+
+    public Integer getUseIDType() {
+        return UseIDType;
+    }
+
+    public void setUseIDType(Integer useIDType) {
+        UseIDType = useIDType;
+    }
+
+    public String getSim() {
+        return Sim;
+    }
+
+    public void setSim(String sim) {
+        Sim = sim;
+    }
+
+    public List<DetectInfoDetail> getLivenessDetail() {
+        return LivenessDetail;
+    }
+
+    public void setLivenessDetail(List<DetectInfoDetail> livenessDetail) {
+        LivenessDetail = livenessDetail;
+    }
+}

+ 20 - 0
src/cn/hmsoft/art/data/response/live/DetectInfoVideoData.java

@@ -0,0 +1,20 @@
+package cn.hmsoft.art.data.response.live;
+
+/**
+ * @Description 视频信息
+ * @Author haoguanghui
+ * @date 2024/10/23
+ */
+public class DetectInfoVideoData {
+
+    //活体视频的base64编码
+    private String LivenessVideo;
+
+    public String getLivenessVideo() {
+        return LivenessVideo;
+    }
+
+    public void setLivenessVideo(String livenessVideo) {
+        LivenessVideo = livenessVideo;
+    }
+}

+ 53 - 0
src/cn/hmsoft/art/data/vo/live/DetectAuthVO.java

@@ -0,0 +1,53 @@
+package cn.hmsoft.art.data.vo.live;
+
+/**
+ * @Description 获取token请求VO
+ * @Author haoguanghui
+ * @date 2024/10/22
+ */
+public class DetectAuthVO {
+
+    // 考生姓名
+    private String stdName;
+
+    //证件号码
+    private String certId;
+
+    // 业务ID
+    private String ruleId;
+
+    //回调地址
+    private String redirectUrl;
+
+    public String getStdName() {
+        return stdName;
+    }
+
+    public void setStdName(String stdName) {
+        this.stdName = stdName;
+    }
+
+    public String getCertId() {
+        return certId;
+    }
+
+    public void setCertId(String certId) {
+        this.certId = certId;
+    }
+
+    public String getRuleId() {
+        return ruleId;
+    }
+
+    public void setRuleId(String ruleId) {
+        this.ruleId = ruleId;
+    }
+
+    public String getRedirectUrl() {
+        return redirectUrl;
+    }
+
+    public void setRedirectUrl(String redirectUrl) {
+        this.redirectUrl = redirectUrl;
+    }
+}

+ 42 - 0
src/cn/hmsoft/art/data/vo/live/DetectInfoVO.java

@@ -0,0 +1,42 @@
+package cn.hmsoft.art.data.vo.live;
+
+/**
+ * @Description 获取活体识别结果VO
+ * @Author haoguanghui
+ * @date 2024/10/22
+ */
+public class DetectInfoVO {
+
+    // 人脸核身流程的标识,调用DetectAuth接口时生成
+    private String bizToken;
+
+    // 业务ID
+    private String ruleId;
+
+    //拉取的结果类型(0:全部;1:文本类;2:身份证信息;3:视频最佳截图信息)
+    private String infoType;
+
+    public String getBizToken() {
+        return bizToken;
+    }
+
+    public void setBizToken(String bizToken) {
+        this.bizToken = bizToken;
+    }
+
+    public String getRuleId() {
+        return ruleId;
+    }
+
+    public void setRuleId(String ruleId) {
+        this.ruleId = ruleId;
+    }
+
+    public String getInfoType() {
+        return infoType;
+    }
+
+    public void setInfoType(String infoType) {
+        this.infoType = infoType;
+    }
+}

文件差異過大導致無法顯示
+ 76 - 0
src/cn/hmsoft/art/enrol/control/tencent/LiveRecognitionControl.java


+ 10 - 0
src/cn/hmsoft/art/helper/ArtParamHelper.java

@@ -314,4 +314,14 @@ public class ArtParamHelper extends FrameParamUtil {
 	//过期时间
 	//过期时间
 	public static int ExpireTime = 30; //默认30分钟
 	public static int ExpireTime = 30; //默认30分钟
 
 
+	// 腾讯活体识别秘钥
+	public static String TencentLiveSecretId = "";
+	public static String TencentLiveSecretKey = "";
+	// 腾讯活体识别业务ID
+	public static String TencentLiveRuleId = "";
+	// 腾讯活体识别业务ID
+	public static String TencentLiveRedirectUrl = "";
+	// 腾讯活体识别结果类型
+	public static String TencentLiveInfoType = "";
+
 }
 }

+ 28 - 0
src/cn/hmsoft/art/helper/GsonHelper.java

@@ -0,0 +1,28 @@
+package cn.hmsoft.art.helper;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParseException;
+
+import java.lang.reflect.Type;
+
+/**
+ * @Description
+ * @Author haoguanghui
+ * @date 2024/10/23
+ */
+public class GsonHelper {
+    private static Gson gson = new GsonBuilder().create();
+
+    public static String toJson(Object value) {
+        return gson.toJson(value);
+    }
+
+    public static <T> T fromJson(String json, Class<T> classOfT) throws JsonParseException {
+        return gson.fromJson(json, classOfT);
+    }
+
+    public static <T> T fromJson(String json, Type typeOfT) throws JsonParseException {
+        return (T) gson.fromJson(json, typeOfT);
+    }
+}

文件差異過大導致無法顯示
+ 59 - 12
src/cn/hmsoft/art/helper/ImageUtil.java


+ 118 - 0
src/cn/hmsoft/art/helper/TencentLiveHelper.java

@@ -0,0 +1,118 @@
+package cn.hmsoft.art.helper;
+
+import cn.hmsoft.art.constants.Constants;
+import cn.hmsoft.art.data.vo.live.DetectAuthVO;
+import cn.hmsoft.art.data.vo.live.DetectInfoVO;
+import cn.hmsoft.frame.exception.BusinessException;
+import cn.hmsoft.helper.StringHelper;
+import com.tencentcloudapi.common.AbstractModel;
+import com.tencentcloudapi.common.Credential;
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
+import com.tencentcloudapi.common.profile.ClientProfile;
+import com.tencentcloudapi.common.profile.HttpProfile;
+import com.tencentcloudapi.faceid.v20180301.FaceidClient;
+import com.tencentcloudapi.faceid.v20180301.models.DetectAuthRequest;
+import com.tencentcloudapi.faceid.v20180301.models.DetectAuthResponse;
+import com.tencentcloudapi.faceid.v20180301.models.GetDetectInfoEnhancedRequest;
+import com.tencentcloudapi.faceid.v20180301.models.GetDetectInfoEnhancedResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Description 腾讯活体识别请求帮助类
+ * @Author haoguanghui
+ * @date 2024/10/22
+ */
+public class TencentLiveHelper {
+
+    private static final Logger logger = LoggerFactory.getLogger(TencentLiveHelper.class);
+
+    public static String END_POINT = "faceid.tencentcloudapi.com";
+
+    /**
+     * 返回腾讯接口返回的Token和人脸核身入口URL
+     *
+     * @param detectAuthVO 请求参数集合
+     * @return bizToken字符串
+     */
+    public static DetectAuthResponse detectAuth(DetectAuthVO detectAuthVO) {
+        try {
+            FaceidClient client = getFaceidClient();
+            // 实例化一个请求对象,每个接口都会对应一个request对象
+            DetectAuthRequest req = getDetectAuthRequest(detectAuthVO);
+            return client.DetectAuth(req);
+        } catch (TencentCloudSDKException e) {
+            logger.error("[DetectAuth],错误信息:{}", e.toString());
+            throw new BusinessException(Constants.SYSTEM_BUSY);
+        }
+    }
+
+
+    public static String getDetectInfoEnhanced(DetectInfoVO detectInfoVO) {
+        try {
+            FaceidClient client = getFaceidClient();
+            // 实例化一个请求对象,每个接口都会对应一个request对象
+            GetDetectInfoEnhancedRequest req = getGetDetectInfoEnhancedRequest(detectInfoVO);
+
+            // 返回的resp是一个GetDetectInfoEnhancedResponse的实例,与请求对象对应
+            GetDetectInfoEnhancedResponse resp = client.GetDetectInfoEnhanced(req);
+            // 输出json格式的字符串回包
+            return AbstractModel.toJsonString(resp);
+        } catch (TencentCloudSDKException e) {
+            logger.error("[DetectInfoEnhanced],错误信息:{}", e.toString());
+            throw new BusinessException("系统繁忙,请稍后重试");
+        }
+    }
+
+    private static DetectAuthRequest getDetectAuthRequest(DetectAuthVO detectAuthVO) {
+        DetectAuthRequest req = new DetectAuthRequest();
+        if (detectAuthVO != null) {
+            // 业务ID
+            if (StringHelper.isNotEmpty(detectAuthVO.getRuleId())) {
+                req.setRuleId(detectAuthVO.getRuleId());
+            }
+            // 证件号码
+            if (StringHelper.isNotEmpty(detectAuthVO.getCertId())) {
+                req.setIdCard(detectAuthVO.getCertId());
+            }
+            // 考生姓名
+            if (StringHelper.isNotEmpty(detectAuthVO.getStdName())) {
+                req.setName(detectAuthVO.getStdName());
+            }
+            // 回调地址
+            if(StringHelper.isNotEmpty(detectAuthVO.getRedirectUrl())) {
+                req.setRedirectUrl(detectAuthVO.getRedirectUrl());
+            }
+        }
+        return req;
+    }
+
+    private static GetDetectInfoEnhancedRequest getGetDetectInfoEnhancedRequest(DetectInfoVO detectInfoVO) {
+        GetDetectInfoEnhancedRequest req = new GetDetectInfoEnhancedRequest();
+        // 人脸核身流程的标识
+        if (StringHelper.isNotEmpty(detectInfoVO.getBizToken())) {
+            req.setBizToken(detectInfoVO.getBizToken());
+        }
+        //业务ID
+        if (StringHelper.isNotEmpty(detectInfoVO.getRuleId())) {
+            req.setRuleId(detectInfoVO.getRuleId());
+        }
+        //拉取的结果类型
+        if (StringHelper.isNotEmpty(detectInfoVO.getInfoType())) {
+            req.setInfoType(detectInfoVO.getInfoType());
+        }
+        return req;
+    }
+
+    private static FaceidClient getFaceidClient() {
+        Credential cred = new Credential(ArtParamHelper.TencentLiveSecretId, ArtParamHelper.TencentLiveSecretKey);
+        // 实例化一个http选项,可选的,没有特殊需求可以跳过
+        HttpProfile httpProfile = new HttpProfile();
+        httpProfile.setEndpoint(END_POINT);
+        // 实例化一个client选项,可选的,没有特殊需求可以跳过
+        ClientProfile clientProfile = new ClientProfile();
+        clientProfile.setHttpProfile(httpProfile);
+        // 实例化要请求产品的client对象,clientProfile是可选的
+        return new FaceidClient(cred, "", clientProfile);
+    }
+}

+ 77 - 0
src/cn/hmsoft/art/helper/VideoHelper.java

@@ -0,0 +1,77 @@
+package cn.hmsoft.art.helper;
+
+import org.apache.commons.codec.binary.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * @Description 视频处理帮助类
+ * @Author haoguanghui
+ * @date 2024/10/24
+ */
+public class VideoHelper {
+
+    private static final Logger log = LoggerFactory.getLogger(VideoHelper.class);
+
+    public static boolean generateBase64ToVideoFile(String videoBase64, String videoFilePath) {
+        if (videoBase64 == null)
+            return false;
+        try {
+            // Base64解码
+            videoBase64 = removeBase64Prefix(videoBase64);
+            byte[] b = Base64.decodeBase64(videoBase64);
+            for (int i = 0; i < b.length; ++i) {
+                if (b[i] < 0) {// 调整异常数据
+                    b[i] += (byte) 256;
+                }
+            }
+            // 生成video
+            OutputStream out = Files.newOutputStream(Paths.get(videoFilePath));
+            out.write(b);
+            out.flush();
+            out.close();
+            return true;
+        } catch (Exception e) {
+            log.error("base64转换视频异常: {}", e.getMessage());
+            return false;
+        }
+    }
+
+    public static String removeBase64Prefix(String base64String) {
+        if (base64String == null || !base64String.startsWith("data:")) {
+            return base64String;
+        }
+
+        // 找到分隔符的位置
+        int commaIndex = base64String.indexOf(',');
+        if (commaIndex == -1) {
+            return base64String;
+        }
+
+        // 返回去掉前缀后的部分
+        return base64String.substring(commaIndex + 1);
+    }
+
+    public static void main(String[] args) {
+        String filePath = "D:/temp/1.txt";
+        StringBuilder sb = new StringBuilder();
+
+        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
+            String line;
+            while ((line = br.readLine()) != null) {
+                sb.append(line).append("\n");
+            }
+        } catch (Exception e) {
+            System.err.println("读取文件时发生错误: " + e.getMessage());
+        }
+
+        boolean flag = generateBase64ToVideoFile(sb.toString(), "d:/temp/1.mp4");
+        System.out.println(flag);
+    }
+}

+ 196 - 0
src/cn/hmsoft/art/service/live/LiveRecognitionService.java

@@ -0,0 +1,196 @@
+package cn.hmsoft.art.service.live;
+
+import cn.hmsoft.art.constants.ArtDefaultValue;
+import cn.hmsoft.art.constants.ArtParamName;
+import cn.hmsoft.art.constants.Constants;
+import cn.hmsoft.art.constants.StdMaterialType;
+import cn.hmsoft.art.data.dao.live.TxLivenessDetailDao;
+import cn.hmsoft.art.data.dao.live.TxStdLiveLogDao;
+import cn.hmsoft.art.data.dao.std.StdMaterialDao;
+import cn.hmsoft.art.data.dao.std.StdRegDao;
+import cn.hmsoft.art.data.model.live.TxLivenessDetail;
+import cn.hmsoft.art.data.model.live.TxStdLiveLog;
+import cn.hmsoft.art.data.model.live.txStdMaterialLog;
+import cn.hmsoft.art.data.model.std.StdMaterial;
+import cn.hmsoft.art.data.model.std.StdReg;
+import cn.hmsoft.art.data.response.live.*;
+import cn.hmsoft.art.data.vo.live.DetectAuthVO;
+import cn.hmsoft.art.enrol.helper.EnrolMaterialHelper;
+import cn.hmsoft.art.helper.ArtParamHelper;
+import cn.hmsoft.art.helper.GsonHelper;
+import cn.hmsoft.art.helper.ImageUtil;
+import cn.hmsoft.art.service.ArtService;
+import cn.hmsoft.frame.constants.FrameStatus;
+import cn.hmsoft.frame.exception.BusinessException;
+import cn.hmsoft.helper.CollectionHelper;
+import cn.hmsoft.helper.StringHelper;
+import cn.hmsoft.jdbc.entity.QueryOrder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.File;
+import java.time.LocalDateTime;
+import java.util.List;
+
+
+@Service
+public class LiveRecognitionService extends ArtService {
+
+    private static final Logger log = LoggerFactory.getLogger(LiveRecognitionService.class);
+
+    @Autowired
+    private StdRegDao stdRegDao;
+
+    @Autowired
+    private TxStdLiveLogDao txStdLiveLogDao;
+
+    @Autowired
+    private TxLivenessDetailDao txLivenessDetailDao;
+
+    @Autowired
+    private StdMaterialDao stdMaterialDao;
+
+    @Transactional
+    public DetectInfoResult saveDetectResult(String bizToken, String result) {
+        log.info("[saveDetectResult] bizToken:{},result:{}", bizToken, result);
+        DetectInfoResult detectInfoResult = new DetectInfoResult();
+        if (StringHelper.isNotEmpty(result)) {
+            DetectInfoBean detectInfoBean = GsonHelper.fromJson(result, DetectInfoBean.class);
+            DetectInfoText text = detectInfoBean.getText();
+            if (text == null) {
+                log.error("[saveDetectResult] bizToken:{} text为空", bizToken);
+                throw new BusinessException(Constants.SYSTEM_BUSY);
+            }
+            // 相似度
+            double sim = Double.parseDouble(
+                    ArtParamHelper.getParamValue(ArtParamName.EnrolVerifyStdIdentityPercent, "" + ArtDefaultValue.EnrolIdentifyVerifyPercent));
+            if (StringHelper.isNotEmpty(text.getSim()) && Double.parseDouble(text.getSim()) > sim) {
+                //识别通过
+                detectInfoResult.setResult(Constants.TRUE_FLAG);
+            } else {
+                detectInfoResult.setResult(Constants.FALSE_FLAG);
+            }
+
+            //保存流水
+            List<DetectInfoDetail> livenessDetail = text.getLivenessDetail();
+            if (livenessDetail != null && !livenessDetail.isEmpty()) {
+                livenessDetail.forEach(item -> {
+                    TxLivenessDetail detail = new TxLivenessDetail();
+                    detail.setBiz_token(bizToken);
+                    detail.setReq_time(item.getReqTime());
+                    detail.setSeq(item.getSeq());
+                    detail.setId_card(item.getIdcard());
+                    detail.setName(item.getName());
+                    detail.setSim(item.getSim());
+                    detail.setNeed_charge(item.getNeedCharge());
+                    detail.setError_code(item.getErrcode());
+                    detail.setError_msg(item.getErrmsg());
+                    detail.setLive_status(item.getLivestatus());
+                    detail.setLive_msg(item.getLivemsg());
+                    detail.setCompare_status(item.getComparestatus());
+                    detail.setCompare_msg(item.getComparemsg());
+                    detail.setCompare_lib_type(item.getCompareLibType());
+                    detail.setLiveness_mode(item.getLivenessMode());
+                    //保存流水
+                    txLivenessDetailDao.insert(detail);
+                });
+            }
+
+            //更新拉取标志
+            List<TxStdLiveLog> stdLiveLogList = txStdLiveLogDao.list("biz_token", bizToken,
+                    new QueryOrder("create_time", QueryOrder.QueryOrderType.DESC));
+            TxStdLiveLog txStdLiveLog;
+            if (CollectionHelper.isNotEmpty(stdLiveLogList)) {
+                txStdLiveLog = stdLiveLogList.get(0);
+                txStdLiveLog.setPull_flag(Constants.TRUE_FLAG);
+                txStdLiveLogDao.update(txStdLiveLog);
+            } else {
+                log.error("[saveDetectResult] biz_token:{} 找不到请求记录", bizToken);
+                throw new BusinessException(Constants.SYSTEM_BUSY);
+            }
+
+            // 保存考生照片
+            try {
+                DetectInfoBestFrame bestFrame = detectInfoBean.getBestFrame();
+                if (bestFrame != null && StringHelper.isNotEmpty(bestFrame.getBestFrame())) {
+                    StdReg reg = stdRegDao.findByStdId(txStdLiveLog.getStd_id());
+                    String rootPath = EnrolMaterialHelper.getEnrolMaterialLocalPath();
+                    // 文件名称
+                    String fileName = EnrolMaterialHelper.generateMaterialName(StdMaterialType.NoCrownPhoto, reg.getCert_id()) + Constants.PHOTO_SUFFIX;
+                    // 文件不存在创建
+                    File file = new File(rootPath + fileName);
+                    if(!file.exists()){
+                        File parentFile = file.getParentFile();
+                        if(!parentFile.exists()){
+                            parentFile.mkdirs();
+                        }
+                    }
+
+                    // 保存
+                    boolean flag = ImageUtil.generateBase64StringToFile(bestFrame.getBestFrame(), file);
+                    if (!flag) {
+                        log.warn("[saveDetectResult] 保存图片失败");
+                        // 失败 将考生的base64数据保存到数据库表中
+                        txStdMaterialLog materialLog = new txStdMaterialLog();
+                        materialLog.setStd_id(txStdLiveLog.getStd_id());
+                        materialLog.setStd_photo(bestFrame.getBestFrame());
+                        materialLog.setCreate_time(LocalDateTime.now());
+                        stdMaterialDao.insert(materialLog);
+                    } else {
+                        // 数据写入到考生材料表
+                        StdMaterial stdMaterial = new StdMaterial();
+                        //是否存在
+                        StdMaterial existMaterial = stdMaterialDao.findMaterial(txStdLiveLog.getStd_id(), StdMaterialType.NoCrownPhoto);
+                        fileName = fileName.replace("\\","/");
+                        if (existMaterial != null) {
+                            existMaterial.setMaterial_file(fileName);
+                            existMaterial.setUpload_time(LocalDateTime.now());
+                            existMaterial.setVerify_status(FrameStatus.Active.toString());
+                            existMaterial.setVerify_time(LocalDateTime.now());
+                            stdMaterialDao.update(existMaterial);
+                        } else {
+                            stdMaterial.setStd_id(txStdLiveLog.getStd_id());
+                            stdMaterial.setMaterial_type(StdMaterialType.NoCrownPhoto);
+                            stdMaterial.setMaterial_file(fileName);
+                            stdMaterial.setFile_type(Constants.PHOTO_SUFFIX_WITHOUT_DOT);
+                            stdMaterial.setUpload_time(LocalDateTime.now());
+                            stdMaterial.setVerify_status(FrameStatus.Active.toString());
+                            stdMaterial.setVerify_time(LocalDateTime.now());
+                            stdMaterialDao.insert(stdMaterial);
+                        }
+
+                        //更新考生表中的头像
+                        reg.setStd_image(fileName);
+                        stdRegDao.update(reg);
+
+                    }
+                }
+            } catch (Exception e) {
+                log.warn("[saveDetectResult] 保存图片失败,{}", e.getMessage());
+            }
+
+            //保存视频数据(暂不保存)
+        }
+
+        return detectInfoResult;
+    }
+
+    public void saveDetectAuth(DetectAuthBean bean, DetectAuthVO detectAuthVO) {
+        StdReg stdReg = stdRegDao.findStdRegByCertAndName(detectAuthVO.getCertId(), detectAuthVO.getStdName());
+        if (stdReg == null) {
+            log.warn("[saveDetectAuth] 考生信息不存在,std_name:{}, cert_id:{}", detectAuthVO.getStdName(), detectAuthVO.getCertId());
+            throw new BusinessException("考生信息不存在");
+        }
+
+        //保存token
+        TxStdLiveLog log = new TxStdLiveLog();
+        log.setBiz_token(bean.getBizToken());
+        log.setStd_id(stdReg.getStd_id());
+        log.setCreate_time(LocalDateTime.now());
+        log.setPull_flag(Constants.FALSE_FLAG);
+        txStdLiveLogDao.insert(log);
+    }
+}

部分文件因文件數量過多而無法顯示