Преглед на файлове

Merge branch 'dev' of lvjian/wxbase into dev

合并 feature/eid 分支
吕健 преди 2 години
родител
ревизия
46334f0508
променени са 45 файла, в които са добавени 1538 реда и са изтрити 111 реда
  1. 23 0
      .gitignore
  2. 23 0
      build.gradle
  3. 6 2
      src/main/java/com/ematou/wxbase/aop/LogAspect.java
  4. 16 0
      src/main/java/com/ematou/wxbase/common/constant/RedisKey.java
  5. 27 0
      src/main/java/com/ematou/wxbase/common/constant/TokenType.java
  6. 19 30
      src/main/java/com/ematou/wxbase/common/web/R.java
  7. 2 0
      src/main/java/com/ematou/wxbase/common/web/ResponseCodeConstant.java
  8. 91 0
      src/main/java/com/ematou/wxbase/config/RedisConfig.java
  9. 33 0
      src/main/java/com/ematou/wxbase/controller/EidController.java
  10. 32 0
      src/main/java/com/ematou/wxbase/controller/EidTokenRecordController.java
  11. 22 0
      src/main/java/com/ematou/wxbase/entity/EidMerch.java
  12. 34 0
      src/main/java/com/ematou/wxbase/entity/EidTokenRecord.java
  13. 20 0
      src/main/java/com/ematou/wxbase/entity/MerchApp.java
  14. 18 0
      src/main/java/com/ematou/wxbase/entity/MerchInfo.java
  15. 18 69
      src/main/java/com/ematou/wxbase/entity/OperationRecord.java
  16. 45 0
      src/main/java/com/ematou/wxbase/entity/dto/EidTokenRequestDTO.java
  17. 38 0
      src/main/java/com/ematou/wxbase/entity/vo/EidTokenResponseVO.java
  18. 102 0
      src/main/java/com/ematou/wxbase/exception/Assert.java
  19. 34 0
      src/main/java/com/ematou/wxbase/exception/ServiceException.java
  20. 46 0
      src/main/java/com/ematou/wxbase/handler/GlobalExceptionHandler.java
  21. 24 0
      src/main/java/com/ematou/wxbase/mapper/EidMerchMapper.java
  22. 24 0
      src/main/java/com/ematou/wxbase/mapper/EidTokenRecordMapper.java
  23. 26 0
      src/main/java/com/ematou/wxbase/mapper/MerchAppMapper.java
  24. 32 0
      src/main/java/com/ematou/wxbase/mapper/MerchInfoMapper.java
  25. 1 1
      src/main/java/com/ematou/wxbase/scheduler/RefreshAccessTokenScheduler.java
  26. 27 0
      src/main/java/com/ematou/wxbase/service/EidService.java
  27. 22 0
      src/main/java/com/ematou/wxbase/service/EidTokenRecordService.java
  28. 23 0
      src/main/java/com/ematou/wxbase/service/MerchInfoService.java
  29. 2 1
      src/main/java/com/ematou/wxbase/service/TokenRecordService.java
  30. 253 0
      src/main/java/com/ematou/wxbase/service/impl/EidServiceImpl.java
  31. 51 0
      src/main/java/com/ematou/wxbase/service/impl/EidTokenRecordServiceImpl.java
  32. 41 0
      src/main/java/com/ematou/wxbase/service/impl/MerchInfoServiceImpl.java
  33. 17 0
      src/main/resources/application-prod.yml
  34. 17 0
      src/main/resources/application-test.yml
  35. 5 8
      src/main/resources/application.yml
  36. 25 0
      src/main/resources/mybatis/EidMerchMapper.xml
  37. 24 0
      src/main/resources/mybatis/EidTokenRecordMapper.xml
  38. 32 0
      src/main/resources/mybatis/MerchAppMapper.xml
  39. 33 0
      src/main/resources/mybatis/MerchInfoMapper.xml
  40. 12 0
      src/main/resources/mybatis/OperationRecordMapper.xml
  41. 26 0
      src/test/java/com/ematou/wxbase/EidTokenRecordServiceTest.java
  42. 60 0
      src/test/java/com/ematou/wxbase/EidTokenTest.java
  43. 31 0
      src/test/java/com/ematou/wxbase/MerchAppMapperTest.java
  44. 41 0
      src/test/java/com/ematou/wxbase/MerchInfoServiceTest.java
  45. 40 0
      src/test/java/com/ematou/wxbase/RedisTest.java

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+# Windows
+[Dd]esktop.ini
+Thumbs.db
+$RECYCLE.BIN/
+
+# macOS
+.DS_Store
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+
+# Node.js
+node_modules/
+miniprogram_npm/
+
+# idea
+.idea/
+
+# gradle
+.gradle/
+build/
+out/

+ 23 - 0
build.gradle

@@ -5,32 +5,42 @@
 plugins {
     id 'java'
     id 'maven-publish'
+    id "org.asciidoctor.convert" version "2.3.0"
+    id 'org.springframework.boot' version '2.4.5'
 }
 
