From 8883d654f52171657b62aaec85027f29ef0a4dd0 Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期日, 26 二月 2023 22:30:39 +0800
Subject: [PATCH] 新增加用户端接口

---
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java            |   18 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java                            |   38 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java                 |   26 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java              |   67 +
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java                  |   11 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java            |   31 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java         |  147 ++
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java                        |  250 +++-
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java                        |   11 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java         |   17 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java   |   26 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java                       |   72 +
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java            |   63 +
 user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java                  |   72 +
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java          |   16 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java      |  264 ++++
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml                 |   12 
 user/guns-admin/pom.xml                                                                                           |   61 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java              |   59 +
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.xml              |   25 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java               |   27 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java                  |   71 +
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java     |   36 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java                       |   59 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java                           |   50 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java                         |  103 +
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java       |  156 ++
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java             |  288 +++++
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java              |   23 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java |   26 
 /dev/null                                                                                                         |  266 ----
 user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java                      |   23 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java                    |   52 
 user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java                                   |   46 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java        |  269 ++++
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java                |  415 +++++++
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java               |   49 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java                     |    7 
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java                        |   66 +
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java                      |   61 +
 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java        |   33 
 41 files changed, 3,001 insertions(+), 411 deletions(-)

diff --git a/user/guns-admin/pom.xml b/user/guns-admin/pom.xml
index d8673f4..98fd737 100644
--- a/user/guns-admin/pom.xml
+++ b/user/guns-admin/pom.xml
@@ -63,6 +63,12 @@
         </dependency>
 
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>2.11.3</version>
+        </dependency>
+
+        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-tomcat</artifactId>
             <scope>provided</scope>
@@ -104,16 +110,11 @@
             <artifactId>ehcache</artifactId>
         </dependency>
 
-        <!--需要分布式session的话需要放开注释-->
-        <!--<dependency>-->
-            <!--<groupId>org.springframework.session</groupId>-->
-            <!--<artifactId>spring-session-data-redis</artifactId>-->
-        <!--</dependency>-->
-        <!--<dependency>-->
-            <!--<groupId>org.springframework.boot</groupId>-->
-            <!--<artifactId>spring-boot-starter-data-redis</artifactId>-->
-        <!--</dependency>-->
-
+        <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+            <version>2.9.0</version>
+        </dependency>
         <dependency>
             <groupId>com.github.penggle</groupId>
             <artifactId>kaptcha</artifactId>
@@ -181,44 +182,12 @@
             <artifactId>geodesy</artifactId>
             <version>1.1.3</version>
         </dependency>
-
-        <!--引入本地工行支付jar start-->
         <dependency>
