puzhibing
2023-02-26 8883d654f52171657b62aaec85027f29ef0a4dd0
新增加用户端接口
3个文件已修改
1个文件已删除
37个文件已添加
3412 ■■■■ 已修改文件
user/guns-admin/pom.xml 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/HttpClientUtil.java 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java 250 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java 288 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java 415 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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>
user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java
New file
@@ -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;
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java
New file
@@ -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 "";
}
user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java
New file
@@ -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;
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java
New file
@@ -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());
        }
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java
New file
@@ -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());
        }
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java
New file
@@ -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> {
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java
New file
@@ -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> {
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.xml
New file
@@ -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>
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml
New file
@@ -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>
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java
New file
@@ -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;
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java
New file
@@ -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;
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java
New file
@@ -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;
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java
New file
@@ -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> {
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java
New file
@@ -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);
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java
New file
@@ -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 {
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/HttpClientUtil.java
File was deleted
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");
    }
}
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);
    }
    /**
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java
New file
@@ -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();
            }
        }
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java
New file
@@ -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;
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java
New file
@@ -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;
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java
New file
@@ -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;
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java
New file
@@ -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();
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java
New file
@@ -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;
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java
New file
@@ -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);
        }
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java
New file
@@ -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();
        }
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java
New file
@@ -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;
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java
New file
@@ -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);
        // 只要不抛出异常就好
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java
New file
@@ -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));
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java
New file
@@ -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;
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java
New file
@@ -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;
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java
New file
@@ -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);
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java
New file
@@ -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;
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java
New file
@@ -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;
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java
New file
@@ -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;
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java
New file
@@ -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;
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java
New file
@@ -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;
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java
New file
@@ -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;
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java
New file
@@ -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);
    }
}
user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java
New file
@@ -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;
}