+apply plugin: 'io.spring.dependency-management'
+
 repositories {
     mavenLocal()
     maven {
         url = uri('http://maven.youzanyun.com/repository/maven-central/')
+        allowInsecureProtocol = true
     }
 
     maven {
         url = uri('http://maven.youzanyun.com/repository/maven-release/')
+        allowInsecureProtocol = true
     }
 
     maven {
         url = uri('http://maven.youzanyun.com/repository/maven-snapshots/')
+        allowInsecureProtocol = true
     }
 
     maven {
         url = uri('http://nexus.ds-bay.com/content/groups/public/')
+        allowInsecureProtocol = true
     }
 
     maven {
         url = uri('http://nexus.ds-bay.com/content/repositories/releases/')
+        allowInsecureProtocol = true
     }
 
     maven {
         url = uri('http://nexus.ds-bay.com/content/repositories/snapshots')
+        allowInsecureProtocol = true
     }
 
     maven {
@@ -43,6 +53,7 @@ repositories {
 
     maven {
         url = uri('http://repo.spring.io/libs-milestone/')
+        allowInsecureProtocol = true
     }
 
     maven {
@@ -87,10 +98,16 @@ repositories {
 
     maven {
         url = uri('http://repo.maven.apache.org/maven2')
+        allowInsecureProtocol = true
     }
 }
 
 dependencies {
+    implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.4.5'
+    implementation 'cn.hutool:hutool-all:5.8.12'
+    compileOnly 'org.projectlombok:lombok:1.18.10'
+    annotationProcessor 'org.projectlombok:lombok:1.18.10'
+    implementation 'com.tencentcloudapi:tencentcloud-sdk-java:3.1.695'
     implementation 'org.springframework.boot:spring-boot-starter-web:2.4.5'
     implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4'
     implementation 'org.springframework.boot:spring-boot-starter-logging:2.4.5'
@@ -117,3 +134,9 @@ tasks.withType(JavaCompile) {
     options.encoding = 'UTF-8'
     options.compilerArgs << "-Xlint:unchecked"
 }
+
+/* gradle test 编译器 */
+test {
+    enabled = false
+    useJUnitPlatform()
+}

+ 6 - 2
src/main/java/com/ematou/wxbase/aop/LogAspect.java

@@ -1,6 +1,6 @@
 package com.ematou.wxbase.aop;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
+import com.ematou.wxbase.exception.ServiceException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.Signature;
@@ -39,8 +39,12 @@ public class LogAspect {
             logger.info("----------------方法: " + targetMethodName + "的参数为: " + objectMapper.writeValueAsString(joinPoint.getArgs()) + "----------------");
             proceed = joinPoint.proceed();
             logger.info("----------------方法: " + targetMethodName + "执行的返回值为: " + proceed + "----------------");
+        } catch (ServiceException se){
+            // 如果时业务异常记录结果,抛出交给统一异常处理
+            throw se;
         } catch (Throwable throwable) {
-            logger.error("----------------方法: " + targetMethodName + "执行出现异常,错误信息为:" + throwable.getMessage() + "----------------");
+            logger.error("----------------方法: " + targetMethodName + "执行出现异常,错误信息为:" + throwable.getMessage() + "----------------", throwable);
+            throw new ServiceException("系统异常,请稍后再试!");
         }
         return proceed;
     }

+ 16 - 0
src/main/java/com/ematou/wxbase/common/constant/RedisKey.java

@@ -0,0 +1,16 @@
+package com.ematou.wxbase.common.constant;
+
+/**
+ * Redis 缓存键前缀
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/20 16:38
+ */
+public class RedisKey {
+
+    /**
+     * 获取E证通 token 缓存键前缀
+     */
+    public static final String GET_EID_TOKEN = "get_eid_token:";
+}

+ 27 - 0
src/main/java/com/ematou/wxbase/common/constant/TokenType.java

@@ -0,0 +1,27 @@
+package com.ematou.wxbase.common.constant;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+
+/**
+ * Token 类型
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/19 11:47
+ */
+@Getter
+@ToString
+@AllArgsConstructor
+public enum TokenType {
+
+    WX_TOKEN("0", "微信 token"),
+    EID_TOKEN("1", "E证通 token"),
+    ;
+
+
+    private String item;
+    private String itemName;
+
+}

+ 19 - 30
src/main/java/com/ematou/wxbase/common/web/R.java

@@ -1,11 +1,16 @@
 package com.ematou.wxbase.common.web;
 
+import lombok.Data;
+
+import java.io.Serializable;
+
 /**
  * @author lhm
  * @version 1.0
  * 2021-04-30 14:25
  */
-public class R<T> {
+@Data
+public class R<T> implements Serializable {
 
     private int code;
 
@@ -22,41 +27,25 @@ public class R<T> {
         this.data = data;
     }
 
-    public R<T> success(T data) {
-        this.setCode(ResponseCodeConstant.code_200);
-        this.setMessage("Success");
-        this.setData(data);
-        return this;
-    }
-
-    public R<?> error(String message) {
-        this.setCode(ResponseCodeConstant.code_100);
-        this.setMessage(message);
-        this.setData(null);
-        return this;
-    }
-
-    public int getCode() {
-        return code;
+    public static <T> R<T> success(T data) {
+        return restResult(data, ResponseCodeConstant.code_200, "成功");
     }
 
-    public void setCode(int code) {
-        this.code = code;
-    }
-
-    public String getMessage() {
-        return message;
+    public static <T> R<T> error(String message) {
+        return restResult(null, ResponseCodeConstant.code_500, message);
     }
 
-    public void setMessage(String message) {
-        this.message = message;
+    public static <T> R<T> error(int code, String message) {
+        return restResult(null, code, message);
     }
 
-    public T getData() {
-        return data;
+    private static <T> R<T> restResult(T data, int code, String msg)
+    {
+        R<T> apiResult = new R<>();
+        apiResult.setCode(code);
+        apiResult.setData(data);
+        apiResult.setMessage(msg);
+        return apiResult;
     }
 
-    public void setData(T data) {
-        this.data = data;
-    }
 }

+ 2 - 0
src/main/java/com/ematou/wxbase/common/web/ResponseCodeConstant.java

@@ -7,6 +7,8 @@ package com.ematou.wxbase.common.web;
  */
 public class ResponseCodeConstant {
 
+    public static final int code_500 = 500;
+
     public static final int code_200 = 200;
 
     public static final int code_100 = 100;

+ 91 - 0
src/main/java/com/ematou/wxbase/config/RedisConfig.java

@@ -0,0 +1,91 @@
+package com.ematou.wxbase.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.interceptor.KeyGenerator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.lang.reflect.Method;
+import java.time.Duration;
+
+/**
+ * Redis配置类
+ *
+ */
+@Configuration
+@EnableCaching
+public class RedisConfig {
+
+    // 使用默认标签做缓存
+    @Bean
+    public KeyGenerator wiselyKeyGenerator() {
+        return new KeyGenerator() {
+            @Override
+            public Object generate(Object target, Method method, Object... params) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(target.getClass().getName());
+                sb.append(method.getName());
+                for (Object obj : params) {
+                    sb.append(obj.toString());
+                }
+                return sb.toString();
+            }
+        };
+    }
+
+    // 声明模板
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(redisConnectionFactory);
+        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
+
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
+
+        redisTemplate.afterPropertiesSet();
+        return redisTemplate;
+    }
+
+    @Bean
+    public CacheManager cacheManager(RedisConnectionFactory factory) {
+        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
+        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+
+        //解决查询缓存转换异常的问题
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+
+        // 配置序列化(解决乱码的问题),过期时间600秒
+        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
+                .entryTtl(Duration.ofSeconds(600))
+                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
+                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
+                .disableCachingNullValues();
+
+        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
+                .cacheDefaults(config)
+                .build();
+        return cacheManager;
+    }
+}

+ 33 - 0
src/main/java/com/ematou/wxbase/controller/EidController.java

@@ -0,0 +1,33 @@
+package com.ematou.wxbase.controller;
+
+import com.ematou.wxbase.common.web.R;
+import com.ematou.wxbase.entity.dto.EidTokenRequestDTO;
+import com.ematou.wxbase.service.EidService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * E证通业务 Controller
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/18 16:58
+ */
+@RestController
+@RequestMapping("/eid")
+public class EidController {
+
+    @Autowired
+    private EidService eidService;
+
+    @PostMapping("/getEidToken")
+    public R getEidToken(@RequestBody EidTokenRequestDTO requestDTO) {
+        return eidService.getEidToken(requestDTO);
+    }
+
+    @GetMapping("/getEidResult")
+    public R getEidResult() {
+        return eidService.getEidResult();
+    }
+
+}

+ 32 - 0
src/main/java/com/ematou/wxbase/controller/EidTokenRecordController.java

@@ -0,0 +1,32 @@
+package com.ematou.wxbase.controller;
+
+import com.ematou.wxbase.common.web.R;
+import com.ematou.wxbase.service.EidTokenRecordService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * E证通 token 记录控制器
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/16 17:40
+ */
+@RestController
+@RequestMapping("/eid/token")
+public class EidTokenRecordController {
+
+    @Autowired
+    private EidTokenRecordService tokenRecordService;
+
+    @GetMapping("/{merchSn}")
+    public R getToken(@PathVariable String merchSn) {
+        String token = tokenRecordService.getEidToken(merchSn);
+        return new R<String>().success(token);
+    }
+
+}
+

+ 22 - 0
src/main/java/com/ematou/wxbase/entity/EidMerch.java

@@ -0,0 +1,22 @@
+package com.ematou.wxbase.entity;
+
+import lombok.Data;
+
+/**
+ * E证通商户配置表
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/17 14:33
+ */
+@Data
+public class EidMerch {
+
+    private String id;// 主键
+    private String merchSn;// 商户编号
+    private String secretId;// secretId
+    private String secretKey;// secretKey
+    private String merchId;// E证通商户 Id
+    private Long tokenExpired;// token 过期时间
+
+}

+ 34 - 0
src/main/java/com/ematou/wxbase/entity/EidTokenRecord.java

@@ -0,0 +1,34 @@
+package com.ematou.wxbase.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+
+/**
+ * E证通 token 记录
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/16 16:20
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class EidTokenRecord {
+
+    private String id;// 主键
+    private String merchSn;// 商户编号
+    private String appCode;// 应用编码
+    private String token;// token
+    private LocalDateTime generationTime;// 生成时间
+    private LocalDateTime expireTime;// 过期时间
+    private String idCard;// 身份证
+    private String userName;// 用户姓名
+    private Timestamp tstm;// 时间戳
+
+}

+ 20 - 0
src/main/java/com/ematou/wxbase/entity/MerchApp.java

@@ -0,0 +1,20 @@
+package com.ematou.wxbase.entity;
+
+import lombok.Data;
+
+/**
+ * 商户应用表
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/17 14:58
+ */
+@Data
+public class MerchApp {
+
+    private String id;// 主键
+    private String merchSn;// 商户编号
+    private String appCode;// 应用编码
+    private String appName;// 应用名称
+
+}

+ 18 - 0
src/main/java/com/ematou/wxbase/entity/MerchInfo.java

@@ -0,0 +1,18 @@
+package com.ematou.wxbase.entity;
+
+import lombok.Data;
+
+/**
+ * 商户信息
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/16 16:24
+ */
+@Data
+public class MerchInfo {
+
+    private String merchSn;// 商户编号
+    private String merchName;// 商户名称
+
+}

+ 18 - 69
src/main/java/com/ematou/wxbase/entity/OperationRecord.java

@@ -1,5 +1,10 @@
 package com.ematou.wxbase.entity;
 
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
 import java.sql.Timestamp;
 
 /**
@@ -7,77 +12,21 @@ import java.sql.Timestamp;
  * @version 1.0
  * 2021-04-30 11:35
  */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
 public class OperationRecord {
 
-    private Integer opId;
-
-    private String appCode;
-
-    private String createrSn;
-
-    private String createTime;
-
-    private String moderSn;
-
-    private String modTime;
-
-    private Timestamp tstm;
-
-    public Integer getOpId() {
-        return opId;
-    }
-
-    public void setOpId(Integer opId) {
-        this.opId = opId;
-    }
-
-    public String getAppCode() {
-        return appCode;
-    }
-
-    public void setAppCode(String appCode) {
-        this.appCode = appCode;
-    }
-
-    public String getCreaterSn() {
-        return createrSn;
-    }
-
-    public void setCreaterSn(String createrSn) {
-        this.createrSn = createrSn;
-    }
-
-    public String getCreateTime() {
-        return createTime;
-    }
-
-    public void setCreateTime(String createTime) {
-        this.createTime = createTime;
-    }
-
-    public String getModerSn() {
-        return moderSn;
-    }
-
-    public void setModerSn(String moderSn) {
-        this.moderSn = moderSn;
-    }
-
-    public String getModTime() {
-        return modTime;
-    }
-
-    public void setModTime(String modTime) {
-        this.modTime = modTime;
-    }
-
-    public Timestamp getTstm() {
-        return tstm;
-    }
-
-    public void setTstm(Timestamp tstm) {
-        this.tstm = tstm;
-    }
+    private Integer opId;// 主键
+    private String appCode;// 应用编码
+    private String merchSn;// 商户号
+    private String tokenType;// token 类型, 0:微信 token,1:E证通 token
+    private String createrSn;// 创建人编号
+    private String createTime;// 创建时间
+    private String moderSn;// 修改人编号
+    private String modTime;// 修改时间
+    private Timestamp tstm;// 时间戳
 
     @Override
     public String toString() {

+ 45 - 0
src/main/java/com/ematou/wxbase/entity/dto/EidTokenRequestDTO.java

@@ -0,0 +1,45 @@
+package com.ematou.wxbase.entity.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * E证通请求 DTO
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/19 10:28
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class EidTokenRequestDTO implements Serializable {
+
+    /**
+     * 商户号
+     */
+    private String merchSn;
+
+    /**
+     * 应用编码
+     */
+    private String appCode;
+
+    /**
+     * 用户姓名
+     */
+    private String userName;
+
+    /**
+     * 用户身份证
+     */
+    private String idCard;
+
+
+
+}

+ 38 - 0
src/main/java/com/ematou/wxbase/entity/vo/EidTokenResponseVO.java

@@ -0,0 +1,38 @@
+package com.ematou.wxbase.entity.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * E证通响应 VO
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/20 14:11
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class EidTokenResponseVO implements Serializable {
+
+    /**
+     * 核验人姓名
+     */
+    private String userName;
+
+    /**
+     * 核验人身份证
+     */
+    private String idCard;
+
+    /**
+     * 核验人E证通 token
+     */
+    private String eidToken;
+
+}

+ 102 - 0
src/main/java/com/ematou/wxbase/exception/Assert.java

@@ -0,0 +1,102 @@
+package com.ematou.wxbase.exception;
+
+
+import cn.hutool.core.util.StrUtil;
+
+import java.util.Objects;
+
+/**
+ * 断言类
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2022/12/21 10:39
+ */
+public abstract class Assert {
+
+    /**
+     * 断言对象不为空
+     * 如果对象obj为空,则抛出异常
+     * @param obj 待判断对象
+     */
+    public static void notNull(Object obj, String msg) {
+        if (Objects.isNull(obj)) {
+            throw new ServiceException(msg);
+        }
+    }
+
+
+    /**
+     * 断言对象为空
+     * 如果对象obj不为空,则抛出异常
+     * @param object
+     * @param msg
+     */
+    public static void isNull(Object object, String msg) {
+        if (Objects.nonNull(object)) {
+            throw new ServiceException(msg);
+        }
+    }
+
+    /**
+     * 断言表达式为真
+     * 如果为假,则抛出异常
+     *
+     * @param expression 是否成功
+     */
+    public static void isTrue(boolean expression, String msg) {
+        if (!expression) {
+            throw new ServiceException(msg);
+        }
+    }
+
+    /**
+     * 断言表达式为假
+     * 如果为真,则抛出异常
+     *
+     * @param expression 是否成功
+     */
+    public static void notTrue(boolean expression, String msg) {
+        if (expression) {
+            throw new ServiceException(msg);
+        }
+    }
+
+    /**
+     * 断言两个对象不相等
+     * 如果相等,则抛出异常
+     * @param m1
+     * @param m2
+     * @param msg
+     */
+    public static void notEquals(Object m1, Object m2,  String msg) {
+        if (m1.equals(m2)) {
+            throw new ServiceException(msg);
+        }
+    }
+
+    /**
+     * 断言两个对象相等
+     * 如果不相等,则抛出异常
+     * @param m1
+     * @param m2
+     * @param msg
+     */
+    public static void equals(Object m1, Object m2,  String msg) {
+        if (!m1.equals(m2)) {
+            throw new ServiceException(msg);
+        }
+    }
+
+    /**
+     * 断言参数不为空
+     * 如果为空,则抛出异常
+     * @param s
+     * @param msg
+     */
+    public static void notEmpty(String s, String msg) {
+        if (StrUtil.isBlank(s)) {
+            throw new ServiceException(msg);
+        }
+    }
+}

+ 34 - 0
src/main/java/com/ematou/wxbase/exception/ServiceException.java

@@ -0,0 +1,34 @@
+package com.ematou.wxbase.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 业务异常类
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/18 15:36
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public final class ServiceException extends RuntimeException {
+
+    /**
+     * 错误码,默认 500
+     */
+    private Integer code = 500;
+
+    /**
+     * 错误信息
+     */
+    private String message;
+
+    public ServiceException(String message)
+    {
+        this.message = message;
+    }
+
+}

+ 46 - 0
src/main/java/com/ematou/wxbase/handler/GlobalExceptionHandler.java

@@ -0,0 +1,46 @@
+package com.ematou.wxbase.handler;
+
+import com.ematou.wxbase.common.web.R;
+import com.ematou.wxbase.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 全局异常处理器
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/18 15:35
+ */
+@RestControllerAdvice
+@Slf4j
+public class GlobalExceptionHandler {
+
+    /**
+     * 业务异常
+     */
+    @ExceptionHandler(ServiceException.class)
+    public R handleServiceException(ServiceException e, HttpServletRequest request)
+    {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址【{}】,发生业务异常【{}】", requestURI, e.getMessage());
+        return R.error(e.getMessage());
+    }
+
+    /**
+     * 系统异常
+     */
+    @ExceptionHandler(Exception.class)
+    public R handleException(Exception e, HttpServletRequest request)
+    {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址'{}',发生系统异常.", requestURI, e);
+        return R.error("系统异常!");
+    }
+
+
+
+}

+ 24 - 0
src/main/java/com/ematou/wxbase/mapper/EidMerchMapper.java

@@ -0,0 +1,24 @@
+package com.ematou.wxbase.mapper;
+
+import com.ematou.wxbase.entity.EidMerch;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * E证通商户 Mapper
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/19 11:18
+ */
+@Mapper
+public interface EidMerchMapper {
+
+    /**
+     * 根据商户号查询商户E证通配置信息
+     *
+     * @param merchSn
+     * @return
+     */
+    EidMerch selectByMerchSn(String merchSn);
+
+}

+ 24 - 0
src/main/java/com/ematou/wxbase/mapper/EidTokenRecordMapper.java

@@ -0,0 +1,24 @@
+package com.ematou.wxbase.mapper;
+
+import com.ematou.wxbase.entity.EidTokenRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * E证通 token 记录 Mapper
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/16 16:33
+ */
+@Mapper
+public interface EidTokenRecordMapper {
+
+    /**
+     * 插入E证通 token 记录表
+     * @param record
+     * @return
+     */
+    int insert(EidTokenRecord record);
+
+}

+ 26 - 0
src/main/java/com/ematou/wxbase/mapper/MerchAppMapper.java

@@ -0,0 +1,26 @@
+package com.ematou.wxbase.mapper;
+
+import com.ematou.wxbase.entity.MerchApp;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 商户应用表
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/18 17:30
+ */
+@Mapper
+public interface MerchAppMapper {
+
+    /**
+     * 根据条件查询商户应用
+     *
+     * @param merchApp
+     * @return
+     */
+    List<MerchApp> selectByCondition(MerchApp merchApp);
+
+}

+ 32 - 0
src/main/java/com/ematou/wxbase/mapper/MerchInfoMapper.java

@@ -0,0 +1,32 @@
+package com.ematou.wxbase.mapper;
+
+import com.ematou.wxbase.entity.MerchInfo;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 商户信息 Mapper
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/17 14:42
+ */
+@Mapper
+public interface MerchInfoMapper {
+
+    /**
+     * 插入商户信息
+     *
+     * @param merchInfo
+     * @return
+     */
+    int insert(MerchInfo merchInfo);
+
+    /**
+     * 根据条件查询商户信息
+     *
+     * @param merchInfo
+     * @return
+     */
+    MerchInfo selectOne(MerchInfo merchInfo);
+
+}

+ 1 - 1
src/main/java/com/ematou/wxbase/scheduler/RefreshAccessTokenScheduler.java

@@ -33,7 +33,7 @@ public class RefreshAccessTokenScheduler {
     /**
      * 定时器刷新token
      */
-    @Scheduled(cron = "0 0 0/2 * * ?", zone = "Asia/Shanghai")
+    @Scheduled(cron = "0 0 0/1 * * ?", zone = "Asia/Shanghai")
     public void refreshAccessToken() {
         try {
             logger.info("定时器开始刷新Token...");

+ 27 - 0
src/main/java/com/ematou/wxbase/service/EidService.java

@@ -0,0 +1,27 @@
+package com.ematou.wxbase.service;
+
+import com.ematou.wxbase.common.web.R;
+import com.ematou.wxbase.entity.dto.EidTokenRequestDTO;
+import com.ematou.wxbase.entity.vo.EidTokenResponseVO;
+
+/**
+ * E证通业务接口
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/18 16:29
+ */
+public interface EidService {
+
+    /**
+     * 获取E证通 Token
+     * @return
+     */
+    R<EidTokenResponseVO> getEidToken(EidTokenRequestDTO requestDTO);
+
+    /**
+     * 获取E证通结果
+     */
+    R getEidResult();
+
+}

+ 22 - 0
src/main/java/com/ematou/wxbase/service/EidTokenRecordService.java

@@ -0,0 +1,22 @@
+package com.ematou.wxbase.service;
+
+import org.springframework.stereotype.Service;
+
+/**
+ * E证通 token service
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/16 15:28
+ */
+public interface EidTokenRecordService {
+
+    /**
+     * 获取E证通 token
+     *
+     * @param merchSn 商户号
+     * @return
+     */
+    String getEidToken(String merchSn);
+
+}

+ 23 - 0
src/main/java/com/ematou/wxbase/service/MerchInfoService.java

@@ -0,0 +1,23 @@
+package com.ematou.wxbase.service;
+
+import com.ematou.wxbase.common.web.R;
+import com.ematou.wxbase.entity.MerchInfo;
+
+/**
+ * 商户信息 service
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/17 15:12
+ */
+public interface MerchInfoService {
+
+    /**
+     * 插入商户信息
+     *
+     * @param merchInfo
+     * @return
+     */
+    R insert(MerchInfo merchInfo);
+
+}

+ 2 - 1
src/main/java/com/ematou/wxbase/service/TokenRecordService.java

@@ -188,7 +188,8 @@ public class TokenRecordService {
 
         if (StringUtils.hasLength(accessToken) && expiresIn != null && errCode == null && !StringUtils.hasLength(errMsg)) {
             Date now = new Date();
-            long expiresTime = now.getTime() + (expiresIn * 1000);
+            //  微信返回数据结束时间为7200s 生成的结束时间 - 10分钟防止accessToken 过期导致无效
+            long expiresTime = now.getTime() + ((expiresIn - (60 * 10)) * 1000);
             tokenRecord.setAccessToken(accessToken);
             tokenRecord.setExpiresIn(expiresIn);
             tokenRecord.setEffectTime(DateUtils.formatDate(now));

+ 253 - 0
src/main/java/com/ematou/wxbase/service/impl/EidServiceImpl.java

@@ -0,0 +1,253 @@
+package com.ematou.wxbase.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.ematou.wxbase.common.constant.RedisKey;
+import com.ematou.wxbase.common.constant.TokenType;
+import com.ematou.wxbase.common.web.R;
+import com.ematou.wxbase.entity.*;
+import com.ematou.wxbase.entity.dto.EidTokenRequestDTO;
+import com.ematou.wxbase.entity.vo.EidTokenResponseVO;
+import com.ematou.wxbase.exception.Assert;
+import com.ematou.wxbase.exception.ServiceException;
+import com.ematou.wxbase.mapper.EidMerchMapper;
+import com.ematou.wxbase.mapper.EidTokenRecordMapper;
+import com.ematou.wxbase.mapper.MerchAppMapper;
+import com.ematou.wxbase.mapper.MerchInfoMapper;
+import com.ematou.wxbase.service.EidService;
+import com.ematou.wxbase.service.OperationRecordService;
+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.GetEidTokenConfig;
+import com.tencentcloudapi.faceid.v20180301.models.GetEidTokenRequest;
+import com.tencentcloudapi.faceid.v20180301.models.GetEidTokenResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * E证通业务实现类
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/18 16:56
+ */
+@Service
+@Slf4j
+public class EidServiceImpl implements EidService {
+
+    @Resource
+    private MerchInfoMapper merchInfoMapper;
+
+    @Resource
+    private MerchAppMapper merchAppMapper;
+
+    @Resource
+    private EidMerchMapper eidMerchMapper;
+
+    @Resource
+    private OperationRecordService recordService;
+
+    @Resource
+    private EidTokenRecordMapper eidTokenRecordMapper;
+
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
+    /**
+     * 获取E证通 Token
+     *
+     * @return
+     */
+    @Override
+    public R<EidTokenResponseVO> getEidToken(EidTokenRequestDTO requestDTO) {
+        // 校验参数
+        validateParam(requestDTO);
+        // 校验商户
+        validateMerch(requestDTO.getMerchSn());
+        // 校验商户应用
+        validateMerchApp(requestDTO.getMerchSn(), requestDTO.getAppCode());
+        // 查询E证通商户配置
+        EidMerch eidMerch = eidMerchMapper.selectByMerchSn(requestDTO.getMerchSn());
+        Assert.notNull(eidMerch, "商户E证通配置不存在!");
+
+        // 响应数据
+        EidTokenResponseVO responseVO =
+                EidTokenResponseVO.builder()
+                        .idCard(requestDTO.getIdCard())
+                        .userName(requestDTO.getUserName())
+                        .build();
+
+        // 获取E证通缓存键(功能名称:商户号:应用编码:核验人身份证号),例如: get_eid_token:merch123:kmall-pt:431422196503217896
+        String cacheKey =
+                RedisKey.GET_EID_TOKEN +
+                        requestDTO.getMerchSn() + ":" +
+                        requestDTO.getAppCode() + ":" +
+                        requestDTO.getIdCard();
+        // 获取缓存中的 token
+        String cacheEidToken = (String) redisTemplate.opsForValue().get(cacheKey);
+        if (StrUtil.isNotBlank(cacheEidToken)) {
+            // 如果不为空,缓存存在
+            // 将 token 组装到响应数据中
+            throw new ServiceException("请勿重复请求!");
+        }else {
+            // 如果为空,缓存不存在
+            // 请求E证通 token
+            String eidToken = reqEidToken(eidMerch, requestDTO.getUserName(), requestDTO.getIdCard());
+            //String eidToken = "1a2b3c4d5e6f";
+            // 将E证通 token 设置进 Redis,并设置过期时间
+            redisTemplate.opsForValue().set(cacheKey, eidToken, Duration.ofSeconds(1L));
+            // 插入操作记录
+            insertOptRecord(requestDTO);
+            // 插入E证通操作记录
+            insertEidTokenRecord(requestDTO, eidToken);
+            // 返回结果组装
+            responseVO.setEidToken(eidToken);
+        }
+
+        // 返回结果
+        return R.success(responseVO);
+    }
+
+
+    /**
+     * 获取E证通结果
+     */
+    @Override
+    public R<?> getEidResult() {
+        return null;
+    }
+
+    /**
+     * 校验参数
+     *
+     * @param requestDTO
+     */
+    private void validateParam(EidTokenRequestDTO requestDTO) {
+        Assert.notEmpty(requestDTO.getMerchSn(), "商户号不能为空!");
+        Assert.notEmpty(requestDTO.getAppCode(), "应用编码不能为空!");
+        Assert.notEmpty(requestDTO.getUserName(), "用户名不能为空!");
+        Assert.notEmpty(requestDTO.getIdCard(), "用户身份证不能为空!");
+    }
+
+    /**
+     * 校验商户应用
+     *
+     * @param merchSn
+     * @param appCode
+     */
+    private void validateMerchApp(String merchSn, String appCode) {
+        MerchApp query = new MerchApp();
+        query.setMerchSn(merchSn);
+        query.setAppCode(appCode);
+        List<MerchApp> merchApps = merchAppMapper.selectByCondition(query);
+        Assert.notTrue(merchApps.size() < 1, "商户应用不存在!");
+    }
+
+    /**
+     * 校验商户
+     *
+     * @param merchSn
+     */
+    private void validateMerch(String merchSn) {
+        MerchInfo query = new MerchInfo();
+        query.setMerchSn(merchSn);
+        MerchInfo merchInfo = merchInfoMapper.selectOne(query);
+        Assert.notNull(merchInfo, "商户信息不存在!");
+    }
+
+    /**
+     * 获取E证通 token
+     *
+     * @param eidMerch E证通商户配置
+     * @param userName 核验人姓名
+     * @param idCard 核验人身份证
+     * @return
+     */
+    private String reqEidToken(EidMerch eidMerch, String userName, String idCard) {
+        try {
+            // 创建访问凭据
+            Credential cred = new Credential(eidMerch.getSecretId(), eidMerch.getSecretKey());
+            // 实例化一个http选项,可选的,没有特殊需求可以跳过
+            HttpProfile httpProfile = new HttpProfile();
+            httpProfile.setEndpoint("faceid.tencentcloudapi.com");
+            // 实例化一个client选项,可选的,没有特殊需求可以跳过
+            ClientProfile clientProfile = new ClientProfile();
+            clientProfile.setHttpProfile(httpProfile);
+            // 实例化要请求产品的client对象,clientProfile是可选的
+            FaceidClient client = new FaceidClient(cred, "", clientProfile);
+            // 实例化一个请求对象,每个接口都会对应一个request对象
+            GetEidTokenRequest req = new GetEidTokenRequest();
+            // 设置商户 id
+            req.setMerchantId(eidMerch.getMerchId());
+            // 设置核验人姓名
+            req.setName(userName);
+            // 设置核验人身份证
+            req.setIdCard(idCard);
+            GetEidTokenConfig config = new GetEidTokenConfig();
+            config.setInputType("4");
+            req.setConfig(config);
+            // 返回的resp是一个GetEidTokenResponse的实例,与请求对象对应
+            GetEidTokenResponse resp = client.GetEidToken(req);
+            // 输出json格式的字符串回包
+            log.info("E证通返回结果: " + GetEidTokenResponse.toJsonString(resp));
+            // 获取 EidToken
+            String eidToken = resp.getEidToken();
+            // 返回 token
+            return eidToken;
+        } catch (TencentCloudSDKException e) {
+            log.error("腾讯云 SDK 请求E证通 token 异常.", e);
+            throw new ServiceException("请求E证通异常!");
+        }
+
+    }
+
+    /**
+     * 插入操作记录
+     *
+     * @param requestDTO
+     */
+    private void insertOptRecord(EidTokenRequestDTO requestDTO) {
+        OperationRecord operationRecord =
+                OperationRecord.builder()
+                        .appCode(requestDTO.getAppCode())
+                        .merchSn(requestDTO.getMerchSn())
+                        .tokenType(TokenType.EID_TOKEN.getItem())
+                        .build();
+        // 插入操作记录
+        recordService.insertRecord(operationRecord);
+    }
+
+    /**
+     * 插入E证通 token 记录
+     *
+     * @param requestDTO
+     * @param eidToken
+     */
+    private void insertEidTokenRecord(EidTokenRequestDTO requestDTO, String eidToken) {
+        // token 生成时间
+        LocalDateTime generationTime = LocalDateTime.now();
+        // 过期时间, 生成时间 + 60s
+        LocalDateTime expireTime = generationTime.plusMinutes(8L);
+        EidTokenRecord eidTokenRecord = EidTokenRecord.builder()
+                .merchSn(requestDTO.getMerchSn())
+                .appCode(requestDTO.getAppCode())
+                .token(eidToken)
+                .generationTime(generationTime)
+                .expireTime(expireTime)
+                .idCard(requestDTO.getIdCard())
+                .userName(requestDTO.getUserName())
+                .build();
+        int insert = eidTokenRecordMapper.insert(eidTokenRecord);
+        Assert.notTrue(insert < 1, "插入E证通 token 记录失败!");
+    }
+
+}

+ 51 - 0
src/main/java/com/ematou/wxbase/service/impl/EidTokenRecordServiceImpl.java

@@ -0,0 +1,51 @@
+package com.ematou.wxbase.service.impl;
+
+import com.ematou.wxbase.entity.EidTokenRecord;
+import com.ematou.wxbase.mapper.EidTokenRecordMapper;
+import com.ematou.wxbase.service.EidTokenRecordService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.UUID;
+
+/**
+ * E证通 token 实现类
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/16 15:30
+ */
+@Service
+public class EidTokenRecordServiceImpl implements EidTokenRecordService {
+
+    private static final Logger log = LoggerFactory.getLogger(EidTokenRecordServiceImpl.class);
+
+    @Resource
+    private EidTokenRecordMapper eidTokenRecordMapper;
+
+    @Override
+    public String getEidToken(String merchSn) {
+
+        String token = UUID.randomUUID().toString().replace("-", "");
+        insertRecord(merchSn, token);
+
+        return token;
+    }
+
+    private void insertRecord(String merchSn, String token) {
+        LocalDateTime localDateTime = LocalDateTime.now();
+        EidTokenRecord record = new EidTokenRecord();
+        record.setToken(token);
+        record.setMerchSn(merchSn);
+        record.setGenerationTime(localDateTime);
+        record.setExpireTime(localDateTime.plusMinutes(1));
+
+        int insert = eidTokenRecordMapper.insert(record);
+        log.debug("插入是否成功 => {}", insert);
+    }
+
+
+}

+ 41 - 0
src/main/java/com/ematou/wxbase/service/impl/MerchInfoServiceImpl.java

@@ -0,0 +1,41 @@
+package com.ematou.wxbase.service.impl;
+
+import com.ematou.wxbase.common.web.R;
+import com.ematou.wxbase.entity.MerchInfo;
+import com.ematou.wxbase.mapper.MerchInfoMapper;
+import com.ematou.wxbase.service.MerchInfoService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * [类描述]
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/17 15:13
+ */
+@Service
+public class MerchInfoServiceImpl implements MerchInfoService {
+
+    @Resource
+    private MerchInfoMapper merchInfoMapper;
+
+    /**
+     * 插入商户信息
+     *
+     * @param merchInfo
+     * @return
+     */
+    @Override
+    public R insert(MerchInfo merchInfo) {
+        int insert = merchInfoMapper.insert(merchInfo);
+
+        if (insert > 0)
+            return R.success(null);
+        else
+            return R.error("新增商户信息失败");
+
+    }
+
+}

+ 17 - 0
src/main/resources/application-prod.yml

@@ -0,0 +1,17 @@
+server:
+  port: 4040
+
+spring:
+  # 数据源配置
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    username: wx_base
+    password: goZ7ooGmxV
+    url: jdbc:mysql://out-rm-wz92efl25x02n44xego.mysql.rds.aliyuncs.com:3306/wx_base?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+  # redis 配置
+  redis:
+    database: 1
+    host: 120.78.152.8
+    port: 6379
+    password: Abc-123#*.-
+

+ 17 - 0
src/main/resources/application-test.yml

@@ -0,0 +1,17 @@
+server:
+  port: 3030
+
+spring:
+  # 数据源配置
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    username: tuser
+    password: Qq!123
+    url: jdbc:mysql://47.112.115.196:3306/wx_base?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+  # redis 配置
+  redis:
+    database: 1
+    host: 183.62.225.124
+    port: 6379
+    password: Abc-123#*.-
+

+ 5 - 8
src/main/resources/application.yml

@@ -1,18 +1,15 @@
-server:
-  port: 3030
-
 wx:
   general:
     appId: wxf9360d70bc1406ee
     appSecret: 78413a82d0332ecbf7fdf475d0a8b08e
     grantType: client_credential
     url: https://api.weixin.qq.com/cgi-bin/token?grant_type=%s&appid=%s&secret=%s
+
 spring:
-  datasource:
-    driver-class-name: com.mysql.cj.jdbc.Driver
-    username: tuser
-    password: Qq!123
-    url: jdbc:mysql://120.76.84.45:3306/wx_base?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+  # 环境切换
+  profiles:
+    active: prod
+
 mybatis:
   mapper-locations: classpath:mybatis/*.xml
   configuration:

+ 25 - 0
src/main/resources/mybatis/EidMerchMapper.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ematou.wxbase.mapper.EidMerchMapper">
+
+    <sql id="eidMerchColumns">
+        t.id,
+        t.merch_sn,
+        t.secret_id,
+        t.secret_key,
+        t.merch_id,
+        t.token_expired
+    </sql>
+
+    <!-- 根据商户号查询商户E证通配置信息 -->
+    <select id="selectByMerchSn" resultType="com.ematou.wxbase.entity.EidMerch">
+        SELECT
+            <include refid="eidMerchColumns" />
+        FROM
+            `eid_merch` t
+        WHERE t.merch_sn = #{merchSn};
+    </select>
+
+</mapper>

+ 24 - 0
src/main/resources/mybatis/EidTokenRecordMapper.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ematou.wxbase.mapper.EidTokenRecordMapper">
+
+    <!-- 插入E证通 token 记录表 -->
+    <insert id="insert">
+        INSERT INTO eid_token_record ( merch_sn, app_code, token, generation_time, expire_time, id_card, user_name )
+        VALUES
+            (
+                #{merchSn},
+                #{appCode},
+                #{token},
+                #{generationTime},
+                #{expireTime},
+                #{idCard},
+                #{userName}
+            )
+    </insert>
+
+</mapper>
+
+

+ 32 - 0
src/main/resources/mybatis/MerchAppMapper.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ematou.wxbase.mapper.MerchAppMapper">
+
+    <sql id="merchAppColumns">
+        t.merch_sn,
+        t.app_code,
+        t.app_name
+    </sql>
+
+    <!-- 根据条件查询商户应用信息 -->
+    <select id="selectByCondition" resultType="com.ematou.wxbase.entity.MerchApp">
+        SELECT
+            <include refid="merchAppColumns" />
+        FROM
+            merch_app t
+        <where>
+            <if test="merchSn != null and merchSn !='' ">
+                AND merch_sn = #{merchSn}
+            </if>
+            <if test="appCode != null and appCode !='' ">
+                AND app_code = #{appCode}
+            </if>
+            <if test="appName != null and appName !='' ">
+                AND app_name = #{appName}
+            </if>
+        </where>
+    </select>
+
+</mapper>

+ 33 - 0
src/main/resources/mybatis/MerchInfoMapper.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ematou.wxbase.mapper.MerchInfoMapper">
+
+    <sql id="merchInfoColumns">
+        t.merch_sn,
+        t.merch_name
+    </sql>
+
+    <!-- 插入商户信息 -->
+    <insert id="insert">
+        INSERT INTO merch_info(merch_sn, merch_name) VALUES (#{merchSn}, #{merchName});
+    </insert>
+
+    <!-- 根据条件查询商户信息 -->
+    <select id="selectOne" resultType="com.ematou.wxbase.entity.MerchInfo">
+        SELECT
+            <include refid="merchInfoColumns" />
+        FROM
+            merch_info t
+        <where>
+            <if test="merchSn !=null and merchSn != '' ">
+                and merch_sn = #{merchSn}
+            </if>
+            <if test="merchName !=null and merchName != '' ">
+                and merch_name = #{merchName}
+            </if>
+        </where>
+    </select>
+
+</mapper>

+ 12 - 0
src/main/resources/mybatis/OperationRecordMapper.xml

@@ -17,6 +17,12 @@
             <if test="record.appCode != null">
                 app_code,
             </if>
+            <if test="record.merchSn != null">
+                merch_sn,
+            </if>
+            <if test="record.tokenType != null">
+                token_type,
+            </if>
             <if test="record.createrSn != null">
                 creater_sn,
             </if>
@@ -37,6 +43,12 @@
             <if test="record.appCode != null">
                 #{record.appCode},
             </if>
+            <if test="record.merchSn != null">
+                #{record.merchSn},
+            </if>
+            <if test="record.tokenType != null">
+                #{record.tokenType},
+            </if>
             <if test="record.createrSn != null">
                 #{record.createrSn},
             </if>

+ 26 - 0
src/test/java/com/ematou/wxbase/EidTokenRecordServiceTest.java

@@ -0,0 +1,26 @@
+package com.ematou.wxbase;
+
+import com.ematou.wxbase.service.EidTokenRecordService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+/**
+ * E证通 token 记录 service 测试类
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/16 17:02
+ */
+@SpringBootTest
+public class EidTokenRecordServiceTest {
+
+    @Autowired
+    private EidTokenRecordService service;
+
+    @Test
+    public void testGetToken() {
+        System.out.println(service.getEidToken("mrch123456") + " =========");
+    }
+
+}

+ 60 - 0
src/test/java/com/ematou/wxbase/EidTokenTest.java

@@ -0,0 +1,60 @@
+package com.ematou.wxbase;
+
+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.GetEidTokenRequest;
+import com.tencentcloudapi.faceid.v20180301.models.GetEidTokenResponse;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.test.context.SpringBootTest;
+
+/**
+ * E证通 token 单元测试
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/16 15:22
+ */
+@SpringBootTest
+public class EidTokenTest {
+
+    Logger log = LoggerFactory.getLogger(EidTokenTest.class);
+
+    private final static String secretId = "AKIDIRYotrdCZnqkT0LTVzJSQFrGmLoALLJA";
+    private final static String secretKey = "jICxWrJchWBg8RYO2RZYLwMXh0bR6imd";
+    private final static String merchId = "0NSJ2302171312018748";
+
+    @Test
+    public void getEidToken() {
+        try {
+            // 创建访问凭据
+            Credential cred = new Credential(secretId, secretKey);
+            // 实例化一个http选项,可选的,没有特殊需求可以跳过
+            HttpProfile httpProfile = new HttpProfile();
+            httpProfile.setEndpoint("faceid.tencentcloudapi.com");
+            // 实例化一个client选项,可选的,没有特殊需求可以跳过
+            ClientProfile clientProfile = new ClientProfile();
+            clientProfile.setHttpProfile(httpProfile);
+            // 实例化要请求产品的client对象,clientProfile是可选的
+            FaceidClient client = new FaceidClient(cred, "", clientProfile);
+            // 实例化一个请求对象,每个接口都会对应一个request对象
+            GetEidTokenRequest req = new GetEidTokenRequest();
+            // 设置商户 id
+            req.setMerchantId(merchId);
+            req.setIdCard("431422196503217896");
+            req.setName("张三");
+            // 返回的resp是一个GetEidTokenResponse的实例,与请求对象对应
+            GetEidTokenResponse resp = client.GetEidToken(req);
+            // 输出json格式的字符串回包
+            System.out.println(GetEidTokenResponse.toJsonString(resp));
+            resp.getEidToken();
+        } catch (TencentCloudSDKException e) {
+            log.error("腾讯云 SDK Error", e);
+        }
+    }
+
+}

+ 31 - 0
src/test/java/com/ematou/wxbase/MerchAppMapperTest.java

@@ -0,0 +1,31 @@
+package com.ematou.wxbase;
+
+import com.ematou.wxbase.entity.MerchApp;
+import com.ematou.wxbase.mapper.MerchAppMapper;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.annotation.Resource;
+
+/**
+ * 商户应用 Mapper 测试类
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/18 17:35
+ */
+@SpringBootTest
+public class MerchAppMapperTest {
+
+    @Resource
+    private MerchAppMapper mapper;
+
+    @Test
+    public void testSelect() {
+        MerchApp merchApp = new MerchApp();
+        merchApp.setMerchSn("mhbs990053989883052032");
+        merchApp.setAppCode("Kmall-cw");
+        System.out.println(mapper.selectByCondition(merchApp));
+    }
+
+}

+ 41 - 0
src/test/java/com/ematou/wxbase/MerchInfoServiceTest.java

@@ -0,0 +1,41 @@
+package com.ematou.wxbase;
+
+import com.ematou.wxbase.entity.MerchInfo;
+import com.ematou.wxbase.mapper.MerchInfoMapper;
+import com.ematou.wxbase.service.MerchInfoService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+/**
+ * E证通 token 记录 service 测试类
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/16 17:02
+ */
+@SpringBootTest
+public class MerchInfoServiceTest {
+
+    @Autowired
+    private MerchInfoService service;
+
+    @Autowired
+    private MerchInfoMapper mapper;
+
+    @Test
+    public void testMapperSelectOne() {
+        MerchInfo merchInfo = new MerchInfo();
+        merchInfo.setMerchSn("mhbs990053989883052032");
+        System.out.println(mapper.selectOne(merchInfo));
+    }
+
+    @Test
+    public void testInsert() {
+        MerchInfo merchInfo = new MerchInfo();
+        merchInfo.setMerchSn("mhbs990053989883052032");
+        merchInfo.setMerchName("中网科技");
+        System.out.println(service.insert(merchInfo));
+    }
+
+}

+ 40 - 0
src/test/java/com/ematou/wxbase/RedisTest.java

@@ -0,0 +1,40 @@
+package com.ematou.wxbase;
+
+import cn.hutool.core.util.StrUtil;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.time.Duration;
+
+/**
+ * Redis 测试类
+ *
+ * @author frankeleyn
+ * @email lvjian@qhdswl.com
+ * @date 2023/2/20 15:35
+ */
+@SpringBootTest
+public class RedisTest {
+
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
+    @Test
+    public void testSet() {
+        redisTemplate.opsForValue().set("get_eid_token:merch123:kmall-pt:431422196503217896", "1234556", Duration.ofSeconds(60L));
+    }
+
+    @Test
+    public void testDelete() {
+        redisTemplate.delete("lj_hh");
+    }
+
+    @Test
+    public void testGet() {
+        String o = (String) redisTemplate.opsForValue().get("get_eid_token:merch123:kmall-pt:431422196503217896");
+        System.out.println("Redis 取值 => " + o + " 是否为空 =》 " + StrUtil.isNotBlank(o));
+    }
+
+}