-            <groupId>com.icbc</groupId>
-            <artifactId>icbc</artifactId>
-            <version>v2</version>
-            <scope>system</scope>
-            <systemPath>${project.basedir}/lib/icbc-api-sdk-cop.jar</systemPath>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.20</version>
+            <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>com.icbc.api</groupId>
-            <artifactId>icbc</artifactId>
-            <version>v2</version>
-            <scope>system</scope>
-            <systemPath>${project.basedir}/lib/icbc-api-sdk-cop-io.jar</systemPath>
-        </dependency>
-        <dependency>
-            <groupId>cn.com.infosec</groupId>
-            <artifactId>icbc</artifactId>
-            <version>v2</version>
-            <scope>system</scope>
-            <systemPath>${project.basedir}/lib/icbc-ca.jar</systemPath>
-        </dependency>
-        <dependency>
-            <groupId>cn.com.infosecCrypto</groupId>
-            <artifactId>icbc</artifactId>
-            <version>v2</version>
-            <scope>system</scope>
-            <systemPath>${project.basedir}/lib/InfosecCrypto_Java1_02_JDK14+.jar</systemPath>
-        </dependency>
-        <dependency>
-            <groupId>proguard</groupId>
-            <artifactId>icbc</artifactId>
-            <version>v2</version>
-            <scope>system</scope>
-            <systemPath>${project.basedir}/lib/proguard.jar</systemPath>
-        </dependency>
-        <!--引入本地工行支付jar end-->
 
         <dependency>
             <groupId>commons-codec</groupId>
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java b/user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java
new file mode 100644
index 0000000..949a267
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java
@@ -0,0 +1,46 @@
+package com.supersavedriving.user.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+@Configuration
+@PropertySource("classpath:redis.properties")
+public class RedisConfig {
+    @Value("${spring.redis.host}")
+    private String host;
+
+    @Value("${spring.redis.port}")
+    private int port;
+
+    @Value("${spring.redis.timeout}")
+    private int timeout;
+
+    @Value("${spring.redis.jedis.pool.max-idle}")
+    private int maxIdle;
+
+    @Value("${spring.redis.jedis.pool.max-wait}")
+    private long maxWaitMillis;
+
+    @Value("${spring.redis.password}")
+    private String password;
+
+    @Value("${spring.redis.block-when-exhausted}")
+    private boolean  blockWhenExhausted;
+
+    @Bean
+    public JedisPool redisPoolFactory()  throws Exception{
+        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
+        jedisPoolConfig.setMaxIdle(maxIdle);
+        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
+        // 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
+        jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted);
+        // 是否启用pool的jmx管理功能, 默认true
+        jedisPoolConfig.setJmxEnabled(true);
+        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
+        return jedisPool;
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java b/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java
new file mode 100644
index 0000000..668ab0f
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java
@@ -0,0 +1,23 @@
+package com.supersavedriving.user.core.common.annotion;
+
+import java.lang.annotation.*;
+
+/**
+ * 接口日志注解
+ */
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface ServiceLog {
+    /**
+     * 接口名称
+     * @return
+     */
+    String name() default "";
+
+    /**
+     * 接口地址
+     * @return
+     */
+    String url() default "";
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java b/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java
new file mode 100644
index 0000000..4850d1c
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java
@@ -0,0 +1,72 @@
+package com.supersavedriving.user.core.common.aspect;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supersavedriving.user.core.common.annotion.ServiceLog;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+@Aspect
+@Component
+public class ServiceLogAspect {
+
+    Logger logger = LoggerFactory.getLogger("ServiceLog");
+
+    /**
+     * //切面点为标记了@ServiceLog注解的方法
+     */
+    @Pointcut("@annotation(com.supersavedriving.user.core.common.annotion.ServiceLog)")
+    public void serviceLog(){
+    }
+
+
+    //环绕通知
+    @Around("serviceLog()")
+    @SuppressWarnings("unchecked")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        long starTime = System.currentTimeMillis();
+        //通过反射获取被调用方法的Class
+        Class type = joinPoint.getSignature().getDeclaringType();
+        //获取类名
+        String typeName = type.getSimpleName();
+        //方法名
+        String methodName = joinPoint.getSignature().getName();
+        //获取参数列表
+        Object[] args = joinPoint.getArgs();
+        //参数Class的数组
+        Class[] clazz = new Class[args.length];
+        for (int i = 0; i < args.length; i++) {
+            clazz[i] = args[i].getClass();
+        }
+        //通过反射获取调用的方法method
+        Method method = type.getMethod(methodName, clazz);
+        ServiceLog serviceLog = method.getAnnotation(ServiceLog.class);
+        //获取方法的参数
+        Parameter[] parameters = method.getParameters();
+        JSONObject jsonObject = new JSONObject();
+        for (int i = 0; i < parameters.length; i++) {
+            Parameter parameter = parameters[i];
+            String name = parameter.getName();
+            jsonObject.put(name, args[i]);
+        }
+        //执行结果
+        //执行目标方法,获取执行结果
+        Object res = joinPoint.proceed();
+        logger.debug("调用{}.{}方法成功\n" +
+                "接口名称:{}\n" +
+                "接口地址:{}\n" +
+                "耗时:{}ms\n" +
+                "参数为:{}\n" +
+                "返回结果:{}", typeName, methodName, serviceLog.name(), serviceLog.url(),
+                (System.currentTimeMillis() - starTime), jsonObject.toJSONString(), JSONObject.toJSONString(res));
+        //返回执行结果
+        return res;
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java
new file mode 100644
index 0000000..7781fe9
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java
@@ -0,0 +1,66 @@
+package com.supersavedriving.user.modular.api;
+
+import com.supersavedriving.user.core.common.annotion.ServiceLog;
+import com.supersavedriving.user.core.util.ToolUtil;
+import com.supersavedriving.user.modular.system.service.IAppUserService;
+import com.supersavedriving.user.modular.system.util.ResultUtil;
+import com.supersavedriving.user.modular.system.warpper.ResponseWarpper;
+import com.supersavedriving.user.modular.system.warpper.SignInToRegister;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 用户控制器
+ */
+@RestController
+@RequestMapping("")
+public class AppUserController {
+
+    @Autowired
+    private IAppUserService appUserService;
+
+
+    @ResponseBody
+    @PostMapping("/base/appUser/appUserLogin")
+    @ServiceLog(name = "微信登录", url = "/base/appUser/appUserLogin")
+    @ApiOperation(value = "微信登录", tags = {"用户端-首页"}, notes = "")
+    @ApiImplicitParams({
+            @ApiImplicitParam(value = "微信jscode", name = "jscode", required = true, dataType = "string"),
+    })
+    public ResponseWarpper appUserLogin(String jscode){
+        if(ToolUtil.isEmpty(jscode)){
+            return ResponseWarpper.success(ResultUtil.paranErr("jscode"));
+        }
+        try {
+            ResultUtil resultUtil = appUserService.appUserLogin(jscode);
+            return ResponseWarpper.success(resultUtil);
+        }catch (Exception e){
+            e.printStackTrace();
+            return new ResponseWarpper(500, e.getMessage());
+        }
+    }
+
+
+
+    @ResponseBody
+    @PostMapping("/base/appUser/signInToRegister")
+    @ServiceLog(name = "微信手机授权登录", url = "/base/appUser/signInToRegister")
+    @ApiOperation(value = "微信手机授权登录", tags = {"用户端-首页"}, notes = "")
+    @ApiImplicitParams({
+    })
+    public ResponseWarpper signInToRegister(SignInToRegister signInToRegister){
+        try {
+            ResultUtil resultUtil = appUserService.signInToRegister(signInToRegister);
+            return ResponseWarpper.success(resultUtil);
+        }catch (Exception e){
+            e.printStackTrace();
+            return new ResponseWarpper(500, e.getMessage());
+        }
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java
new file mode 100644
index 0000000..4e10ba7
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java
@@ -0,0 +1,50 @@
+package com.supersavedriving.user.modular.api;
+
+import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.supersavedriving.user.core.common.annotion.ServiceLog;
+import com.supersavedriving.user.modular.system.model.Html;
+import com.supersavedriving.user.modular.system.service.IHtmlService;
+import com.supersavedriving.user.modular.system.util.ResultUtil;
+import com.supersavedriving.user.modular.system.warpper.ResponseWarpper;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 协议控制器
+ */
+@RestController
+@RequestMapping("")
+public class HtmlController {
+
+    @Autowired
+    private IHtmlService htmlService;
+
+
+
+
+    @ResponseBody
+    @PostMapping("/base/html/queryHtml")
+    @ServiceLog(name = "获取各种协议和说明", url = "/base/html/queryHtml")
+    @ApiOperation(value = "获取各种协议和说明", tags = {"用户端-首页"}, notes = "")
+    @ApiImplicitParams({
+            @ApiImplicitParam(value = "类型(1=用户协议,2=隐私政策,3=法律条款,4=代驾服务协议,5=个人信息处理规则,6=积分说明,7=佣金规则说明,8=行程录音说明,9=预估价格说明,10=加盟基本要求,11=加盟流程,12=起步价说明,13=注销协议,14=关于我们,15=司机消单说明)", name = "type", required = true, dataType = "int"),
+    })
+    public ResponseWarpper<String> queryHtml(Integer type){
+        if(null == type){
+            return ResponseWarpper.success(ResultUtil.paranErr("type"));
+        }
+        try {
+            Html html = htmlService.selectOne(new EntityWrapper<Html>().eq("type", type));
+            return ResponseWarpper.success(null == html ? "" : html.getHtml());
+        }catch (Exception e){
+            e.printStackTrace();
+            return new ResponseWarpper(500, e.getMessage());
+        }
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java
new file mode 100644
index 0000000..ee22cc5
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java
@@ -0,0 +1,7 @@
+package com.supersavedriving.user.modular.system.dao;
+
+import com.baomidou.mybatisplus.mapper.BaseMapper;
+import com.supersavedriving.user.modular.system.model.AppUser;
+
+public interface AppUserMapper extends BaseMapper<AppUser> {
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java
new file mode 100644
index 0000000..d5089c0
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java
@@ -0,0 +1,11 @@
+package com.supersavedriving.user.modular.system.dao;
+
+import com.baomidou.mybatisplus.mapper.BaseMapper;
+import com.supersavedriving.user.modular.system.model.Html;
+
+/**
+ * @author 39373
+ * @date 2023/2/26 17:29
+ */
+public interface HtmlMapper extends BaseMapper<Html> {
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.xml b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.xml
new file mode 100644
index 0000000..235b934
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.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.supersavedriving.user.modular.system.dao.AppUserMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.supersavedriving.user.modular.system.model.AppUser">
+        <id column="id" property="id"/>
+        <result column="nickname" property="nickname"/>
+        <result column="phone" property="phone"/>
+        <result column="sex" property="sex"/>
+        <result column="avatar" property="avatar"/>
+        <result column="openid" property="openid"/>
+        <result column="unionid" property="unionid"/>
+        <result column="emergencyContact" property="emergencyContact"/>
+        <result column="emergencyPhone" property="emergencyPhone"/>
+        <result column="accountBalance" property="accountBalance"/>
+        <result column="userTagId" property="userTagId"/>
+        <result column="status" property="status"/>
+        <result column="createTime" property="createTime"/>
+        <result column="is_exception" property="isException"/>
+        <result column="remark" property="remark"/>
+        <result column="inviterType" property="inviterType"/>
+        <result column="inviterId" property="inviterId"/>
+    </resultMap>
+</mapper>
\ No newline at end of file
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml
new file mode 100644
index 0000000..f6500c6
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml
@@ -0,0 +1,12 @@
+<?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.supersavedriving.user.modular.system.dao.HtmlMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.supersavedriving.user.modular.system.model.Html">
+        <id column="id" property="id"/>
+        <result column="type" property="type"/>
+        <result column="html" property="html"/>
+        <result column="createTime" property="createTime"/>
+    </resultMap>
+</mapper>
\ No newline at end of file
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java
new file mode 100644
index 0000000..7564177
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java
@@ -0,0 +1,103 @@
+package com.supersavedriving.user.modular.system.model;
+
+import com.baomidou.mybatisplus.annotations.TableField;
+import com.baomidou.mybatisplus.annotations.TableId;
+import com.baomidou.mybatisplus.annotations.TableName;
+import com.baomidou.mybatisplus.enums.IdType;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 用户
+ */
+@Data
+@TableName("t_app_user")
+public class AppUser {
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    @TableField("id")
+    private Integer id;
+    /**
+     * 昵称
+     */
+    @TableField("nickname")
+    private String nickname;
+    /**
+     * 电话
+     */
+    @TableField("phone")
+    private String phone;
+    /**
+     * 性别(1=男,2=女)
+     */
+    @TableField("sex")
+    private Integer sex;
+    /**
+     * 头像
+     */
+    @TableField("avatar")
+    private String avatar;
+    /**
+     * 微信openid
+     */
+    @TableField("openid")
+    private String openid;
+    /**
+     * 微信unionid
+     */
+    @TableField("unionid")
+    private String unionid;
+    /**
+     * 紧急联系人
+     */
+    @TableField("emergencyContact")
+    private String emergencyContact;
+    /**
+     * 紧急联系人电话
+     */
+    @TableField("emergencyPhone")
+    private String emergencyPhone;
+    /**
+     * 账户余额
+     */
+    @TableField("accountBalance")
+    private Double accountBalance;
+    /**
+     * 用户标签id
+     */
+    @TableField("userTagId")
+    private Integer userTagId;
+    /**
+     * 状态(1=正常,2=冻结,3=删除)
+     */
+    @TableField("status")
+    private Integer status;
+    /**
+     * 添加时间
+     */
+    @TableField("createTime")
+    private Date createTime;
+    /**
+     * 是否异常 1正常 2异常
+     */
+    @TableField("is_exception")
+    private Integer isException;
+    /**
+     * 启动冻结理由
+     */
+    @TableField("remark")
+    private String remark;
+    /**
+     * 邀约人类型(1=用户,2=司机)
+     */
+    @TableField("inviterType")
+    private Integer inviterType;
+    /**
+     * 邀约人id
+     */
+    @TableField("inviterId")
+    private Integer inviterId;
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java
new file mode 100644
index 0000000..cad5459
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java
@@ -0,0 +1,38 @@
+package com.supersavedriving.user.modular.system.model;
+
+import com.baomidou.mybatisplus.annotations.TableField;
+import com.baomidou.mybatisplus.annotations.TableId;
+import com.baomidou.mybatisplus.annotations.TableName;
+import com.baomidou.mybatisplus.enums.IdType;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 协议
+ */
+@Data
+@TableName("t_html")
+public class Html {
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    @TableField("id")
+    private Integer id;
+    /**
+     * 类型(1=用户协议,2=隐私政策,3=法律条款,4=代驾服务协议,5=个人信息处理规则,6=积分说明,7=佣金规则说明,8=行程录音说明,9=预估价格说明,10=加盟基本要求,11=加盟流程,12=起步价说明,13=注销协议,14=关于我们)
+     */
+    @TableField("type")
+    private Integer type;
+    /**
+     * H5内容
+     */
+    @TableField("html")
+    private String html;
+    /**
+     * 添加时间
+     */
+    @TableField("createTime")
+    private Date createTime;
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java
new file mode 100644
index 0000000..e392733
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java
@@ -0,0 +1,27 @@
+package com.supersavedriving.user.modular.system.service;
+
+import com.baomidou.mybatisplus.service.IService;
+import com.supersavedriving.user.modular.system.model.AppUser;
+import com.supersavedriving.user.modular.system.util.ResultUtil;
+import com.supersavedriving.user.modular.system.warpper.SignInToRegister;
+
+public interface IAppUserService extends IService<AppUser> {
+
+
+    /**
+     * 用户登录
+     * @param jscode
+     * @return
+     * @throws Exception
+     */
+    ResultUtil appUserLogin(String jscode) throws Exception;
+
+
+    /**
+     * 微信授权注册登录
+     * @param signInToRegister
+     * @return
+     * @throws Exception
+     */
+    ResultUtil signInToRegister(SignInToRegister signInToRegister) throws Exception;
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java
new file mode 100644
index 0000000..264b1bf
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java
@@ -0,0 +1,11 @@
+package com.supersavedriving.user.modular.system.service;
+
+import com.baomidou.mybatisplus.service.IService;
+import com.supersavedriving.user.modular.system.model.Html;
+
+/**
+ * @author 39373
+ * @date 2023/2/26 17:30
+ */
+public interface IHtmlService extends IService<Html> {
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java
new file mode 100644
index 0000000..ab94bfa
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java
@@ -0,0 +1,156 @@
+package com.supersavedriving.user.modular.system.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.baomidou.mybatisplus.service.impl.ServiceImpl;
+import com.supersavedriving.user.core.shiro.ShiroKit;
+import com.supersavedriving.user.core.shiro.ShiroUser;
+import com.supersavedriving.user.core.util.JwtTokenUtil;
+import com.supersavedriving.user.core.util.ToolUtil;
+import com.supersavedriving.user.modular.system.dao.AppUserMapper;
+import com.supersavedriving.user.modular.system.model.AppUser;
+import com.supersavedriving.user.modular.system.service.IAppUserService;
+import com.supersavedriving.user.modular.system.util.RedisUtil;
+import com.supersavedriving.user.modular.system.util.ResultUtil;
+import com.supersavedriving.user.modular.system.util.weChat.WXCore;
+import com.supersavedriving.user.modular.system.util.weChat.WeChatUtil;
+import com.supersavedriving.user.modular.system.util.weChat.model.Code2Session;
+import com.supersavedriving.user.modular.system.warpper.SignInToRegister;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
+import org.apache.shiro.crypto.hash.Md5Hash;
+import org.apache.shiro.util.ByteSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+
+
+/**
+ * 用户
+ */
+@Service
+public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements IAppUserService {
+
+    @Autowired
+    private WeChatUtil weChatUtil;
+
+    @Autowired
+    private RedisUtil redisUtil;
+
+    private final String salt = "s5d1";
+
+
+
+    @Override
+    public ResultUtil<String> appUserLogin(String jscode) throws Exception {
+        Code2Session code2Session = weChatUtil.code2Session(jscode);
+        if(code2Session.getErrcode() != 0){
+            return ResultUtil.error(code2Session.getErrmsg());
+        }
+        String openid = code2Session.getOpenid();
+        AppUser appUser = this.selectOne(new EntityWrapper<AppUser>().eq("openid", openid).ne("status", 3));
+        if(null == appUser){
+            return ResultUtil.error("无效的账号");
+        }
+        if(appUser.getStatus() == 2){
+            return ResultUtil.error("账号被冻结");
+        }
+        String token = getToken(appUser);
+        if(ToolUtil.isEmpty(token)){
+            return ResultUtil.error("获取身份凭证失败");
+        }
+        return ResultUtil.success(token);
+    }
+
+
+    /**
+     * 获取身份凭证
+     * @return
+     */
+    public String getToken(AppUser appUser){
+        //封装请求账号密码为shiro可验证的token
+        String phone = appUser.getPhone();
+        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(phone, phone.toCharArray());
+
+        String credentials = ShiroKit.md5(phone, salt);
+        ByteSource credentialsSalt = new Md5Hash(salt);
+        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
+                new ShiroUser(), credentials, credentialsSalt, "");
+
+        //校验用户账号密码
+        HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher();
+        md5CredentialsMatcher.setHashAlgorithmName(ShiroKit.hashAlgorithmName);
+        md5CredentialsMatcher.setHashIterations(ShiroKit.hashIterations);
+        boolean passwordTrueFlag = md5CredentialsMatcher.doCredentialsMatch(
+                usernamePasswordToken, simpleAuthenticationInfo);
+
+        if (passwordTrueFlag) {
+            String token = JwtTokenUtil.generateToken(phone);
+            String key = token;
+            if(token.length() > 16){
+                key = token.substring(token.length() - 16);
+            }
+            redisUtil.setStrValue(key, appUser.getId().toString(), 7 * 24 * 60 * 60);
+            return token;
+        }
+        return null;
+    }
+
+
+    /**
+     * 微信授权注册登录
+     * @param signInToRegister
+     * @return
+     * @throws Exception
+     */
+    @Override
+    public ResultUtil signInToRegister(SignInToRegister signInToRegister) throws Exception {
+        if(ToolUtil.isEmpty(signInToRegister.getJscode())){
+            return ResultUtil.paranErr("jscode");
+        }
+        if(ToolUtil.isEmpty(signInToRegister.getEncryptedDataPhone())){
+            return ResultUtil.paranErr("encryptedDataPhone");
+        }
+        if(ToolUtil.isEmpty(signInToRegister.getIvPhone())){
+            return ResultUtil.paranErr("ivPhone");
+        }
+        Code2Session code2Session = weChatUtil.code2Session(signInToRegister.getJscode());
+        if(code2Session.getErrcode() != 0){
+            return ResultUtil.error(code2Session.getErrmsg());
+        }
+        String openid = code2Session.getOpenid();
+        String session_key = code2Session.getSession_key();
+        String decrypt = WXCore.decrypt(signInToRegister.getEncryptedDataPhone(), session_key, signInToRegister.getIvPhone());
+        if(ToolUtil.isEmpty(decrypt)){
+            return ResultUtil.error("获取手机号失败");
+        }
+        JSONObject phone = JSON.parseObject(decrypt);
+        String purePhoneNumber = phone.getString("purePhoneNumber");
+        AppUser appUser = this.selectOne(new EntityWrapper<AppUser>().eq("phone", purePhoneNumber).ne("status", 3));
+        if(null == appUser){
+            appUser = new AppUser();
+            appUser.setNickname("亲爱的用户");
+            appUser.setPhone(purePhoneNumber);
+            appUser.setOpenid(openid);
+            appUser.setUnionid(code2Session.getUnionid());
+            appUser.setAccountBalance(0D);
+            appUser.setStatus(1);
+            appUser.setCreateTime(new Date());
+            appUser.setIsException(1);
+            appUser.setInviterId(signInToRegister.getInviterId());
+            appUser.setInviterType(signInToRegister.getInviterType());
+            this.insert(appUser);
+        }
+        if(appUser.getStatus() == 2){
+            return ResultUtil.error("账号被冻结");
+        }
+        String token = getToken(appUser);
+        if(ToolUtil.isEmpty(token)){
+            return ResultUtil.error("获取身份凭证失败");
+        }
+        return ResultUtil.success(token);
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java
new file mode 100644
index 0000000..91834bf
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java
@@ -0,0 +1,16 @@
+package com.supersavedriving.user.modular.system.service.impl;
+
+import com.baomidou.mybatisplus.service.impl.ServiceImpl;
+import com.supersavedriving.user.modular.system.dao.HtmlMapper;
+import com.supersavedriving.user.modular.system.model.Html;
+import com.supersavedriving.user.modular.system.service.IHtmlService;
+import org.springframework.stereotype.Service;
+
+/**
+ * TODO 协议
+ * @author 39373
+ * @date 2023/2/26 17:31
+ */
+@Service
+public class HtmlServiceImpl extends ServiceImpl<HtmlMapper, Html> implements IHtmlService {
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/HttpClientUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/HttpClientUtil.java
deleted file mode 100644
index 4d19840..0000000
--- a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/HttpClientUtil.java
+++ /dev/null
@@ -1,266 +0,0 @@
-package com.supersavedriving.user.modular.system.util;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.util.EntityUtils;
-import org.springframework.stereotype.Component;
-
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-
-/**
- * http工具类
- */
-@Component
-public class HttpClientUtil {
-
-    private CloseableHttpClient httpClient;
-
-    private CloseableHttpResponse httpResponse;
-
-    private RequestConfig requestConfig;
-
-
-    /**
-     * 创建一个httpClient对象
-     */
-    private void getHttpCline(){
-        //1.创建连接池管理器
-        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(60000,
-                TimeUnit.MILLISECONDS);
-        connectionManager.setMaxTotal(1000);
-        connectionManager.setDefaultMaxPerRoute(50);
-
-        //2.创建httpclient对象
-        this.httpClient = HttpClients.custom()
-                .setConnectionManager(connectionManager)
-                .disableAutomaticRetries()
-                .build();
-    }
-
-    private RequestConfig getRequestConfig(){
-        return RequestConfig.custom()
-                .setConnectTimeout(60000)
-                .setSocketTimeout(60000)
-                .build();
-    }
-
-
-
-    /**
-     * 创建一个POST请求实例
-     * @param url       请求地址
-     * @param params    请求参数
-     */
-    private void setPostHttpRequset(String url, Map<String, Object> params, Map<String, String> header, String contentType){
-        HttpPost httpPost = new HttpPost(url);
-        httpPost.setConfig(this.getRequestConfig());
-        if(null != header){
-            for(String key : header.keySet()){
-                httpPost.setHeader(key, header.get(key));
-            }
-        }
-        List<NameValuePair> list = new ArrayList<>();
-        if(null != params){
-            Set<String> keys = params.keySet();
-            for(String key : keys){
-                list.add(new BasicNameValuePair(key, params.get(key).toString()));
-            }
-        }
-        try {
-            switch (contentType){
-                case "form":
-                    httpPost.setEntity(new UrlEncodedFormEntity(list, "UTF-8"));
-                    break;
-                case "json":
-                    ObjectMapper objectMapper = new ObjectMapper();
-                    String s =objectMapper.writeValueAsString(params);
-                    System.err.println(s);
-                    httpPost.setEntity(new StringEntity(s, Charset.forName("UTF-8")));
-                    break;
-            }
-            this.getHttpCline();
-            if(null == this.httpClient){
-                this.getHttpCline();
-            }
-            httpResponse = this.httpClient.execute(httpPost);
-        } catch (IOException e) {
-            e.printStackTrace();
-            this.close();
-        }
-    }
-
-
-    /**
-     * 获取get请求实例
-     * @param url       请求地址
-     * @param params    请求参数
-     */
-    private void setGetHttpRequset(String url, Map<String, Object> params, Map<String, String> header){
-        StringBuffer sb = new StringBuffer();
-        String p = "";
-        if(null != params){
-            Set<String> keys = params.keySet();
-            for(String key : keys){
-                sb.append(key + "=" + params.get(key) + "&");
-            }
-            p = "?" + sb.substring(0, sb.length() - 1);
-        }
-        HttpGet httpGet = new HttpGet(url + p);
-        if(null != header){
-            for(String key : header.keySet()){
-                httpGet.setHeader(key, header.get(key));
-            }
-        }
-        this.getHttpCline();
-        if(null == this.httpClient){
-            this.getHttpCline();
-        }
-        try {
-            httpResponse = this.httpClient.execute(httpGet);
-        } catch (IOException e) {
-            e.printStackTrace();
-            this.close();
-        }
-    }
-
-
-    /**
-     * 发送http请求
-     * @param mothed        "GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS"
-     * @param url           请求地址
-     * @param params        请求参数
-     * @param header        请求头
-     * @param contentType   参数请求方式form/json
-     * @return
-     */
-    public String pushHttpRequset(String mothed, String url, Map<String, Object> params, Map<String, String> header, String contentType){
-        String content = null;
-        switch (mothed){
-            case "GET":
-                this.setGetHttpRequset(url, params, header);
-                break;
-            case "POST":
-                this.setPostHttpRequset(url, params, header, contentType);
-                break;
-        }
-        if(httpResponse.getStatusLine().getStatusCode() == 200){
-            try {
-                content = EntityUtils.toString(httpResponse.getEntity());
-                this.close();
-                return content;
-            } catch (IOException e) {
-                e.printStackTrace();
-                this.close();
-            }
-        }
-        if(httpResponse.getStatusLine().getStatusCode() == 201){
-            content = "{\"status\":201}";
-            this.close();
-            return content;
-        }else{
-            try {
-                System.err.println("返回状态码:" + httpResponse.getStatusLine() + "。");
-                content = EntityUtils.toString(httpResponse.getEntity());
-                this.close();
-                return content;
-            } catch (IOException e) {
-                e.printStackTrace();
-                this.close();
-            }
-        }
-        this.close();
-        return content;
-    }
-
-
-    /**
-     * 发送XML请求
-     * @param url       请求地址
-     * @param xml       XML数据
-     * @param header    自定义请求头
-     * @return
-     */
-    public String pushHttpRequsetXml(String url, String xml, Map<String, String> header){
-        HttpPost httpPost = new HttpPost(url);
-        for(String key : header.keySet()){
-            httpPost.setHeader(key, header.get(key));
-        }
-        httpPost.setHeader("Content-Type", "application/xml");
-        try {
-            httpPost.setEntity(new StringEntity(xml, "UTF-8"));
-            this.getHttpCline();
-            if(null == this.httpClient){
-                this.getHttpCline();
-            }
-            httpResponse = this.httpClient.execute(httpPost);
-            String content = null;
-            if(httpResponse.getStatusLine().getStatusCode() == 200){
-                try {
-                    content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
-                    this.close();
-                    return content;
-                } catch (IOException e) {
-                    e.printStackTrace();
-                    this.close();
-                }
-            }else{
-                try {
-                    content = "返回状态码:" + httpResponse.getStatusLine() + "。" + EntityUtils.toString(httpResponse.getEntity());
-                    this.close();
-                    return content;
-                } catch (IOException e) {
-                    e.printStackTrace();
-                    this.close();
-                }
-            }
-            this.close();
-            return content;
-        } catch (IOException e) {
-            e.printStackTrace();
-            this.close();
-        }
-        return null;
-    }
-
-
-
-    /**
-     * 关闭资源
-     */
-    private void close(){
-        try {
-            if(null != httpClient){
-                httpClient.close();
-            }
-            if(null != httpResponse){
-                httpResponse.close();
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }finally {
-            try {
-                if(null != httpClient){
-                    httpClient.close();
-                }
-                if(null != httpResponse){
-                    httpResponse.close();
-                }
-            }catch (Exception e){
-                e.printStackTrace();
-            }
-        }
-    }
-}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java
index 7594824..218ef97 100644
--- a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java
@@ -1,16 +1,15 @@
 package com.supersavedriving.user.modular.system.util;
 
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
 import com.supersavedriving.user.core.util.ToolUtil;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
 import org.springframework.stereotype.Component;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.client.RestTemplate;
+import org.springframework.util.StringUtils;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.Pipeline;
+
+import java.io.IOException;
+import java.util.*;
 
 
 /**
@@ -20,7 +19,9 @@
 public class RedisUtil {
 
     @Autowired
-    private RestTemplate internalRestTemplate;
+    private JedisPool jedisPool;
+
+    private Timer timer;
 
 
     /**
@@ -29,23 +30,11 @@
      * @param value
      */
     public void setStrValue(String key, String value){
-        if(ToolUtil.isNotEmpty(key)){
-            //发送验证码短信
-            HttpHeaders headers = new HttpHeaders();
-            // 以表单的方式提交
-            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
-            //将请求头部和参数合成一个请求
-            MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
-            params.add("key", key);
-            params.add("value", value);
-            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
-            String s = internalRestTemplate.postForObject("http://zuul-gateway/redis/setValue_", requestEntity, String.class);
-            JSONObject jsonObject = JSON.parseObject(s, JSONObject.class);
-            if(jsonObject.getIntValue("code") != 200){
-                System.err.println("调用redis出错了");
-            }
+        if(ToolUtil.isNotEmpty(key) && ToolUtil.isNotEmpty(value)){
+            Jedis resource = jedisPool.getResource();
+            String set = resource.set(key, value);
+            closeJedis(resource);
         }
-
     }
 
 
@@ -56,22 +45,10 @@
      * @param time 秒
      */
     public void setStrValue(String key, String value, int time){
-        if(ToolUtil.isNotEmpty(key)){
-            //发送验证码短信
-            HttpHeaders headers = new HttpHeaders();
-            // 以表单的方式提交
-            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
-            //将请求头部和参数合成一个请求
-            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
-            params.add("key", key);
-            params.add("value", value);
-            params.add("time", String.valueOf(time));
-            HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
-            String s = internalRestTemplate.postForObject("http://zuul-gateway/redis/setValue", requestEntity, String.class);
-            JSONObject jsonObject = JSON.parseObject(s, JSONObject.class);
-            if(jsonObject.getIntValue("code") != 200){
-                System.err.println("调用redis出错了");
-            }
+        if(ToolUtil.isNotEmpty(key) && ToolUtil.isNotEmpty(value)){
+            Jedis resource = jedisPool.getResource();
+            String setex = resource.setex(key, time, value);
+            closeJedis(resource);
         }
     }
 
@@ -83,45 +60,180 @@
      */
     public String getValue(String key){
         if(ToolUtil.isNotEmpty(key)){
-            HttpHeaders headers = new HttpHeaders();
-            // 以表单的方式提交
-            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
-            //将请求头部和参数合成一个请求
-            MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
-            params.add("key", key);
-            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
-            String s = internalRestTemplate.postForObject("http://zuul-gateway/redis/getValue",requestEntity , String.class);
-            JSONObject jsonObject = JSON.parseObject(s, JSONObject.class);
-            if(jsonObject.getIntValue("code") != 200){
-                System.err.println("调用redis出错了");
-            }
-            return jsonObject.getString("data");
+            Jedis resource = jedisPool.getResource();
+            String data = resource.get(key);
+            closeJedis(resource);
+            return data;
         }
         return null;
     }
 
+
+    /**
+     * 批量获取
+     * @param kes
+     * @return
+     */
+    public List<Object> getValues(List<String> kes){
+        if(null != kes){
+            Jedis resource = jedisPool.getResource();
+            Pipeline pipelined = resource.pipelined();
+            for(String key : kes){
+                pipelined.get(key);
+            }
+            List<Object> list = pipelined.syncAndReturnAll();
+
+            closeJedis(resource);
+            pipelined.clear();
+            try {
+                pipelined.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            List<Object> data = new ArrayList<>();
+            for(Object o : list){
+                if(null != o){
+                    data.add(o);
+                }
+            }
+            return data;
+        }
+        return null;
+    }
 
 
     /**
      * 删除key
      * @param key
      */
-    public String remove(String key){
+    public void remove(String key){
         if(ToolUtil.isNotEmpty(key)){
-            HttpHeaders headers = new HttpHeaders();
-            // 以表单的方式提交
-            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
-            //将请求头部和参数合成一个请求
-            MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
-            params.add("key", key);
-            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
-            String s = internalRestTemplate.postForObject("http://zuul-gateway/redis/remove",requestEntity , String.class);
-            JSONObject jsonObject = JSON.parseObject(s, JSONObject.class);
-            if(jsonObject.getIntValue("code") != 200){
-                System.err.println("调用redis出错了");
-            }
-            return jsonObject.getString("data");
+            Jedis resource = jedisPool.getResource();
+            Long del = resource.del(key);
+            closeJedis(resource);
         }
-        return null;
+    }
+
+
+    /**
+     * 向集合key添加数据
+     * @param key
+     * @param members
+     */
+    public void addSetValue(String key, String...members){
+        if(ToolUtil.isNotEmpty(key) && ToolUtil.isNotEmpty(members)){
+            Jedis resource = jedisPool.getResource();
+            Long sadd = resource.sadd(key, members);
+            resource.close();
+        }
+    }
+
+
+    /**
+     * 返回Set集合数据
+     * @param key
+     * @return
+     */
+    public Set<String> getSetAllValue(String key){
+        Set<String> smembers = new HashSet<>();
+        if(ToolUtil.isNotEmpty(key)){
+            Jedis resource = jedisPool.getResource();
+            smembers = resource.smembers(key);
+            resource.close();
+        }
+        return smembers;
+    }
+
+
+    /**
+     * 删除Set集合中的值
+     * @param key
+     * @param members
+     */
+    public void delSetValue(String key, String...members){
+        if(ToolUtil.isNotEmpty(key) && ToolUtil.isNotEmpty(members)){
+            Jedis resource = jedisPool.getResource();
+            Long sadd = resource.srem(key, members);
+            resource.close();
+        }
+    }
+
+
+    /**
+     * 删除资源
+     * @param jedis
+     */
+    public void closeJedis(Jedis jedis){
+        if(null != jedis){
+            jedis.close();
+        }
+    }
+
+
+    /**
+     * redis加锁
+     * @param key
+     * @param value
+     * @param time
+     * @return
+     */
+    public boolean lock(String key, String value, int time){
+        if(!StringUtils.isEmpty(key)){
+            key += "_lock";
+            Jedis resource = jedisPool.getResource();
+            String set = resource.set(key, value, "nx", "ex", time);
+            if("OK".equals(set)){
+                String finalKey = key;
+                timer = new Timer();
+                timer.schedule(new TimerTask() {
+                    @Override
+                    public void run() {
+                        System.err.println("定时任务启动");
+                        Jedis resource = jedisPool.getResource();
+                        resource.setex(finalKey, time, value);
+                        resource.close();
+                    }
+                }, 1000, 500);
+            }
+            resource.close();
+            return "OK".equals(set) ? true : false;
+        }
+        return false;
+    }
+
+    /**
+     * 获取redis锁
+     * @param time
+     * @return
+     */
+    public boolean lock(int time){
+        String uuid = UUID.randomUUID().toString();
+        return lock("redis", uuid, time);
+    }
+
+
+    /**
+     * redis释放锁
+     * @param key
+     * @return
+     */
+    public boolean unlock(String key){
+        if(!StringUtils.isEmpty(key)){
+            key += "_lock";
+            Jedis resource = jedisPool.getResource();
+            timer.cancel();//取消定时任务
+            Long del = resource.del(key);
+            resource.close();
+            return del != 0 ? true : false;
+        }
+        return false;
+    }
+
+    /**
+     * 删除锁
+     * @return
+     */
+    public boolean unlock(){
+        return unlock("redis");
     }
 }
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java
index e78e01f..8134c57 100644
--- a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java
@@ -1,6 +1,5 @@
 package com.supersavedriving.user.modular.system.util;
 
-import com.alibaba.fastjson.JSONObject;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -10,23 +9,23 @@
 @ApiModel(value = "统一返回结果集")
 public class ResultUtil<T> {
 
-    public static final Integer SUCCESS = 200;
+    public static final Integer SUCCESS = 10000;
 
-    public static final Integer PARAM_ERROR = 300;
+    public static final Integer PARAM_ERROR = 10010;
 
-    public static final Integer RUNTIME_ERROR = 400;
+    public static final Integer SYSTEM_INFO = 10020;
 
-    public static final Integer ERROR = 500;
+    public static final Integer TOKEN_ERROR = 10030;
 
-    public static final Integer TOKEN_ERROR = 600;
+    public static final Integer SIGN_ERROR = 10040;
 
-    public static final Integer SIGN_ERROR = 700;
+    public static final Integer RUNTIME_ERROR = 10050;
 
-    public static final String Token = "TOKEN_INVALID";
+    public static final String Token = "token无效";
 
-    public static final String SIGN = "SIGN_INVALID";
+    public static final String SIGN = "签名无效";
 
-    @ApiModelProperty(name = "code", value = "业务状态码 200:成功,300:参数错误,400:运行异常,500:其他异常, 600:token无效,需重新登录,700:签名无效")
+    @ApiModelProperty(name = "code", value = "业务状态码 10000:成功,10010:参数错误,10020:系统提示, 10030:身份校验异常,10040:签名不通过,10050:系统运行异常")
     private Integer code;//备用状态码
 
     @ApiModelProperty(name = "msg", value = "返回结果说明")
@@ -75,15 +74,15 @@
      * @return
      */
     public static ResultUtil error(String mag){
-        return ResultUtil.getResult(ResultUtil.ERROR, mag, new JSONObject());
+        return ResultUtil.getResult(ResultUtil.SYSTEM_INFO, mag, new Object());
     }
 
     /**
      * 错误信息
      * @return
      */
-    public static <T> ResultUtil<T> error(String mag, T obj){
-        return ResultUtil.getResult(ResultUtil.ERROR, mag, obj);
+    public static <T> ResultUtil <T> error(String mag, T obj){
+        return ResultUtil.getResult(ResultUtil.SYSTEM_INFO, mag, obj);
     }
 
     /**
@@ -91,7 +90,7 @@
      * @return
      */
     public static ResultUtil tokenErr(){
-        return ResultUtil.getResult(ResultUtil.TOKEN_ERROR, ResultUtil.Token, new JSONObject());
+        return ResultUtil.getResult(ResultUtil.TOKEN_ERROR, ResultUtil.Token, new Object());
     }
 
     /**
@@ -99,7 +98,15 @@
      * @return
      */
     public static ResultUtil tokenErr(String msg){
-        return ResultUtil.getResult(ResultUtil.TOKEN_ERROR, msg, new JSONObject());
+        return ResultUtil.getResult(ResultUtil.TOKEN_ERROR, msg, new Object());
+    }
+
+    /**
+     * 参数异常
+     * @return
+     */
+    public static  ResultUtil paranErr(String...ages){
+        return ResultUtil.getResult(ResultUtil.PARAM_ERROR, "【" + ages + "】参数异常", new Object());
     }
 
     /**
@@ -107,15 +114,7 @@
      * @return
      */
     public static ResultUtil paranErr(){
-        return ResultUtil.getResult(ResultUtil.PARAM_ERROR, "PARAM_ERROR", new JSONObject());
-    }
-
-    /**
-     * 参数异常
-     * @return
-     */
-    public static <T> ResultUtil<T> paranErr(T data){
-        return ResultUtil.getResult(ResultUtil.PARAM_ERROR, "SYSTEM_RUN_ERROR", data);
+        return ResultUtil.getResult(ResultUtil.PARAM_ERROR, "参数异常", new Object());
     }
 
     /**
@@ -123,7 +122,7 @@
      * @return
      */
     public static ResultUtil runErr(){
-        return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "SYSTEM_RUN_ERROR", new JSONObject());
+        return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "系统运行异常", new Object());
     }
 
 
@@ -131,15 +130,15 @@
      * 运行异常
      * @return
      */
-    public static <T> ResultUtil<T> runErr(T data){
-        return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "SYSTEM_RUN_ERROR", data);
+    public static <T>ResultUtil<T> runErr(T data){
+        return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "系统运行异常", data);
     }
 
     /**
      * 运行异常
      * @return
      */
-    public static <T> ResultUtil<T> runErr(T data, String msg){
+    public static <T>ResultUtil<T> runErr(T data, String msg){
         return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, msg, data);
     }
 
@@ -150,7 +149,7 @@
      * @return
      */
     public static ResultUtil success(){
-        return ResultUtil.getResult(ResultUtil.SUCCESS, "SUCCESS", new JSONObject());
+        return ResultUtil.getResult(ResultUtil.SUCCESS, "成功", new Object());
     }
 
 
@@ -161,7 +160,7 @@
      * @return
      */
     public static <T> ResultUtil<T> success(T data){
-        return ResultUtil.getResult(ResultUtil.SUCCESS, "SUCCESS", data);
+        return ResultUtil.getResult(ResultUtil.SUCCESS, "成功", data);
     }
 
     /**
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java
new file mode 100644
index 0000000..35921fe
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java
@@ -0,0 +1,269 @@
+package com.supersavedriving.user.modular.system.util.httpClinet;
+
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.net.ssl.SSLContext;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.security.KeyStore;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * http工具类
+ */
+
+public class HttpClientUtil {
+
+    private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
+
+    private static PoolingHttpClientConnectionManager connectionManager;
+
+
+    {
+        //1.创建连接池管理器
+        connectionManager = new PoolingHttpClientConnectionManager(60000,
+                TimeUnit.MILLISECONDS);
+        connectionManager.setMaxTotal(1000);
+        connectionManager.setDefaultMaxPerRoute(50);
+    }
+
+    /**
+     * 创建一个httpClient对象
+     */
+    private static CloseableHttpClient getHttpCline(){
+        return  HttpClients.custom()
+                .setConnectionManager(connectionManager)
+                .disableAutomaticRetries()
+                .build();
+    }
+
+    private static RequestConfig getRequestConfig(){
+        RequestConfig.Builder builder = RequestConfig.custom();
+        builder.setSocketTimeout(60000)//3.1设置客户端等待服务端返回数据的超时时间
+                .setConnectTimeout(30000)//3.2设置客户端发起TCP连接请求的超时时间
+                .setExpectContinueEnabled(true)
+                .setConnectionRequestTimeout(30000);//3.3设置客户端从连接池获取链接的超时时间
+        return builder.build();
+    }
+
+
+
+    /**
+     * 创建一个POST请求实例
+     * @param url       请求地址
+     * @param params    请求参数
+     */
+    private static CloseableHttpResponse setPostHttpRequset(String url, Map<String, Object> params, Map<String, String> header, String contentType) throws Exception{
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setConfig(getRequestConfig());
+        if(null != header){
+            for(String key : header.keySet()){
+                httpPost.setHeader(key, header.get(key));
+            }
+        }
+        List<NameValuePair> list = new ArrayList<>();
+        if(null != params){
+            Set<String> keys = params.keySet();
+            for(String key : keys){
+                list.add(new BasicNameValuePair(key, null == params.get(key) ? null : params.get(key).toString()));
+            }
+        }
+        switch (contentType){
+            case "form":
+                httpPost.setEntity(new UrlEncodedFormEntity(list, "UTF-8"));
+                break;
+            case "json":
+                ObjectMapper objectMapper = new ObjectMapper();
+                String s =objectMapper.writeValueAsString(params);
+                httpPost.setEntity(new StringEntity(s, ContentType.create(ContentType.APPLICATION_JSON.getMimeType(), Charset.forName("UTF-8"))));
+                break;
+        }
+        return getHttpCline().execute(httpPost);
+    }
+
+
+    /**
+     * 获取get请求实例
+     * @param url       请求地址
+     * @param params    请求参数
+     */
+    private static CloseableHttpResponse setGetHttpRequset(String url, Map<String, Object> params, Map<String, String> header) throws Exception{
+        StringBuffer sb = new StringBuffer();
+        String p = "";
+        if(null != params){
+            Set<String> keys = params.keySet();
+            for(String key : keys){
+                sb.append(key + "=" + params.get(key) + "&");
+            }
+            p = "?" + sb.substring(0, sb.length() - 1);
+        }
+        HttpGet httpGet = new HttpGet(url + p);
+        httpGet.setConfig(getRequestConfig());
+        if(null != header){
+            for(String key : header.keySet()){
+                httpGet.setHeader(key, header.get(key));
+            }
+        }
+        return getHttpCline().execute(httpGet);
+    }
+
+
+    /**
+     * 发送http请求
+     * @param mothed        "GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS"
+     * @param url           请求地址
+     * @param params        请求参数
+     * @param header        请求头
+     * @param contentType   参数请求方式form/json
+     * @return
+     */
+    public static HttpResult pushHttpRequset(String mothed, String url, Map<String, Object> params, Map<String, String> header, String contentType) throws Exception{
+        String randome = UUID.randomUUID().toString();
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
+        logger.info(sdf.format(new Date()) + "----(" + randome + ")请求参数:" + JSON.toJSONString(params));
+        CloseableHttpResponse httpResponse = null;
+        switch (mothed){
+            case "GET":
+                httpResponse = setGetHttpRequset(url, params, header);
+                break;
+            case "POST":
+                httpResponse = setPostHttpRequset(url, params, header, contentType);
+                break;
+        }
+        int statusCode = httpResponse.getStatusLine().getStatusCode();
+        String content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
+        logger.info(sdf.format(new Date()) + "----(" + randome + ")返回结果:" + content);
+        HttpResult httpResult = HttpResult.getHttpResult(statusCode, content);
+        close(httpResponse);
+        return httpResult;
+    }
+
+
+    /**
+     * 发送XML请求
+     * @param url       请求地址
+     * @param xml       XML数据
+     * @param header    自定义请求头
+     * @return
+     */
+    public static HttpResult pushHttpRequsetXml(String url, String xml, Map<String, String> header) throws Exception{
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setConfig(getRequestConfig());
+        for(String key : header.keySet()){
+            httpPost.setHeader(key, header.get(key));
+        }
+        httpPost.setHeader("Content-Type", "application/xml");
+        httpPost.setEntity(new StringEntity(xml, "UTF-8"));
+        CloseableHttpResponse httpResponse = getHttpCline().execute(httpPost);
+        int statusCode = httpResponse.getStatusLine().getStatusCode();
+        String content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
+        HttpResult httpResult = HttpResult.getHttpResult(statusCode, content);
+        close(httpResponse);
+        return httpResult;
+    }
+
+
+
+    /**
+     * 请求https发送XML请求
+     * @param url           接口路径
+     * @param xml           内容
+     * @param header        请求头
+     * @param certPassword      证书密码
+     * @param certPath      证书路径
+     * @param certType      证书类型
+     * @return
+     * @throws Exception
+     */
+    public static String pushHttpsRequsetXml(String url, String xml, Map<String, String> header, String certPassword, String certPath, String certType) throws Exception{
+        HttpPost httpPost = new HttpPost(url);
+        for(String key : header.keySet()){
+            httpPost.setHeader(key, header.get(key));
+        }
+        httpPost.setHeader("Content-Type", "application/xml");
+        httpPost.setEntity(new StringEntity(xml, "UTF-8"));
+        CloseableHttpClient httpCline = initCert(certPassword, certPath, certType);
+        CloseableHttpResponse httpResponse = httpCline.execute(httpPost);
+        String content = null;
+        if(httpResponse.getStatusLine().getStatusCode() == 200){
+            content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
+        }else{
+            content = "返回状态码:" + httpResponse.getStatusLine() + "。" + EntityUtils.toString(httpResponse.getEntity());
+        }
+        close(httpResponse);
+        httpCline.close();
+        return content;
+    }
+
+
+    /**
+     * 初始化https对象(带证书)
+     * @param key       证书密码
+     * @param certPath  证书路径
+     * @param certType  证书类型
+     * @throws Exception
+     */
+    private static CloseableHttpClient initCert(String key, String certPath, String certType) throws Exception {
+        KeyStore keyStore = KeyStore.getInstance(certType);
+        InputStream inputStream = new FileInputStream(new File(certPath));
+        try {
+            keyStore.load(inputStream, key.toCharArray());
+        } finally {
+            inputStream.close();
+        }
+        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
+        SSLConnectionSocketFactory sslsf =
+                new SSLConnectionSocketFactory(sslcontext, new String[] {"TLSv1"}, null,
+                        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
+        return HttpClients.custom().setSSLSocketFactory(sslsf).build();
+    }
+
+
+
+    /**
+     * 关闭资源
+     */
+    private static void close(CloseableHttpResponse httpResponse){
+        try {
+            if(null != httpResponse){
+                EntityUtils.consume(httpResponse.getEntity());//此处高能,通过源码分析,由EntityUtils是否回收HttpEntity
+                httpResponse.close();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }finally {
+            try {
+                if(null != httpResponse){
+                    httpResponse.close();
+                }
+            }catch (Exception e){
+                e.printStackTrace();
+            }
+        }
+    }
+
+
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java
new file mode 100644
index 0000000..6ff434a
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java
@@ -0,0 +1,31 @@
+package com.supersavedriving.user.modular.system.util.httpClinet;
+
+import lombok.Data;
+
+/**
+ * http请求返回封装
+ */
+@Data
+public class HttpResult {
+    /**
+     * 返回状态码
+     */
+    private Integer code;
+    /**
+     * 返回结果
+     */
+    private String data;
+
+    /**
+     * 返回封装结果
+     * @param code
+     * @param data
+     * @return
+     */
+    public static HttpResult getHttpResult(Integer code, String data){
+        HttpResult httpResult = new HttpResult();
+        httpResult.setCode(code);
+        httpResult.setData(data);
+        return httpResult;
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java
new file mode 100644
index 0000000..5ec0eb8
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java
@@ -0,0 +1,72 @@
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.*;
+
+/**
+* AES加密
+* @author pzb
+* @Date 2021/12/3 15:43
+*/
+public class AES {
+
+    public static boolean initialized = false;
+
+    /**
+     * AES解密
+     *
+     * @param content
+     *            密文
+     * @return
+     * @throws InvalidAlgorithmParameterException
+     * @throws NoSuchProviderException
+     */
+    public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
+        initialize();
+        try {
+            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
+            Key sKeySpec = new SecretKeySpec(keyByte, "AES");
+            cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
+            byte[] result = cipher.doFinal(content);
+            return result;
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        } catch (NoSuchPaddingException e) {
+            e.printStackTrace();
+        } catch (InvalidKeyException e) {
+            e.printStackTrace();
+        } catch (IllegalBlockSizeException e) {
+            e.printStackTrace();
+        } catch (BadPaddingException e) {
+            e.printStackTrace();
+        } catch (NoSuchProviderException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static void initialize() {
+        if (initialized)
+            return;
+        Security.addProvider(new BouncyCastleProvider());
+        initialized = true;
+    }
+
+    // 生成iv
+    public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+        params.init(new IvParameterSpec(iv));
+        return params;
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java
new file mode 100644
index 0000000..178e466
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java
@@ -0,0 +1,59 @@
+package com.supersavedriving.user.modular.system.util.weChat;
+
+@SuppressWarnings("serial")
+public class AesException extends Exception {
+
+	public final static int OK = 0;
+	public final static int ValidateSignatureError = -40001;
+	public final static int ParseXmlError = -40002;
+	public final static int ComputeSignatureError = -40003;
+	public final static int IllegalAesKey = -40004;
+	public final static int ValidateAppidError = -40005;
+	public final static int EncryptAESError = -40006;
+	public final static int DecryptAESError = -40007;
+	public final static int IllegalBuffer = -40008;
+	//public final static int EncodeBase64Error = -40009;
+	//public final static int DecodeBase64Error = -40010;
+	//public final static int GenReturnXmlError = -40011;
+
+	private int code;
+
+	private static String getMessage(int code) {
+		switch (code) {
+		case ValidateSignatureError:
+			return "签名验证错误";
+		case ParseXmlError:
+			return "xml解析失败";
+		case ComputeSignatureError:
+			return "sha加密生成签名失败";
+		case IllegalAesKey:
+			return "SymmetricKey非法";
+		case ValidateAppidError:
+			return "appid校验失败";
+		case EncryptAESError:
+			return "aes加密失败";
+		case DecryptAESError:
+			return "aes解密失败";
+		case IllegalBuffer:
+			return "解密后得到的buffer非法";
+//		case EncodeBase64Error:
+//			return "base64加密错误";
+//		case DecodeBase64Error:
+//			return "base64解密错误";
+//		case GenReturnXmlError:
+//			return "xml生成失败";
+		default:
+			return null; // cannot be
+		}
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	AesException(int code) {
+		super(getMessage(code));
+		this.code = code;
+	}
+
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java
new file mode 100644
index 0000000..e35ba9d
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java
@@ -0,0 +1,26 @@
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import java.util.ArrayList;
+
+class ByteGroup {
+	ArrayList<Byte> byteContainer = new ArrayList<Byte>();
+
+	public byte[] toBytes() {
+		byte[] bytes = new byte[byteContainer.size()];
+		for (int i = 0; i < byteContainer.size(); i++) {
+			bytes[i] = byteContainer.get(i);
+		}
+		return bytes;
+	}
+
+	public ByteGroup addBytes(byte[] bytes) {
+		for (byte b : bytes) {
+			byteContainer.add(b);
+		}
+		return this;
+	}
+
+	public int size() {
+		return byteContainer.size();
+	}
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java
new file mode 100644
index 0000000..0513b21
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java
@@ -0,0 +1,67 @@
+/**
+ * 对公众平台发送给公众账号的消息加解密示例代码.
+ * 
+ * @copyright Copyright (c) 1998-2014 Tencent Inc.
+ */
+
+// ------------------------------------------------------------------------
+
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+/**
+ * 提供基于PKCS7算法的加解密接口.
+ */
+class PKCS7Encoder {
+	static Charset CHARSET = Charset.forName("utf-8");
+	static int BLOCK_SIZE = 32;
+
+	/**
+	 * 获得对明文进行补位填充的字节.
+	 * 
+	 * @param count 需要进行填充补位操作的明文字节个数
+	 * @return 补齐用的字节数组
+	 */
+	static byte[] encode(int count) {
+		// 计算需要填充的位数
+		int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
+		if (amountToPad == 0) {
+			amountToPad = BLOCK_SIZE;
+		}
+		// 获得补位所用的字符
+		char padChr = chr(amountToPad);
+		String tmp = new String();
+		for (int index = 0; index < amountToPad; index++) {
+			tmp += padChr;
+		}
+		return tmp.getBytes(CHARSET);
+	}
+
+	/**
+	 * 删除解密后明文的补位字符
+	 * 
+	 * @param decrypted 解密后的明文
+	 * @return 删除补位字符后的明文
+	 */
+	static byte[] decode(byte[] decrypted) {
+		int pad = (int) decrypted[decrypted.length - 1];
+		if (pad < 1 || pad > 32) {
+			pad = 0;
+		}
+		return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
+	}
+
+	/**
+	 * 将数字转化成ASCII码对应的字符,用于对明文进行补码
+	 * 
+	 * @param a 需要转化的数字
+	 * @return 转化得到的字符
+	 */
+	static char chr(int a) {
+		byte target = (byte) (a & 0xFF);
+		return (char) target;
+	}
+
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java
new file mode 100644
index 0000000..5099cfe
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java
@@ -0,0 +1,61 @@
+/**
+ * 对公众平台发送给公众账号的消息加解密示例代码.
+ * 
+ * @copyright Copyright (c) 1998-2014 Tencent Inc.
+ */
+
+// ------------------------------------------------------------------------
+
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+/**
+ * SHA1 class
+ *
+ * 计算公众平台的消息签名接口.
+ */
+public class SHA1 {
+
+	/**
+	 * 用SHA1算法生成安全签名
+	 * @param token 票据
+	 * @param timestamp 时间戳
+	 * @param nonce 随机字符串
+	 * @param encrypt 密文
+	 * @return 安全签名
+	 * @throws AesException 
+	 */
+	public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException
+			  {
+		try {
+			String[] array = new String[] { token, timestamp, nonce, encrypt };
+			StringBuffer sb = new StringBuffer();
+			// 字符串排序
+			Arrays.sort(array);
+			for (int i = 0; i < 4; i++) {
+				sb.append(array[i]);
+			}
+			String str = sb.toString();
+			// SHA1签名生成
+			MessageDigest md = MessageDigest.getInstance("SHA-1");
+			md.update(str.getBytes());
+			byte[] digest = md.digest();
+
+			StringBuffer hexstr = new StringBuffer();
+			String shaHex = "";
+			for (int i = 0; i < digest.length; i++) {
+				shaHex = Integer.toHexString(digest[i] & 0xFF);
+				if (shaHex.length() < 2) {
+					hexstr.append(0);
+				}
+				hexstr.append(shaHex);
+			}
+			return hexstr.toString();
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw new AesException(AesException.ComputeSignatureError);
+		}
+	}
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java
new file mode 100644
index 0000000..9c6d06d
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java
@@ -0,0 +1,264 @@
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.stylefeng.guns.modular.system.util.RedisUtil;
+import com.stylefeng.guns.modular.system.util.ResultUtil;
+import com.stylefeng.guns.modular.system.util.httpClinet.HttpClientUtil;
+import com.stylefeng.guns.modular.system.util.httpClinet.HttpResult;
+import com.stylefeng.guns.modular.system.util.weChat.model.Category;
+import com.stylefeng.guns.modular.system.util.weChat.model.MessageTemplate;
+import com.stylefeng.guns.modular.system.util.weChat.model.PubTemplateKeywords;
+import com.stylefeng.guns.modular.system.util.weChat.model.PubTemplatetitles;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 订阅消息
+ */
+@Component
+public class SubscribeMessageUtil {
+
+    @Value("${wx.appletsAppid}")
+    private String wxAppletsAppid;
+
+    @Autowired
+    private RedisUtil redisUtil;
+
+    @Autowired
+    private HttpClientUtil httpClientUtil;
+
+
+    /**
+     * 获取消息类目
+     * @return
+     */
+    public ResultUtil<List<Category>> getcategory(){
+        try {
+            String accessToken = redisUtil.getValue("wxAppletsAccessToken");
+            String url = "https://api.weixin.qq.com/wxaapi/newtmpl/getcategory?access_token=" + accessToken;
+            HttpResult httpResult = httpClientUtil.pushHttpRequset("GET", url, null, null, "form");
+            if(httpResult.getCode() != 200){
+                return ResultUtil.error("获取消息类目失败");
+            }
+            JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+            Integer errcode = jsonObject.getInteger("errcode");
+            if(0 != errcode){
+                return ResultUtil.error(jsonObject.getString("errmsg"));
+            }
+            JSONArray data = jsonObject.getJSONArray("data");
+            List<Category> categories = data.toJavaList(Category.class);
+            return ResultUtil.success(categories);
+        }catch (Exception e){
+            e.printStackTrace();
+            return ResultUtil.runErr();
+        }
+    }
+
+
+    /**
+     * 获取所属类目下的公共模板
+     * @param ids
+     * @return
+     */
+    public ResultUtil<List<PubTemplatetitles>> getpubtemplatetitles(String ids){
+        List<PubTemplatetitles> pubTemplatetitles = new ArrayList<>();
+        try {
+            Integer start = 0;
+            while (true){
+                String accessToken = redisUtil.getValue("wxAppletsAccessToken");
+                String url = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=" + accessToken;
+                Map<String, Object> params = new HashMap<>();
+                params.put("ids", ids);
+                params.put("start", start + "");
+                params.put("limit", 30);
+                HttpResult httpResult = httpClientUtil.pushHttpRequset("GET", url, params, null, "form");
+                if(httpResult.getCode() != 200){
+                    return ResultUtil.error("获取消息类目失败");
+                }
+                JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+                Integer errcode = jsonObject.getInteger("errcode");
+                if(0 != errcode){
+                    return ResultUtil.error(jsonObject.getString("errmsg"));
+                }
+                JSONArray data = jsonObject.getJSONArray("data");
+                List<PubTemplatetitles> pubTemplatetitles1 = data.toJavaList(PubTemplatetitles.class);
+                if(pubTemplatetitles1.size() == 0){
+                    break;
+                }
+                pubTemplatetitles.addAll(pubTemplatetitles1);
+                start += 30;
+            }
+            return ResultUtil.success(pubTemplatetitles);
+        }catch (Exception e){
+            e.printStackTrace();
+            return ResultUtil.runErr();
+        }
+    }
+
+
+    /**
+     * 获取关键词列表
+     * @param tid
+     * @return
+     */
+    public ResultUtil<List<PubTemplateKeywords>> getpubtemplatekeywords(String tid){
+        try {
+            String accessToken = redisUtil.getValue("wxAppletsAccessToken");
+            String url = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token=" + accessToken;
+            Map<String, Object> params = new HashMap<>();
+            params.put("tid", tid);
+            HttpResult httpResult = httpClientUtil.pushHttpRequset("GET", url, params, null, "form");
+            if(httpResult.getCode() != 200){
+                return ResultUtil.error("获取消息类目失败");
+            }
+            JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+            Integer errcode = jsonObject.getInteger("errcode");
+            if(0 != errcode){
+                return ResultUtil.error(jsonObject.getString("errmsg"));
+            }
+            JSONArray data = jsonObject.getJSONArray("data");
+            List<PubTemplateKeywords> pubTemplateKeywords = data.toJavaList(PubTemplateKeywords.class);
+            return ResultUtil.success(pubTemplateKeywords);
+        }catch (Exception e){
+            e.printStackTrace();
+            return ResultUtil.runErr();
+        }
+    }
+
+
+    /**
+     * 获取个人模板列表
+     * @return
+     */
+    public ResultUtil<List<MessageTemplate>> getMessageTemplateList(){
+        try {
+            String accessToken = redisUtil.getValue("wxAppletsAccessToken");
+            String url = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token=" + accessToken;
+            HttpResult httpResult = httpClientUtil.pushHttpRequset("GET", url, null, null, "form");
+            if(httpResult.getCode() != 200){
+                return ResultUtil.error("获取消息模板失败");
+            }
+            JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+            Integer errcode = jsonObject.getInteger("errcode");
+            if(0 != errcode){
+                return ResultUtil.error(jsonObject.getString("errmsg"));
+            }
+            JSONArray data = jsonObject.getJSONArray("data");
+            List<MessageTemplate> messageTemplates = data.toJavaList(MessageTemplate.class);
+            return ResultUtil.success(messageTemplates);
+        }catch (Exception e){
+            e.printStackTrace();
+            return ResultUtil.runErr();
+        }
+    }
+
+
+    /**
+     * 添加消息模板
+     * @param tid           模板标题 id
+     * @param kidList       开发者自行组合好的模板关键词列表,关键词顺序可以自由搭配(例如 [3,5,4] 或 [4,5,3]),最多支持5个,最少2个关键词组合
+     * @param sceneDesc     服务场景描述,15个字以内
+     * @return
+     */
+    public ResultUtil<String> addtemplate(String tid, String[] kidList, String sceneDesc){
+        try {
+            String accessToken = redisUtil.getValue("wxAppletsAccessToken");
+            String url = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token=" + accessToken;
+            Map<String, Object> params = new HashMap<>();
+            params.put("tid", tid);
+            params.put("kidList", kidList);
+            params.put("sceneDesc", sceneDesc);
+            HttpResult httpResult = httpClientUtil.pushHttpRequset("POST", url, params, null, "json");
+            if(httpResult.getCode() != 200){
+                return ResultUtil.error("获取消息模板失败");
+            }
+            JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+            Integer errcode = jsonObject.getInteger("errcode");
+            if(0 != errcode){
+                return ResultUtil.error(jsonObject.getString("errmsg"));
+            }
+            String priTmplId = jsonObject.getString("priTmplId");
+            return ResultUtil.success(priTmplId);
+        }catch (Exception e){
+            e.printStackTrace();
+            return ResultUtil.runErr();
+        }
+
+    }
+
+
+    /**
+     * 删除模板
+     * @param priTmplId     模板id
+     * @return
+     */
+    public ResultUtil deltemplate(String priTmplId){
+        try {
+            String accessToken = redisUtil.getValue("wxAppletsAccessToken");
+            String url = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token=" + accessToken;
+            Map<String, Object> params = new HashMap<>();
+            params.put("priTmplId", priTmplId);
+            HttpResult httpResult = httpClientUtil.pushHttpRequset("POST", url, params, null, "json");
+            if(httpResult.getCode() != 200){
+                return ResultUtil.error("获取消息模板失败");
+            }
+            JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+            Integer errcode = jsonObject.getInteger("errcode");
+            if(0 != errcode){
+                return ResultUtil.error(jsonObject.getString("errmsg"));
+            }
+            return ResultUtil.success();
+        }catch (Exception e){
+            e.printStackTrace();
+            return ResultUtil.runErr();
+        }
+    }
+
+
+    /**
+     * 发送消息
+     * @param template_id           所需下发的订阅模板id
+     * @param page                  点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转
+     * @param touser                接收者(用户)的 openid
+     * @param data                  模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }的object
+     * @param miniprogram_state     跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
+     * @param lang                  进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN
+     * @return
+     */
+    public ResultUtil send(String template_id, String page, String touser, String data, String miniprogram_state, String lang){
+        try {
+            String accessToken = redisUtil.getValue("wxAppletsAccessToken");
+            String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken;
+            Map<String, Object> params = new HashMap<>();
+            params.put("template_id", template_id);
+            params.put("page", page);
+            params.put("touser", touser);
+            params.put("data", JSON.parseObject(data));
+            params.put("miniprogram_state", miniprogram_state);
+            params.put("lang", lang);
+            HttpResult httpResult = httpClientUtil.pushHttpRequset("POST", url, params, null, "json");
+            if(httpResult.getCode() != 200){
+                return ResultUtil.error("获取消息模板失败");
+            }
+            JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+            Integer errcode = jsonObject.getInteger("errcode");
+            if(0 != errcode){
+                return ResultUtil.error(jsonObject.getString("errmsg"));
+            }
+            return ResultUtil.success();
+        }catch (Exception e){
+            e.printStackTrace();
+            return ResultUtil.runErr();
+        }
+    }
+
+
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java
new file mode 100644
index 0000000..5b606fa
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java
@@ -0,0 +1,288 @@
+/**
+ * 对公众平台发送给公众账号的消息加解密示例代码.
+ * 
+ * @copyright Copyright (c) 1998-2014 Tencent Inc.
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * 针对org.apache.commons.codec.binary.Base64,
+ * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)
+ * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
+ */
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串).
+ * <ol>
+ * 	<li>第三方回复加密消息给公众平台</li>
+ * 	<li>第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。</li>
+ * </ol>
+ * 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案
+ * <ol>
+ * 	<li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:
+ *      http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
+ * 	<li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
+ * 	<li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li>
+ * 	<li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li>
+ * </ol>
+ */
+public class WXBizMsgCrypt {
+	static Charset CHARSET = Charset.forName("utf-8");
+	Base64 base64 = new Base64();
+	byte[] aesKey;
+	String token;
+	String appId;
+
+	/**
+	 * 构造函数
+	 * @param token 公众平台上,开发者设置的token
+	 * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey
+	 * @param appId 公众平台appid
+	 * 
+	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
+	 */
+	public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException {
+		if (encodingAesKey.length() != 43) {
+			throw new AesException(AesException.IllegalAesKey);
+		}
+
+		this.token = token;
+		this.appId = appId;
+		aesKey = Base64.decodeBase64(encodingAesKey + "=");
+	}
+
+	// 生成4个字节的网络字节序
+	byte[] getNetworkBytesOrder(int sourceNumber) {
+		byte[] orderBytes = new byte[4];
+		orderBytes[3] = (byte) (sourceNumber & 0xFF);
+		orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
+		orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
+		orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
+		return orderBytes;
+	}
+
+	// 还原4个字节的网络字节序
+	int recoverNetworkBytesOrder(byte[] orderBytes) {
+		int sourceNumber = 0;
+		for (int i = 0; i < 4; i++) {
+			sourceNumber <<= 8;
+			sourceNumber |= orderBytes[i] & 0xff;
+		}
+		return sourceNumber;
+	}
+
+	// 随机生成16位字符串
+	String getRandomStr() {
+		String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+		Random random = new Random();
+		StringBuffer sb = new StringBuffer();
+		for (int i = 0; i < 16; i++) {
+			int number = random.nextInt(base.length());
+			sb.append(base.charAt(number));
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * 对明文进行加密.
+	 * 
+	 * @param text 需要加密的明文
+	 * @return 加密后base64编码的字符串
+	 * @throws AesException aes加密失败
+	 */
+	String encrypt(String randomStr, String text) throws AesException {
+		ByteGroup byteCollector = new ByteGroup();
+		byte[] randomStrBytes = randomStr.getBytes(CHARSET);
+		byte[] textBytes = text.getBytes(CHARSET);
+		byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);
+		byte[] appidBytes = appId.getBytes(CHARSET);
+
+		// randomStr + networkBytesOrder + text + appid
+		byteCollector.addBytes(randomStrBytes);
+		byteCollector.addBytes(networkBytesOrder);
+		byteCollector.addBytes(textBytes);
+		byteCollector.addBytes(appidBytes);
+
+		// ... + pad: 使用自定义的填充方式对明文进行补位填充
+		byte[] padBytes = PKCS7Encoder.encode(byteCollector.size());
+		byteCollector.addBytes(padBytes);
+
+		// 获得最终的字节流, 未加密
+		byte[] unencrypted = byteCollector.toBytes();
+
+		try {
+			// 设置加密模式为AES的CBC模式
+			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+			SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
+			IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
+			cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
+
+			// 加密
+			byte[] encrypted = cipher.doFinal(unencrypted);
+
+			// 使用BASE64对加密后的字符串进行编码
+			String base64Encrypted = base64.encodeToString(encrypted);
+
+			return base64Encrypted;
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw new AesException(AesException.EncryptAESError);
+		}
+	}
+
+	/**
+	 * 对密文进行解密.
+	 * 
+	 * @param text 需要解密的密文
+	 * @return 解密得到的明文
+	 * @throws AesException aes解密失败
+	 */
+	String decrypt(String text) throws AesException {
+		byte[] original;
+		try {
+			// 设置解密模式为AES的CBC模式
+			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+			SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");
+			IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
+			cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
+
+			// 使用BASE64对密文进行解码
+			byte[] encrypted = Base64.decodeBase64(text);
+
+			// 解密
+			original = cipher.doFinal(encrypted);
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw new AesException(AesException.DecryptAESError);
+		}
+
+		String xmlContent, from_appid;
+		try {
+			// 去除补位字符
+			byte[] bytes = PKCS7Encoder.decode(original);
+
+			// 分离16位随机字符串,网络字节序和AppId
+			byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
+
+			int xmlLength = recoverNetworkBytesOrder(networkOrder);
+
+			xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);
+			from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length),
+					CHARSET);
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw new AesException(AesException.IllegalBuffer);
+		}
+
+		// appid不相同的情况
+		if (!from_appid.equals(appId)) {
+			throw new AesException(AesException.ValidateAppidError);
+		}
+		return xmlContent;
+
+	}
+
+	/**
+	 * 将公众平台回复用户的消息加密打包.
+	 * <ol>
+	 * 	<li>对要发送的消息进行AES-CBC加密</li>
+	 * 	<li>生成安全签名</li>
+	 * 	<li>将消息密文和安全签名打包成xml格式</li>
+	 * </ol>
+	 * 
+	 * @param replyMsg 公众平台待回复用户的消息,xml格式的字符串
+	 * @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp
+	 * @param nonce 随机串,可以自己生成,也可以用URL参数的nonce
+	 * 
+	 * @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串
+	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
+	 */
+	public String encryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException {
+		// 加密
+		String encrypt = encrypt(getRandomStr(), replyMsg);
+
+		// 生成安全签名
+		if (timeStamp == "") {
+			timeStamp = Long.toString(System.currentTimeMillis());
+		}
+
+		String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt);
+
+		// System.out.println("发送给平台的签名是: " + signature[1].toString());
+		// 生成发送的xml
+		String result = XMLParse.generate(encrypt, signature, timeStamp, nonce);
+		return result;
+	}
+
+	/**
+	 * 检验消息的真实性,并且获取解密后的明文.
+	 * <ol>
+	 * 	<li>利用收到的密文生成安全签名,进行签名验证</li>
+	 * 	<li>若验证通过,则提取xml中的加密消息</li>
+	 * 	<li>对消息进行解密</li>
+	 * </ol>
+	 * 
+	 * @param msgSignature 签名串,对应URL参数的msg_signature
+	 * @param timeStamp 时间戳,对应URL参数的timestamp
+	 * @param nonce 随机串,对应URL参数的nonce
+	 * @param postData 密文,对应POST请求的数据
+	 * 
+	 * @return 解密后的原文
+	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
+	 */
+	public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData)
+			throws AesException {
+
+		// 密钥,公众账号的app secret
+		// 提取密文
+		Object[] encrypt = XMLParse.extract(postData);
+
+		// 验证安全签名
+		String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());
+
+		// 和URL中的签名比较是否相等
+		// System.out.println("第三方收到URL中的签名:" + msg_sign);
+		// System.out.println("第三方校验签名:" + signature);
+		if (!signature.equals(msgSignature)) {
+			throw new AesException(AesException.ValidateSignatureError);
+		}
+
+		// 解密
+		String result = decrypt(encrypt[1].toString());
+		return result;
+	}
+
+	/**
+	 * 验证URL
+	 * @param msgSignature 签名串,对应URL参数的msg_signature
+	 * @param timeStamp 时间戳,对应URL参数的timestamp
+	 * @param nonce 随机串,对应URL参数的nonce
+	 * @param echoStr 随机串,对应URL参数的echostr
+	 * 
+	 * @return 解密之后的echostr
+	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
+	 */
+	public String verifyUrl(String msgSignature, String timeStamp, String nonce, String echoStr)
+			throws AesException {
+		String signature = SHA1.getSHA1(token, timeStamp, nonce, "");
+
+		if (!signature.equals(msgSignature)) {
+			throw new AesException(AesException.ValidateSignatureError);
+		}
+
+		String result = decrypt(echoStr);
+		return result;
+	}
+
+}
\ No newline at end of file
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java
new file mode 100644
index 0000000..e8331f6
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java
@@ -0,0 +1,147 @@
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import org.junit.*;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.StringReader;
+
+import static org.junit.Assert.*;
+
+public class WXBizMsgCryptTest {
+	String encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
+	String token = "pamtest";
+	String timestamp = "1409304348";
+	String nonce = "xxxxxx";
+	String appId = "wxb11529c136998cb6";
+	String replyMsg = "我是中文abcd123";
+	String xmlFormat = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>";
+	String afterAesEncrypt = "jn1L23DB+6ELqJ+6bruv21Y6MD7KeIfP82D6gU39rmkgczbWwt5+3bnyg5K55bgVtVzd832WzZGMhkP72vVOfg==";
+	String randomStr = "aaaabbbbccccdddd";
+
+	String replyMsg2 = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>";
+	String afterAesEncrypt2 = "jn1L23DB+6ELqJ+6bruv23M2GmYfkv0xBh2h+XTBOKVKcgDFHle6gqcZ1cZrk3e1qjPQ1F4RsLWzQRG9udbKWesxlkupqcEcW7ZQweImX9+wLMa0GaUzpkycA8+IamDBxn5loLgZpnS7fVAbExOkK5DYHBmv5tptA9tklE/fTIILHR8HLXa5nQvFb3tYPKAlHF3rtTeayNf0QuM+UW/wM9enGIDIJHF7CLHiDNAYxr+r+OrJCmPQyTy8cVWlu9iSvOHPT/77bZqJucQHQ04sq7KZI27OcqpQNSto2OdHCoTccjggX5Z9Mma0nMJBU+jLKJ38YB1fBIz+vBzsYjrTmFQ44YfeEuZ+xRTQwr92vhA9OxchWVINGC50qE/6lmkwWTwGX9wtQpsJKhP+oS7rvTY8+VdzETdfakjkwQ5/Xka042OlUb1/slTwo4RscuQ+RdxSGvDahxAJ6+EAjLt9d8igHngxIbf6YyqqROxuxqIeIch3CssH/LqRs+iAcILvApYZckqmA7FNERspKA5f8GoJ9sv8xmGvZ9Yrf57cExWtnX8aCMMaBropU/1k+hKP5LVdzbWCG0hGwx/dQudYR/eXp3P0XxjlFiy+9DMlaFExWUZQDajPkdPrEeOwofJb";
+
+	@BeforeClass
+	public static void setUpBeforeClass() throws Exception {
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() throws Exception {
+	}
+
+	@Before
+	public void setUp() throws Exception {
+
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+
+	@Test
+	public void testNormal() throws ParserConfigurationException, SAXException, IOException {
+		try {
+			WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
+			String afterEncrpt = pc.encryptMsg(replyMsg, timestamp, nonce);
+
+			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+			DocumentBuilder db = dbf.newDocumentBuilder();
+			StringReader sr = new StringReader(afterEncrpt);
+			InputSource is = new InputSource(sr);
+			Document document = db.parse(is);
+
+			Element root = document.getDocumentElement();
+			NodeList nodelist1 = root.getElementsByTagName("Encrypt");
+			NodeList nodelist2 = root.getElementsByTagName("MsgSignature");
+
+			String encrypt = nodelist1.item(0).getTextContent();
+			String msgSignature = nodelist2.item(0).getTextContent();
+			String fromXML = String.format(xmlFormat, encrypt);
+
+			// 第三方收到公众号平台发送的消息
+			String afterDecrpt = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML);
+			assertEquals(replyMsg, afterDecrpt);
+		} catch (AesException e) {
+			fail("正常流程,怎么就抛出异常了??????");
+		}
+	}
+
+	@Test
+	public void testAesEncrypt() {
+		try {
+			WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
+			assertEquals(afterAesEncrypt, pc.encrypt(randomStr, replyMsg));
+		} catch (AesException e) {
+			e.printStackTrace();
+			fail("no异常");
+		}
+	}
+
+	@Test
+	public void testAesEncrypt2() {
+		try {
+			WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
+			assertEquals(afterAesEncrypt2, pc.encrypt(randomStr, replyMsg2));
+
+		} catch (AesException e) {
+			e.printStackTrace();
+			fail("no异常");
+		}
+	}
+
+	@Test
+	public void testIllegalAesKey() {
+		try {
+			new WXBizMsgCrypt(token, "abcde", appId);
+		} catch (AesException e) {
+			assertEquals(AesException.IllegalAesKey, e.getCode());
+			return;
+		}
+		fail("错误流程不抛出异常???");
+	}
+
+	@Test
+	public void testValidateSignatureError() throws ParserConfigurationException, SAXException,
+			IOException {
+		try {
+			WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
+			String afterEncrpt = pc.encryptMsg(replyMsg, timestamp, nonce);
+			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+			DocumentBuilder db = dbf.newDocumentBuilder();
+			StringReader sr = new StringReader(afterEncrpt);
+			InputSource is = new InputSource(sr);
+			Document document = db.parse(is);
+
+			Element root = document.getDocumentElement();
+			NodeList nodelist1 = root.getElementsByTagName("Encrypt");
+
+			String encrypt = nodelist1.item(0).getTextContent();
+			String fromXML = String.format(xmlFormat, encrypt);
+			pc.decryptMsg("12345", timestamp, nonce, fromXML); // 这里签名错误
+		} catch (AesException e) {
+			assertEquals(AesException.ValidateSignatureError, e.getCode());
+			return;
+		}
+		fail("错误流程不抛出异常???");
+	}
+
+	@Test
+	public void testVerifyUrl() throws AesException {
+		WXBizMsgCrypt wxcpt = new WXBizMsgCrypt("QDG6eK",
+				"jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C", "wx5823bf96d3bd56c7");
+		String verifyMsgSig = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";
+		String timeStamp = "1409659589";
+		String nonce = "263014780";
+		String echoStr = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";
+		wxcpt.verifyUrl(verifyMsgSig, timeStamp, nonce, echoStr);
+		// 只要不抛出异常就好
+	}
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java
new file mode 100644
index 0000000..d673c1a
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java
@@ -0,0 +1,52 @@
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import org.apache.commons.codec.binary.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+
+public class WXCore {
+
+    private static Logger logger = LoggerFactory.getLogger(WXCore.class);
+
+    private static final String WATERMARK = "watermark";
+
+    @Value("${wx.appletsAppid}")
+    private static String appid ;
+
+
+
+    /**
+     * 解密数据
+     * @return
+     * @throws Exception
+     */
+    public static String decrypt(String encryptedData, String sessionKey, String iv){
+        String result = "";
+        try {
+            AES aes = new AES();
+            byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv));
+            if(null != resultByte && resultByte.length > 0){
+                result = new String(WxPKCS7Encoder.decode(resultByte), "UTF-8");
+//                JSONObject jsonObject = JSON.parseObject(result);
+//                String decryptAppid = jsonObject.getJSONObject(WATERMARK).getString("appid");
+//                if(!appid.equals(decryptAppid)){
+//                    result = "";
+//                }
+            }
+        } catch (Exception e) {
+            result = "";
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+
+    public static void main(String[] args) throws Exception{
+        String appId = "wx4f4bc4dec97d474b";
+        String encryptedData = "ajhisfk0EhWCBcoxt/5QJA3bKPTygQXANbCSev92MyqIqGxRhzZFS1SlQrIWAo3tx8YfsB+VlScZNOlRyfnXKqPUl9h+PDeKsTkTst9V4pq4mUbI+r3AautXBffVk/rpyjMfanVsWeOlxkupdv2U9U4BrueR/Rak+TCmHTWUUf8eDLoi6nioL/Pft/rYaO3JD54hgpcY0Ef/k7Boyap4E0/uKVowY1ANO7KVydSXE6S7OKzXuzmDTbV38v+7ryMHtglUzoKzF1gL8y9OZRwkaesfHN1kfItjpsAibjSgkIiiW6ZEHLT2n1UDkJtfUqg63je2cJAH7gzeUm9TCDDftdhLU+NuPk3j/LXyFQ05pJ8B19+kIXF3dcmHOH7lFvi7yGmVuAD/9AnetGWgId3TZDS/OjbiVUM30RogeziAw98VpAyAAq1r2ULFwi8e928m";
+        String sessionKey = "CdxFGwXIluQFZ+qD+NSFKQ==";
+        String iv = "3FD8r1Spwlf7LG8YEq41+Q==";
+        logger.debug(decrypt(encryptedData, sessionKey, iv));
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java
new file mode 100644
index 0000000..c23f490
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java
@@ -0,0 +1,415 @@
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.supersavedriving.user.core.util.ToolUtil;
+import com.supersavedriving.user.modular.system.util.RedisUtil;
+import com.supersavedriving.user.modular.system.util.UUIDUtil;
+import com.supersavedriving.user.modular.system.util.httpClinet.HttpClientUtil;
+import com.supersavedriving.user.modular.system.util.httpClinet.HttpResult;
+import com.supersavedriving.user.modular.system.util.weChat.model.Code2Session;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * 微信工具类
+ */
+@Component
+public class WeChatUtil {
+
+    private static Logger logger = LoggerFactory.getLogger(WeChatUtil.class);
+
+    @Value("${wx.appletsAppid}")
+    private String wxAppletsAppid;
+
+    @Value("${wx.appletsAppSecret}")
+    private String wxAppletsAppSecret;
+
+    @Value("${wx.officialAccountAppid}")
+    private String officialAccountAppid;
+
+    @Value("{wx.officialAccountAppSecret}")
+    private String officialAccountAppSecret;
+
+    @Value("${wx.appid}")
+    private String webAppId;
+
+    @Value("${wx.appSecret}")
+    private String webAppSecret;
+
+    @Autowired
+    private RestTemplate restTemplate;
+
+    @Autowired
+    private RedisUtil redisUtil;
+
+
+
+    {
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(30000);
+                    new Timer().schedule(new TimerTask() {
+                        @Override
+                        public void run() {
+                            try {
+                                String wxAppletsAccessToken = getWxAppletsAccessToken();
+                                if(ToolUtil.isEmpty(wxAppletsAccessToken)){
+                                    System.err.println("获取微信小程序access_token失败");
+                                    return;
+                                }
+                                redisUtil.setStrValue("wxAppletsAccessToken", wxAppletsAccessToken, 7000);
+                            }catch (Exception e){
+                                e.printStackTrace();
+                            }
+                        }
+                    }, 0, 7000000);
+                }catch (Exception e){
+                    e.printStackTrace();
+                }
+            }
+        }).start();
+
+    }
+
+
+    /**
+     * 小程序使用jscode获取openid
+     * @param jscode
+     * @return
+     */
+    public Code2Session code2Session(String jscode) throws Exception{
+        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret
+                + "&js_code=" + jscode + "&grant_type=authorization_code";
+        HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form");
+        if(null == httpResult || httpResult.getCode() != 200){
+            return null;
+        }
+        Code2Session code2Session = JSON.parseObject(httpResult.getData(), Code2Session.class);
+        return code2Session;
+    }
+
+
+    /**
+     * 获取微信小程序token
+     * @return
+     */
+    public String getWxAppletsAccessToken() throws Exception{
+        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret;
+        HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form");
+        if(httpResult.getCode() != 200){
+            return "";
+        }
+        JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+        return jsonObject.getString("access_token");
+    }
+
+
+    /***
+     * 获取jsapiTicket(小程序)
+     * 来源 www.vxzsk.com
+     * @return
+     */
+    public String getWxAppletsJSApiTicket() throws Exception{
+        String wxAppletsAccessToken = redisUtil.getValue("wxAppletsAccessToken");
+        String urlStr = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + wxAppletsAccessToken + "&type=jsapi";
+        HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", urlStr, null, null, "form");
+        if(httpResult.getCode() != 200){
+            return null;
+        }
+        logger.debug(httpResult.getData());
+        String ticket = JSONObject.parseObject(httpResult.getData()).getString("ticket");
+        return  ticket;
+    }
+
+    /**
+     * 通过config接口注入权限验证配置(小程序)
+     * 附录1-JS-SDK使用权限签名算法,
+     * @return
+     */
+    public Map<String,Object> getWxAppletsSignatureConfig(String url) throws Exception{
+        //获取token
+        try {
+            url = URLDecoder.decode(url, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        String ticket = getWxAppletsJSApiTicket();
+        String noncestr = UUIDUtil.getRandomCode();
+        Long timestamp = System.currentTimeMillis();
+        String content = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url;
+        String signature = DigestUtils.sha1Hex(content);
+        Map<String,Object> map=new HashMap<>();
+        map.put("appId", wxAppletsAppid);
+        map.put("timestamp", timestamp);
+        map.put("nonceStr", noncestr);
+        map.put("signature", signature);
+        return  map;
+    }
+
+
+
+    /**
+     * 网站应用登录
+     * @param code
+     * @return
+     */
+    public Map<String, String> webAccessToken(String code) throws Exception{
+        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + webAppId + "&secret=" + webAppSecret + "&code=" + code + "&grant_type=authorization_code";
+        HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form");
+        if(httpResult.getCode() != 200){
+            return null;
+        }
+        JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+        int errcode = jsonObject.getIntValue("errcode");
+        Map<String, String> map = new HashMap<>();
+        if(errcode == 0){//成功
+            map.put("access_token", jsonObject.getString("access_token"));
+            map.put("openid", jsonObject.getString("openid"));
+            map.put("refresh_token", jsonObject.getString("refresh_token"));
+            map.put("unionid", jsonObject.getString("unionid"));
+            return map;
+        }
+        if(errcode == -1){//系统繁忙,此时请开发者稍候再试
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 40029){//code 无效
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 45011){//频率限制,每个用户每分钟100次
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        return map;
+    }
+
+
+    /**
+     * 获取微信个人信息
+     * @param access_token
+     * @param openid
+     * @return
+     */
+    public Map<String, Object> getUserInfo(String access_token, String openid) throws Exception{
+        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid;
+        HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form");
+        if(httpResult.getCode() != 200){
+            return null;
+        }
+        JSONObject jsonObject = JSON.parseObject(httpResult.getData());
+        int errcode = jsonObject.getIntValue("errcode");
+        Map<String, Object> map = new HashMap<>();
+        if(errcode == 0){//成功
+            map.put("nickname", jsonObject.getString("nickname"));
+            map.put("openid", jsonObject.getString("openid"));
+            map.put("sex", jsonObject.getString("sex"));
+            map.put("headimgurl", jsonObject.getString("headimgurl"));
+            return map;
+        }
+        if(errcode == -1){//系统繁忙,此时请开发者稍候再试
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 40029){//code 无效
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 45011){//频率限制,每个用户每分钟100次
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        return map;
+    }
+
+
+    /**
+     * 公众号获取openid
+     * @param code
+     * @return
+     */
+    public Map<String,Object> getOpenId(String code) throws Exception{
+        if (code == null || code.length() == 0) {
+            return null;
+        }
+        String grantType = "authorization_code";
+        String params = "appid=" + officialAccountAppid + "&secret=" + officialAccountAppSecret + "&code=" + code + "&grant_type=" + grantType;
+        logger.debug("sssss"+params);
+        HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", "https://api.weixin.qq.com/sns/oauth2/access_token?" + params, null, null, "form");
+        if(httpResult.getCode() != 200){
+            return null;
+        }
+        JSONObject json = JSON.parseObject(httpResult.getData());
+        logger.debug(json.toJSONString());
+        String openId = json.get("openid").toString();
+        String accessToken = json.get("access_token").toString();
+        Integer expiresIn = json.getInteger("expires_in");
+        String refresh_token = json.getString("refresh_token");
+        String unionid = json.getString("unionid");
+        Map<String,Object> map=new HashMap<>();
+        map.put("openId",openId);
+        map.put("accessToken",accessToken);
+        map.put("expiresIn", expiresIn);
+        map.put("refreshToken", refresh_token);
+        map.put("unionid", unionid);
+        return map;
+    }
+
+
+    /***
+     * 获取acess_token (公众号)
+     * 来源www.vxzsk.com
+     * @return
+     */
+    public String getAccessToken() throws Exception{
+        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + officialAccountAppid
+                + "&secret=" + officialAccountAppSecret;
+        HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form");
+        if(httpResult.getCode() != 200){
+            return null;
+        }
+        String accessToken = JSONObject.parseObject(httpResult.getData()).getString("access_token");
+        return accessToken;
+    }
+
+    /***
+     * 获取jsapiTicket(公众号)
+     * 来源 www.vxzsk.com
+     * @return
+     */
+    public String getJSApiTicket() throws Exception{
+        //获取token
+        String acess_token = redisUtil.getValue("acess_token");
+        String urlStr = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + acess_token + "&type=jsapi";
+        HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", urlStr, null, null, "form");
+        if(httpResult.getCode() != 200){
+            return null;
+        }
+        logger.debug(httpResult.getData());
+        String ticket = JSONObject.parseObject(httpResult.getData()).getString("ticket");
+        return  ticket;
+    }
+
+    /**
+     * 通过config接口注入权限验证配置(公众号)
+     * 附录1-JS-SDK使用权限签名算法,
+     * @return
+     */
+    public Map<String,Object> getSignatureConfig(String url) throws Exception{
+        //获取token
+        try {
+            url = URLDecoder.decode(url, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        String ticket = getJSApiTicket();
+        String noncestr = UUIDUtil.getRandomCode();
+        Long timestamp = System.currentTimeMillis();
+        String content = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url;
+        String signature = DigestUtils.sha1Hex(content);
+        Map<String,Object> map=new HashMap<>();
+        map.put("appId", officialAccountAppid);
+        map.put("timestamp", timestamp);
+        map.put("nonceStr", noncestr);
+        map.put("signature", signature);
+        return  map;
+    }
+
+
+    /**
+     * 公众号获取用户个人信息
+     * @param access_token
+     * @param openid
+     * @return
+     */
+    public Map<String, Object> queryUserInfo(String access_token, String openid) throws Exception{
+        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid + "&lang=zh_CN";
+        HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form");
+        if(httpResult.getCode() != 200){
+            return null;
+        }
+        logger.debug(httpResult.getData());
+        JSONObject j = JSON.parseObject(httpResult.getData());
+        Map<String, Object> map = new HashMap<>();
+        map.put("nickname", j.getString("nickname"));
+        map.put("sex", j.getInteger("sex"));
+        map.put("headimgurl", j.getString("headimgurl"));
+        map.put("unionid", j.getString("unionid"));
+        return map;
+    }
+
+
+    /**
+     * 获取小程序二维码
+     * @param page      跳转页 例如 pages/index/index
+     * @param scene     参数 a=1&b=2
+     */
+    public InputStream getwxacodeunlimit(String page, String scene, String envVersion) throws Exception{
+        try {
+            String wxAppletsAccessToken = redisUtil.getValue("wxAppletsAccessToken");
+            String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + wxAppletsAccessToken;
+            Map<String, Object> param = new HashMap<>();
+            param.put("scene", scene);
+            param.put("page", page);
+            param.put("env_version", envVersion);
+            HttpHeaders httpHeaders = new HttpHeaders();
+            MediaType type=MediaType.parseMediaType("application/json;charset=UTF-8");
+            httpHeaders.setContentType(type);
+            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(param, httpHeaders);
+            ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class, new Object[0]);
+            String body1 = exchange.getBody();
+//            System.err.println(body1);
+            ResponseEntity<byte[]> entity  = restTemplate.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]);
+            byte[] body = entity.getBody();
+//            System.err.println(Base64.encodeBase64String(body));
+            return new ByteArrayInputStream(body);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    /**
+     * 获取小程序urlscheme码
+     * @return
+     */
+    public String getUrlscheme(String path) throws Exception{
+        String wxAppletsAccessToken = redisUtil.getValue("wxAppletsAccessToken");
+        String url = "https://api.weixin.qq.com/wxa/generatescheme?access_token=" + wxAppletsAccessToken;
+        Map<String, Object> param = new HashMap<>();
+        param.put("is_expire", true);
+        param.put("expire_type", 1);
+        param.put("expire_interval", 180);
+        Map<String, Object> map1 = new HashMap<>();
+        map1.put("path", path);
+        map1.put("query", "");
+        map1.put("env_version", "release");
+        param.put("jump_wxa", map1);
+        HttpHeaders httpHeaders = new HttpHeaders();
+        MediaType type=MediaType.parseMediaType("application/json;charset=UTF-8");
+        httpHeaders.setContentType(type);
+        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(param, httpHeaders);
+        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
+        String body1 = exchange.getBody();
+        return body1;
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java
new file mode 100644
index 0000000..8524463
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java
@@ -0,0 +1,63 @@
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+/**
+* 微信小程序加解密
+* @author pzb
+* @Date 2021/12/3 15:43
+*/
+public class WxPKCS7Encoder {
+    private static final Charset CHARSET = Charset.forName("utf-8");
+    private static final int BLOCK_SIZE = 32;
+
+    /**
+     * 获得对明文进行补位填充的字节.
+     *
+     * @param count
+     *            需要进行填充补位操作的明文字节个数
+     * @return 补齐用的字节数组
+     */
+    public static byte[] encode(int count) {
+        // 计算需要填充的位数
+        int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
+        if (amountToPad == 0) {
+            amountToPad = BLOCK_SIZE;
+        }
+        // 获得补位所用的字符
+        char padChr = chr(amountToPad);
+        String tmp = new String();
+        for (int index = 0; index < amountToPad; index++) {
+            tmp += padChr;
+        }
+        return tmp.getBytes(CHARSET);
+    }
+
+    /**
+     * 删除解密后明文的补位字符
+     *
+     * @param decrypted
+     *            解密后的明文
+     * @return 删除补位字符后的明文
+     */
+    public static byte[] decode(byte[] decrypted) {
+        int pad = decrypted[decrypted.length - 1];
+        if (pad < 1 || pad > 32) {
+            pad = 0;
+        }
+        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
+    }
+
+    /**
+     * 将数字转化成ASCII码对应的字符,用于对明文进行补码
+     *
+     * @param a
+     *            需要转化的数字
+     * @return 转化得到的字符
+     */
+    public static char chr(int a) {
+        byte target = (byte) (a & 0xFF);
+        return (char) target;
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java
new file mode 100644
index 0000000..61dedfd
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java
@@ -0,0 +1,71 @@
+/**
+ * 对公众平台发送给公众账号的消息加解密示例代码.
+ * 
+ * @copyright Copyright (c) 1998-2014 Tencent Inc.
+ */
+
+// ------------------------------------------------------------------------
+
+package com.supersavedriving.user.modular.system.util.weChat;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.StringReader;
+
+/**
+ * XMLParse class
+ *
+ * 提供提取消息格式中的密文及生成回复消息格式的接口.
+ */
+class XMLParse {
+
+	/**
+	 * 提取出xml数据包中的加密消息
+	 * @param xmltext 待提取的xml字符串
+	 * @return 提取出的加密消息字符串
+	 * @throws AesException 
+	 */
+	public static Object[] extract(String xmltext) throws AesException     {
+		Object[] result = new Object[3];
+		try {
+			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+			DocumentBuilder db = dbf.newDocumentBuilder();
+			StringReader sr = new StringReader(xmltext);
+			InputSource is = new InputSource(sr);
+			Document document = db.parse(is);
+
+			Element root = document.getDocumentElement();
+			NodeList nodelist1 = root.getElementsByTagName("Encrypt");
+			NodeList nodelist2 = root.getElementsByTagName("ToUserName");
+			result[0] = 0;
+			result[1] = nodelist1.item(0).getTextContent();
+			result[2] = nodelist2.item(0).getTextContent();
+			return result;
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw new AesException(AesException.ParseXmlError);
+		}
+	}
+
+	/**
+	 * 生成xml消息
+	 * @param encrypt 加密后的消息密文
+	 * @param signature 安全签名
+	 * @param timestamp 时间戳
+	 * @param nonce 随机字符串
+	 * @return 生成的xml字符串
+	 */
+	public static String generate(String encrypt, String signature, String timestamp, String nonce) {
+
+		String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n"
+				+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n"
+				+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" + "</xml>";
+		return String.format(format, encrypt, signature, timestamp, nonce);
+
+	}
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java
new file mode 100644
index 0000000..61a2eac
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java
@@ -0,0 +1,18 @@
+package com.supersavedriving.user.modular.system.util.weChat.model;
+
+import lombok.Data;
+
+/**
+ * 订阅消息类目
+ */
+@Data
+public class Category {
+    /**
+     * 类目id
+     */
+    private String id;
+    /**
+     * 类目名称
+     */
+    private String name;
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java
new file mode 100644
index 0000000..c5e8b80
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java
@@ -0,0 +1,33 @@
+package com.supersavedriving.user.modular.system.util.weChat.model;
+
+import lombok.Data;
+
+/**
+ * TODO
+ *
+ * @author 39373
+ * @date 2023/2/26 19:42
+ */
+@Data
+public class Code2Session {
+    /**
+     * 状态码(-1=系统繁忙,0=成功,40029=code无效,45011=频率限制,每个用户每分钟100次,40226=高风险等级用户,小程序登录拦截 )
+     */
+    private Integer errcode;
+    /**
+     * 状态说明
+     */
+    private String errmsg;
+    /**
+     * openid
+     */
+    private String openid;
+    /**
+     * sessionKey
+     */
+    private String session_key;
+    /**
+     * unionid
+     */
+    private String unionid;
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java
new file mode 100644
index 0000000..7a15e62
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java
@@ -0,0 +1,17 @@
+package com.supersavedriving.user.modular.system.util.weChat.model;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class KeywordEnum {
+    /**
+     * 枚举参数的 key
+     */
+    private String keywordCode;
+    /**
+     * 枚举参数值范围列表
+     */
+    private List<String> enumValueList;
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java
new file mode 100644
index 0000000..badf006
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java
@@ -0,0 +1,36 @@
+package com.supersavedriving.user.modular.system.util.weChat.model;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 消息模板
+ */
+@Data
+public class MessageTemplate {
+    /**
+     * 添加至帐号下的模板 id,发送小程序订阅消息时所需
+     */
+    private String priTmplId;
+    /**
+     * 模版标题
+     */
+    private String title;
+    /**
+     * 模版内容
+     */
+    private String content;
+    /**
+     * 模板内容示例
+     */
+    private String example;
+    /**
+     * 模版类型,2 为一次性订阅,3 为长期订阅
+     */
+    private Integer type;
+    /**
+     * 枚举参数值范围
+     */
+    private List<KeywordEnum> keywordEnumValueList;
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java
new file mode 100644
index 0000000..5e9f9ef
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java
@@ -0,0 +1,26 @@
+package com.supersavedriving.user.modular.system.util.weChat.model;
+
+import lombok.Data;
+
+/**
+ * 公共消息模板关键字
+ */
+@Data
+public class PubTemplateKeywords {
+    /**
+     * 关键词 id,选用模板时需要
+     */
+    private Integer kid;
+    /**
+     * 关键词内容
+     */
+    private String name;
+    /**
+     * 关键词内容对应的示例
+     */
+    private String example;
+    /**
+     * 参数类型
+     */
+    private String rule;
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java
new file mode 100644
index 0000000..4eb82e8
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java
@@ -0,0 +1,26 @@
+package com.supersavedriving.user.modular.system.util.weChat.model;
+
+import lombok.Data;
+
+/**
+ * 功能模板
+ */
+@Data
+public class PubTemplatetitles {
+    /**
+     * 模版标题 id
+     */
+    private Integer tid;
+    /**
+     * 模版标题
+     */
+    private String title;
+    /**
+     * 模版类型,2 为一次性订阅,3 为长期订阅
+     */
+    private Integer type;
+    /**
+     * 模版所属类目 id
+     */
+    private Integer categoryId;
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java
new file mode 100644
index 0000000..7f7d595
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java
@@ -0,0 +1,49 @@
+package com.supersavedriving.user.modular.system.warpper;
+
+import com.supersavedriving.user.modular.system.util.ResultUtil;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+* 统一返回实体
+* @author pzb
+* @Date 2023/2/3 10:46
+*/
+@Data
+@ApiModel
+public class ResponseWarpper<T> {
+    @ApiModelProperty(value = "返回状态码(200/300/400/500)", required = true, dataType = "int")
+    private Integer code;
+    @ApiModelProperty(value = "结果备注", required = true, dataType = "string")
+    private String message;
+    @ApiModelProperty(value = "结果集对象", required = false)
+    private ResultUtil<T> resultUtil;
+
+    public ResponseWarpper() {
+    }
+
+    public ResponseWarpper(Integer code, String message) {
+        this.code = code;
+        this.message = message;
+        this.resultUtil = ResultUtil.success();
+    }
+
+    public ResponseWarpper(Integer code, String message, ResultUtil<T> resultUtil) {
+        this.code = code;
+        this.message = message;
+        this.resultUtil = resultUtil;
+    }
+
+    public static ResponseWarpper success() {
+        return new ResponseWarpper(200, "success", ResultUtil.success());
+    }
+
+    public static <T> ResponseWarpper<T> success(T data) {
+        return new ResponseWarpper(200, "success", ResultUtil.success(data));
+    }
+
+    public static <T> ResponseWarpper<T> success(ResultUtil<T> resultUtil) {
+        return new ResponseWarpper(200, "success", resultUtil);
+    }
+}
diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java
new file mode 100644
index 0000000..1e4f337
--- /dev/null
+++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java
@@ -0,0 +1,23 @@
+package com.supersavedriving.user.modular.system.warpper;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 登录
+ * @author 39373
+ * @date 2023/2/26 21:21
+ */
+@Data
+public class SignInToRegister {
+    @ApiModelProperty(value = "微信jscode", required = true, dataType = "string")
+    private String jscode;
+    @ApiModelProperty(value = "手机加密数据", required = true, dataType = "string")
+    private String encryptedDataPhone;
+    @ApiModelProperty(value = "手机号码解码iv", required = true, dataType = "string")
+    private String ivPhone;
+    @ApiModelProperty(value = "邀约人类型(1=用户,2=司机)", required = false, dataType = "int")
+    private Integer inviterType;
+    @ApiModelProperty(value = "邀约人id", required = false, dataType = "int")
+    private Integer inviterId;
+}

--
Gitblit v1.7.1