wang_xy 2 年 前
コミット
b32bf7e777
100 ファイル変更18314 行追加0 行削除
  1. 133 0
      mybusiness/src/main/java/com/ruoyi/business/utils/AliyunSendSmsUtil.java
  2. 246 0
      mybusiness/src/main/java/com/ruoyi/business/utils/QRCodeUtils.java
  3. 48 0
      mybusiness/src/main/java/com/ruoyi/business/xhn/domain/PayDomain.java
  4. 307 0
      mybusiness/src/main/java/com/ruoyi/business/xhn/domain/RegisteruserUnpaid.java
  5. 97 0
      mybusiness/src/main/java/com/ruoyi/business/xhn/domain/XhnWxVo.java
  6. 60 0
      mybusiness/src/main/java/com/ruoyi/business/xhn/mapper/XhnWxMapper.java
  7. 199 0
      mybusiness/src/main/java/com/ruoyi/business/xhn/service/impl/RedisService.java
  8. 61 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/activityMiddle/domain/XhnActivityMiddle.java
  9. 61 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/activityMiddle/mapper/XhnActivityMiddleMapper.java
  10. 136 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/aliyunSms/domain/XhnAliyunSms.java
  11. 14 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/aliyunSms/mapper/XhnAliyunSmsMapper.java
  12. 21 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/aliyunSms/service/IXhnAliyunSmsService.java
  13. 25 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/aliyunSms/service/impl/XhnAliyunSmsServiceImpl.java
  14. 120 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/controller/XhnConsumeMiddleController.java
  15. 115 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/domain/XhnConsumeMiddle.java
  16. 62 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/mapper/XhnConsumeMiddleMapper.java
  17. 62 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/service/IXhnConsumeMiddleService.java
  18. 88 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/service/impl/XhnConsumeMiddleServiceImpl.java
  19. 120 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/controller/XhnIntegralController.java
  20. 107 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/domain/XhnIntegral.java
  21. 61 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/mapper/XhnIntegralMapper.java
  22. 61 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/service/IXhnIntegralService.java
  23. 87 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/service/impl/XhnIntegralServiceImpl.java
  24. 120 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/controller/XhnOrderController.java
  25. 92 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/domain/XhnOrder.java
  26. 61 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/mapper/XhnOrderMapper.java
  27. 61 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/service/IXhnOrderService.java
  28. 87 0
      mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/service/impl/XhnOrderServiceImpl.java
  29. 15 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/Credentials.java
  30. 105 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/SignatureExec.java
  31. 13 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/Validator.java
  32. 96 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/WechatPayHttpClientBuilder.java
  33. 96 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/WechatPayUploadHttpPost.java
  34. 159 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/AutoUpdateCertificatesVerifier.java
  35. 87 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/CertificatesVerifier.java
  36. 40 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/PrivateKeySigner.java
  37. 29 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/Signer.java
  38. 18 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/Verifier.java
  39. 95 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/WechatPay2Credentials.java
  40. 114 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/WechatPay2Validator.java
  41. 327 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/cert/CertificatesManager.java
  42. 33 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/cert/SafeSingleScheduleExecutor.java
  43. 20 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/constant/WechatPayHttpHeaders.java
  44. 14 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/HttpCodeException.java
  45. 14 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/NotFoundException.java
  46. 17 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/ParseException.java
  47. 15 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/ValidationException.java
  48. 18 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/WechatPayException.java
  49. 119 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/notification/Notification.java
  50. 172 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/notification/NotificationHandler.java
  51. 88 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/notification/NotificationRequest.java
  52. 37 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/notification/Request.java
  53. 50 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/util/AesUtil.java
  54. 64 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/util/CertSerializeUtil.java
  55. 70 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/util/PemUtil.java
  56. 61 0
      mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/util/RsaCryptoUtil.java
  57. 172 0
      mybusiness/src/main/resources/mapper/xhnWecharts/XhnWxMapper.xml
  58. 80 0
      mybusiness/src/main/resources/mapper/xhnnotsingle/XhnActivityMiddleMapper.xml
  59. 92 0
      mybusiness/src/main/resources/mapper/xhnnotsingle/XhnAliyunSmsMapper.xml
  60. 124 0
      mybusiness/src/main/resources/mapper/xhnnotsingle/XhnConsumeMiddleMapper.xml
  61. 114 0
      mybusiness/src/main/resources/mapper/xhnnotsingle/XhnIntegralMapper.xml
  62. 102 0
      mybusiness/src/main/resources/mapper/xhnnotsingle/XhnOrderMapper.xml
  63. 118 0
      mybusiness/src/main/resources/templates/appxhn/activity.html
  64. 29 0
      mybusiness/src/main/resources/templates/appxhn/bill.html
  65. 235 0
      mybusiness/src/main/resources/templates/appxhn/chaitamh.html
  66. 24 0
      mybusiness/src/main/resources/templates/appxhn/gywm.html
  67. 694 0
      mybusiness/src/main/resources/templates/appxhn/index.html
  68. 29 0
      mybusiness/src/main/resources/templates/appxhn/kefu.html
  69. 70 0
      mybusiness/src/main/resources/templates/appxhn/lookBuy.html
  70. 67 0
      mybusiness/src/main/resources/templates/appxhn/lookExtension.html
  71. 101 0
      mybusiness/src/main/resources/templates/appxhn/lookGiveGift.html
  72. 33 0
      mybusiness/src/main/resources/templates/appxhn/matchmaker.html
  73. 139 0
      mybusiness/src/main/resources/templates/appxhn/openBox.html
  74. 109 0
      mybusiness/src/main/resources/templates/appxhn/openRecharge.html
  75. 75 0
      mybusiness/src/main/resources/templates/appxhn/qidongye.html
  76. 264 0
      mybusiness/src/main/resources/templates/appxhn/register.html
  77. 248 0
      mybusiness/src/main/resources/templates/appxhn/updateuser.html
  78. 19 0
      mybusiness/src/main/resources/templates/appxhn/vipkt.html
  79. 79 0
      mybusiness/src/main/resources/templates/appxhn/红娘列表.html
  80. 53 0
      mybusiness/src/main/resources/templates/xhnnotsingle/integral/add.html
  81. 58 0
      mybusiness/src/main/resources/templates/xhnnotsingle/integral/edit.html
  82. 118 0
      mybusiness/src/main/resources/templates/xhnnotsingle/integral/integral.html
  83. 53 0
      mybusiness/src/main/resources/templates/xhnnotsingle/order/add.html
  84. 50 0
      mybusiness/src/main/resources/templates/xhnnotsingle/order/edit.html
  85. 101 0
      mybusiness/src/main/resources/templates/xhnnotsingle/order/order.html
  86. BIN
      ruoyi-admin/src/main/resources/apiclient_cert.p12
  87. 23 0
      ruoyi-admin/src/main/resources/apiclient_cert.pem
  88. 28 0
      ruoyi-admin/src/main/resources/apiclient_key.pem
  89. 6313 0
      ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinputPrint.min.js
  90. 10 0
      ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/columns/bootstrap-table-fixed-columns.min.js
  91. 10 0
      ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/editable/bootstrap-table-editable.min.js
  92. 2257 0
      ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/export/tableExport.js
  93. 117 0
      ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/reorder/bootstrap-table-reorder.js
  94. 598 0
      ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/reorder/jquery.tablednd.js
  95. 747 0
      ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-treetable/bootstrap-treetable.js
  96. 2 0
      ruoyi-admin/src/main/resources/static/ajax/libs/layui/lay/modules/laydate.js
  97. 2 0
      ruoyi-admin/src/main/resources/static/ajax/libs/layui/layui.js
  98. 52 0
      ruoyi-admin/src/main/resources/static/ajax/libs/report/echarts/echarts-all.js
  99. 7 0
      ruoyi-admin/src/main/resources/static/ajax/libs/select2/select2-bootstrap.css
  100. 0 0
      ruoyi-admin/src/main/resources/static/ajax/libs/staps/jquery.steps.css

+ 133 - 0
mybusiness/src/main/java/com/ruoyi/business/utils/AliyunSendSmsUtil.java

@@ -0,0 +1,133 @@
+package com.ruoyi.business.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
+import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.http.MethodType;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import com.ruoyi.business.xhnnotsingle.aliyunSms.domain.XhnAliyunSms;
+import com.ruoyi.common.utils.uuid.IdUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 短信验证码工具类
+ * @Author wang_xy
+ * @Date 2022/11/30
+ * @Description TODO
+ **/
+public class AliyunSendSmsUtil {
+
+    //短信API产品名称(短信产品名固定,无需修改)
+    public static final String product = "Dysmsapi";
+    //短信API产品域名(接口地址固定,无需修改)
+    public static final String domain = "dysmsapi.aliyuncs.com";
+    public static final String signName = "阿里云短信测试";
+    //替换成你的AK
+   public static final String accessKeyId = "LTAI5tPDteddvaEAY5cuAsZy";
+    //你的accessKeySecret,参考本文档步骤2
+   public static final String accessKeySecret = "SDfpw5EGSA9X21VlmVeWXkfr66t3n8";
+    public static Map<String,String> codeMsg = new HashMap<>();
+    public static Map<String,String> templCode = new HashMap<>();
+
+    // 返回值code
+    static {
+        // TODO:  短信模板
+      templCode.put("验证码","SMS_154950909");
+
+        // 返回code对应内容
+        codeMsg.put("OK","请求成功");
+        codeMsg.put("isp.RAM_PERMISSION_DENY","RAM权限DENY");
+        codeMsg.put("isv.OUT_OF_SERVICE","业务停机");
+        codeMsg.put("isv.PRODUCT_UN_SUBSCRIPT","未开通云通信产品的阿里云客户");
+        codeMsg.put("isv.PRODUCT_UNSUBSCRIBE","产品未开通");
+        codeMsg.put("isv.ACCOUNT_NOT_EXISTS","账户不存在");
+        codeMsg.put("isv.ACCOUNT_ABNORMAL","账户异常");
+        codeMsg.put("isv.SMS_TEMPLATE_ILLEGAL","短信模板不合法");
+        codeMsg.put("isv.SMS_SIGNATURE_ILLEGAL","短信签名不合法");
+        codeMsg.put("isv.INVALID_PARAMETERS","参数异常");
+        codeMsg.put("isp.SYSTEM_ERROR","系统错误");
+        codeMsg.put("isv.MOBILE_NUMBER_ILLEGAL","非法手机号");
+        codeMsg.put("isv.MOBILE_COUNT_OVER_LIMIT","手机号码数量超过限制");
+        codeMsg.put("isv.TEMPLATE_MISSING_PARAMETERS","模板缺少变量");
+        codeMsg.put("isv.BUSINESS_LIMIT_CONTROL","业务限流");
+        codeMsg.put("isv.INVALID_JSON_PARAM","JSON参数不合法,只接受字符串值");
+        codeMsg.put("isv.BLACK_KEY_CONTROL_LIMIT","黑名单管控");
+        codeMsg.put("isv.PARAM_LENGTH_LIMIT","参数超出长度限制");
+        codeMsg.put("isv.AMOUNT_NOT_ENOUGH","账户余额不足");
+    }
+
+    /**
+     * create by xqh on 2019/5/31 15:36
+     * 阿里云短信发送
+     * @params [aliyunSmsService本地保存service, tempCode短信模板, dataJson模板中的变量替换JSON串, phones]
+     */
+    public static XhnAliyunSms sendMsg(Callback callback, String tempCode, Map<String,String> tempMap, String... phones)throws ClientException {
+        String dataJson = JSON.toJSONString(tempMap);
+        //设置超时时间-可自行调整
+        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
+        System.setProperty("sun.net.client.defaultReadTimeout", "10000");
+
+        //初始化ascClient,暂时不支持多region(请勿修改)
+        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
+        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
+        IAcsClient acsClient = new DefaultAcsClient(profile);
+        //组装请求对象
+        SendSmsRequest request = new SendSmsRequest();
+        //使用post提交
+        request.setMethod(MethodType.POST);
+        //必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时,接收号码格式为国际区号+号码,如“85200000000”
+        request.setPhoneNumbers(StringUtils.join(phones, ","));
+        //必填:短信签名-可在短信控制台中找到
+        request.setSignName(signName);
+        //必填:短信模板-可在短信控制台中找到,发送国际/港澳台消息时,请使用国际/港澳台短信模版
+        request.setTemplateCode(tempCode);
+        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
+        //友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
+        // "{\"name\":\"Tom\", \"code\":\"123\"}"
+        request.setTemplateParam(dataJson);
+        //可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段)
+        //request.setSmsUpExtendCode("90997");
+        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
+//        request.setOutId("yourOutId");
+        //请求失败这里会抛ClientException异常
+        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
+
+        XhnAliyunSms xhnAliyunSms = createLocal(sendSmsResponse, tempCode, dataJson, phones);
+        new Thread(()->{
+            callback.callback(xhnAliyunSms);
+        }).start();
+
+        return xhnAliyunSms;
+    }
+    /**
+     * create by xqh on 2019/5/31 14:24
+     * 保存本地发送短信记录
+     * @params
+     */
+    private static XhnAliyunSms createLocal(SendSmsResponse sendSmsResponse, String tempCode, String dataJson, String... phones) {
+        XhnAliyunSms xhnAliyunSms = new XhnAliyunSms();
+        xhnAliyunSms.setId(IdUtils.randomUUID());
+        xhnAliyunSms.setMessage(sendSmsResponse.getMessage());
+        xhnAliyunSms.setRequestId(sendSmsResponse.getRequestId());
+        xhnAliyunSms.setBizId(sendSmsResponse.getBizId());
+        xhnAliyunSms.setCode(sendSmsResponse.getCode());
+        xhnAliyunSms.setSignName(signName);
+        xhnAliyunSms.setTempCode(tempCode);
+        xhnAliyunSms.setDataJson(dataJson);
+        xhnAliyunSms.setCount(1);
+        xhnAliyunSms.setPhone(StringUtils.join(phones, ","));
+        xhnAliyunSms.setCreateDate(new Date());
+        return xhnAliyunSms;
+    }
+    public interface Callback{
+        void callback(XhnAliyunSms xhnAliyunSms);
+    }
+}

+ 246 - 0
mybusiness/src/main/java/com/ruoyi/business/utils/QRCodeUtils.java

@@ -0,0 +1,246 @@
+package com.ruoyi.business.utils;
+
+import com.google.zxing.*;
+import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Objects;
+
+import static com.ruoyi.common.utils.file.FileUploadUtils.getDefaultBaseDir;
+
+public class QRCodeUtils {
+
+    // 二维码颜色
+    private static final int BLACK = 0x000125;
+    // 二维码背景色
+    private static final int WHITE = 0xFFFFFF;
+    // 二维码尺寸大小
+    private static final int QRCODE_SIZE = 300;
+    // 二维码中间LOGO宽度
+    private static final int LOGO_WIDTH = 60;
+    // 二维码中间LOGO高度
+    private static final int LOGO_HEIGHT = 60;
+    // 二维码base64编辑集
+    private static final String CHARSET = "UTF-8";
+    // 二维码图片格式
+    private static final String FORMAT_NAME = "JPG";
+    // 二维码图片格式
+    private static final String FILE_PATH_NAME = "/upload/";
+
+
+    /**
+     * 强制去除二维码白边
+     * 先根据内容生成二维码,再根据预设的大小进行缩放,预设的大小 - 缩放后的大小 = 白边大小
+     * 白边的生成与二维码的内容多少(内容越多,生成的二维码越密集)以及设置的大小有关
+     * 因为在生成二维码之后,才将白边裁掉,所以裁剪之后的二维码大小与预设的大小将不一致
+     * @param matrix
+     * @return
+     */
+    private static BitMatrix deleteWhite(BitMatrix matrix) {
+        int[] rec = matrix.getEnclosingRectangle();
+        int resWidth = rec[2] + 1;
+        int resHeight = rec[3] + 1;
+
+        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
+        resMatrix.clear();
+        for (int i = 0; i < resWidth; i++) {
+            for (int j = 0; j < resHeight; j++) {
+                if (matrix.get(i + rec[0], j + rec[1])){
+                    resMatrix.set(i, j);
+                }
+            }
+        }
+        return resMatrix;
+    }
+
+    /**
+     *  设置QR二维码参数
+     * @return
+     */
+    private static Map<EncodeHintType, Object> getDecodeHintType() {
+        Hashtable<EncodeHintType, Object> hints = new Hashtable<>(3);
+        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
+        hints.put(EncodeHintType.MARGIN, 1);
+        return hints;
+    }
+
+    /**
+     * 生成二维码
+     * @param content 扫描内容
+     * @param logoImgPath LOGO图片地址
+     * @param needCompress 是否压缩
+     * @return 二维码图片
+     * @throws Exception
+     */
+    private static BufferedImage createImage(String content,String logoImgPath,boolean needCompress) throws Exception {
+        BitMatrix bitMatrix = new MultiFormatWriter().encode(content,BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, QRCodeUtils.getDecodeHintType());
+        //强制去除白边
+        //bitMatrix = deleteWhite(bitMatrix);
+        int width = bitMatrix.getWidth();
+        int height = bitMatrix.getHeight();
+        BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                image.setRGB(x, y, bitMatrix.get(x, y) ? BLACK : WHITE);
+            }
+        }
+        if (null == logoImgPath || "".equals(logoImgPath)) {
+            return image;
+        }
+        // 插入LOGO图片
+        QRCodeUtils.insertImage(image,logoImgPath,needCompress);
+        return image;
+    }
+
+    /**
+     * 插入LOGO
+     * @param source 二维码图片
+     * @param logoImgPath LOGO图片地址
+     * @param needCompress 是否压缩
+     * @throws Exception
+     */
+    private static void insertImage(BufferedImage source,String logoImgPath,boolean needCompress) throws Exception {
+        File file = new File(logoImgPath);
+        if (!file.exists()) {
+            System.err.println(logoImgPath + "该文件不存在!");
+            return;
+        }
+
+        Image src = ImageIO.read(file);
+
+        int width = src.getWidth(null);
+        int height = src.getHeight(null);
+        // 压缩LOGO
+        if (needCompress) {
+            if (width > LOGO_WIDTH) {
+                width = LOGO_WIDTH;
+            }
+            if (height > LOGO_HEIGHT) {
+                height = LOGO_HEIGHT;
+            }
+            Image image = src.getScaledInstance(width, height,Image.SCALE_SMOOTH);
+            BufferedImage tag = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
+            Graphics g = tag.getGraphics();
+            // 绘制缩小后的LOGO图
+            g.drawImage(image,0,0,null);
+            g.dispose();
+            src = image;
+        }
+        // 插入LOGO
+        Graphics2D graph = source.createGraphics();
+        int x = (QRCODE_SIZE - width) / 2;
+        int y = (QRCODE_SIZE - height) / 2;
+        graph.drawImage(src, x, y, width, height, null);
+        Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
+        graph.setStroke(new BasicStroke(3f));
+        graph.draw(shape);
+        graph.dispose();
+    }
+
+    /**
+     * 生成二维码(内嵌LOGO)
+     *
+     * @param content 扫描内容
+     * @param logoImgPath LOGO图片地址
+     * @param needCompress 是否压缩LOGO
+     * @throws Exception
+     */
+    public static String encode(String content,String logoImgPath,boolean needCompress) throws Exception {
+        BufferedImage image = QRCodeUtils.createImage(content,logoImgPath,needCompress);
+        File file =new File(getDefaultBaseDir()+FILE_PATH_NAME);
+        //当文件夹不存在时,自动创建文件夹
+        if (!file.exists() && !file.isDirectory()) {
+            file.mkdirs();
+        }
+        ImageIO.setCacheDirectory(file);
+        String fileName = System.currentTimeMillis() + ".jpg";
+        ImageIO.write(image,FORMAT_NAME,new File(getDefaultBaseDir() + File.separator + fileName));
+        return fileName;
+    }
+
+    /**
+     * 生成二维码
+     * @param content 扫描内容
+     * @throws Exception
+     */
+    public static String encode(String content) throws Exception {
+        return QRCodeUtils.encode(content,null, false);
+    }
+
+    /**
+     * 生成二维码(内嵌LOGO,无需压缩logo图片)
+     * @param content 扫描内容
+     * @param logoImgPath LOGO图片地址
+     * @throws Exception
+     */
+    public static String encode(String content, String logoImgPath) throws Exception {
+        return QRCodeUtils.encode(content, logoImgPath, false);
+    }
+
+
+    /**
+     * 生成二维码(内嵌LOGO)
+     * @param content 扫描内容
+     * @param logoImgPath LOGO图片地址
+     * @param output 输出流
+     * @param needCompress 是否压缩LOGO
+     * @throws Exception
+     */
+    public static void encode(String content,String logoImgPath,OutputStream output,boolean needCompress) throws Exception {
+        BufferedImage image = QRCodeUtils.createImage(content,logoImgPath,needCompress);
+        ImageIO.write(image,FORMAT_NAME, output);
+    }
+
+    /**
+     * 生成二维码
+     * @param content 扫描内容
+     * @param output 输出流
+     * @throws Exception
+     */
+    public static void encode(String content, OutputStream output) throws Exception {
+        QRCodeUtils.encode(content, null, output, false);
+    }
+
+    /**
+     * 解析二维码
+     * @param file 二维码图片
+     * @return
+     * @throws Exception
+     */
+    public static String decode(File file) throws Exception {
+        BufferedImage image;
+        image = ImageIO.read(file);
+        if (Objects.isNull(image)) {
+            return null;
+        }
+        BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
+        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+        Result result;
+        Hashtable<DecodeHintType, Object> hints = new Hashtable<>(1);
+        hints.put(DecodeHintType.CHARACTER_SET,CHARSET);
+        result = new MultiFormatReader().decode(bitmap, hints);
+        String resultStr = result.getText();
+        return resultStr;
+    }
+
+    /**
+     * 解析二维码
+     * @param path 二维码图片地址
+     * @return
+     * @throws Exception
+     */
+    public static String decode(String path) throws Exception {
+        return QRCodeUtils.decode(new File(path));
+    }
+}

+ 48 - 0
mybusiness/src/main/java/com/ruoyi/business/xhn/domain/PayDomain.java

@@ -0,0 +1,48 @@
+package com.ruoyi.business.xhn.domain;
+
+/**
+ * @author wang_xy
+ * @description
+ * @Version 1.0
+ * @params
+ * @return
+ * @since 2022/11/28 13:39
+ */
+public class PayDomain {
+    private Long  timeStamp;
+    private String nonceStr;
+    private String package_;
+    private String paySign;
+
+    public Long getTimeStamp() {
+        return timeStamp;
+    }
+
+    public void setTimeStamp(Long timeStamp) {
+        this.timeStamp = timeStamp;
+    }
+
+    public String getNonceStr() {
+        return nonceStr;
+    }
+
+    public void setNonceStr(String nonceStr) {
+        this.nonceStr = nonceStr;
+    }
+
+    public String getPackage_() {
+        return package_;
+    }
+
+    public void setPackage_(String package_) {
+        this.package_ = package_;
+    }
+
+    public String getPaySign() {
+        return paySign;
+    }
+
+    public void setPaySign(String paySign) {
+        this.paySign = paySign;
+    }
+}

+ 307 - 0
mybusiness/src/main/java/com/ruoyi/business/xhn/domain/RegisteruserUnpaid.java

@@ -0,0 +1,307 @@
+package com.ruoyi.business.xhn.domain;
+
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * @author wang_xy
+ * @description
+ * @Version 1.0
+ * @params
+ * @return
+ * @since 2022/12/1 11:02
+ */
+public class RegisteruserUnpaid {
+    private static final long serialVersionUID=1L;
+
+    /** 微信openid */
+    private String openid;
+
+    /** 会员编号 */
+    private String code;
+
+    /** 身高 */
+    private String height;
+
+    /** 体重 */
+    private String weight;
+
+    /** 性别 */
+    private String sex;
+
+    /** 出生年月 */
+    private String birthDate;
+
+    /** 星座 */
+    private String constellation;
+
+    /** 是否已婚 */
+    private String isMarried;
+
+    /** 职业 */
+    private String occupation;
+
+    /** 工作性质 */
+    private String workNature;
+    private String[] workNatureArray;
+
+    /** 学历(支付后可查看) */
+    private String education;
+
+    /** 地区 */
+    private String location;
+
+    /** 理想ta所在地 */
+    private String taLocation;
+
+    /** 是否接收异地 */
+    private String longDistanceLove;
+
+    /** 性格介绍 */
+    private String character;
+
+    /** 理想型 */
+    private String idealType;
+
+    /** 不能接受性 */
+    private String noAccepted;
+
+    /** 是否脱单 */
+    private String delisting;
+
+    /** 人员头像url */
+    private String portraitUrl;
+
+    /** 是否可以免费开盲盒 */
+    private String isFree;
+
+    private String promoterEwm;
+
+    public String getOpenid() {
+        return openid;
+    }
+
+    public String getEducation() {
+        return education;
+    }
+
+    public void setEducation(String education) {
+        this.education = education;
+    }
+
+    public void setOpenid(String openid) {
+        this.openid = openid;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getHeight() {
+        return height;
+    }
+
+    public void setHeight(String height) {
+        this.height = height;
+    }
+
+    public String getWeight() {
+        return weight;
+    }
+
+    public void setWeight(String weight) {
+        this.weight = weight;
+    }
+
+    public String getSex() {
+        return sex;
+    }
+
+    public void setSex(String sex) {
+        this.sex = sex;
+    }
+
+    public String getBirthDate() {
+        return birthDate;
+    }
+
+    public void setBirthDate(String birthDate) {
+        this.birthDate = birthDate;
+    }
+
+    public String getConstellation() {
+        return constellation;
+    }
+
+    public void setConstellation(String constellation) {
+        this.constellation = constellation;
+    }
+
+    public String getIsMarried() {
+        return isMarried;
+    }
+
+    public void setIsMarried(String isMarried) {
+        this.isMarried = isMarried;
+    }
+
+    public String getOccupation() {
+        return occupation;
+    }
+
+    public void setOccupation(String occupation) {
+        this.occupation = occupation;
+    }
+
+    public String getWorkNature() {
+        return workNature;
+    }
+
+    public void setWorkNature(String workNature) {
+        this.workNature = workNature;
+    }
+
+    public String[] getWorkNatureArray() {
+        return workNatureArray;
+    }
+
+    public void setWorkNatureArray(String[] workNatureArray) {
+        this.workNatureArray = workNatureArray;
+    }
+
+    public String getLocation() {
+        return location;
+    }
+
+    public void setLocation(String location) {
+        this.location = location;
+    }
+
+    public String getTaLocation() {
+        return taLocation;
+    }
+
+    public void setTaLocation(String taLocation) {
+        this.taLocation = taLocation;
+    }
+
+    public String getLongDistanceLove() {
+        return longDistanceLove;
+    }
+
+    public void setLongDistanceLove(String longDistanceLove) {
+        this.longDistanceLove = longDistanceLove;
+    }
+
+    public String getCharacter() {
+        return character;
+    }
+
+    public void setCharacter(String character) {
+        this.character = character;
+    }
+
+    public String getIdealType() {
+        return idealType;
+    }
+
+    public void setIdealType(String idealType) {
+        this.idealType = idealType;
+    }
+
+    public String getNoAccepted() {
+        return noAccepted;
+    }
+
+    public void setNoAccepted(String noAccepted) {
+        this.noAccepted = noAccepted;
+    }
+
+    public String getDelisting() {
+        return delisting;
+    }
+
+    public void setDelisting(String delisting) {
+        this.delisting = delisting;
+    }
+
+    public String getPortraitUrl() {
+        return portraitUrl;
+    }
+
+    public void setPortraitUrl(String portraitUrl) {
+        this.portraitUrl = portraitUrl;
+    }
+
+    public String getIsFree() {
+        return isFree;
+    }
+
+    public void setIsFree(String isFree) {
+        this.isFree = isFree;
+    }
+
+    public String getPromoterEwm() {
+        return promoterEwm;
+    }
+
+    public void setPromoterEwm(String promoterEwm) {
+        this.promoterEwm = promoterEwm;
+    }
+
+    /*********************************以下为筛选字段***********************************/
+    private Integer pageNum;
+    private String startAge;
+    private String endAge;
+    private String startHeight;
+    private String endHeight;
+
+    public Integer getPageNum() {
+        return pageNum;
+    }
+
+    public void setPageNum(Integer pageNum) {
+        this.pageNum = pageNum;
+    }
+
+    public String getStartAge() {
+        return startAge;
+    }
+
+    public void setStartAge(String startAge) {
+        this.startAge = startAge;
+    }
+
+    public String getEndAge() {
+        return endAge;
+    }
+
+    public void setEndAge(String endAge) {
+        this.endAge = endAge;
+    }
+
+    public String getStartHeight() {
+        return startHeight;
+    }
+
+    public void setStartHeight(String startHeight) {
+        this.startHeight = startHeight;
+    }
+
+    public String getEndHeight() {
+        return endHeight;
+    }
+
+    public void setEndHeight(String endHeight) {
+        this.endHeight = endHeight;
+    }
+
+    /*********************************筛选字段end***********************************/
+
+}

+ 97 - 0
mybusiness/src/main/java/com/ruoyi/business/xhn/domain/XhnWxVo.java

@@ -0,0 +1,97 @@
+package com.ruoyi.business.xhn.domain;
+
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * @author wang_xy
+ * @description
+ * @Version 1.0
+ * @params
+ * @return
+ * @since 2022/11/30 15:13
+ */
+public class XhnWxVo extends BaseEntity {
+    private static final long serialVersionUID=1L;
+
+    private String openid;//微信openid
+    private String wechatName;//微信名称
+    private String wechatCode;//微信号
+    private String wechatArgot;//微信暗号
+    private String phone;//手机号
+    private String giftName;//礼物名称
+    private String giftNum;//礼物个数
+    private String integralNum;//礼物个数*礼物对应积分数=总积分数
+    private String createDate;//礼物个数*礼物对应积分数=总积分数
+
+    public String getOpenid() {
+        return openid;
+    }
+
+    public void setOpenid(String openid) {
+        this.openid = openid;
+    }
+
+    public String getWechatName() {
+        return wechatName;
+    }
+
+    public void setWechatName(String wechatName) {
+        this.wechatName = wechatName;
+    }
+
+    public String getWechatCode() {
+        return wechatCode;
+    }
+
+    public void setWechatCode(String wechatCode) {
+        this.wechatCode = wechatCode;
+    }
+
+    public String getWechatArgot() {
+        return wechatArgot;
+    }
+
+    public void setWechatArgot(String wechatArgot) {
+        this.wechatArgot = wechatArgot;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getGiftName() {
+        return giftName;
+    }
+
+    public void setGiftName(String giftName) {
+        this.giftName = giftName;
+    }
+
+    public String getGiftNum() {
+        return giftNum;
+    }
+
+    public void setGiftNum(String giftNum) {
+        this.giftNum = giftNum;
+    }
+
+    public String getIntegralNum() {
+        return integralNum;
+    }
+
+    public void setIntegralNum(String integralNum) {
+        this.integralNum = integralNum;
+    }
+
+    public String getCreateDate() {
+        return createDate;
+    }
+
+    public void setCreateDate(String createDate) {
+        this.createDate = createDate;
+    }
+}

+ 60 - 0
mybusiness/src/main/java/com/ruoyi/business/xhn/mapper/XhnWxMapper.java

@@ -0,0 +1,60 @@
+package com.ruoyi.business.xhn.mapper;
+
+import com.ruoyi.business.xhn.domain.RegisteruserUnpaid;
+import com.ruoyi.business.xhn.domain.XhnWxVo;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 礼物信息Mapper接口
+ *
+ * @author ruoyi
+ * @date 2022-11-23
+ */
+public interface XhnWxMapper {
+
+    /**
+     * 查询本人开盲盒记录
+     * @param openid   消费人id(如果只有这个代表查询记录)
+     * @param boxOpenid  盲盒人id(如果有这个代表验证是否开过此人的盲盒)
+     * @return
+     */
+    public List<XhnWxVo> getOpenBlindBoxList(@Param("openid") String openid,@Param("boxOpenid") String boxOpenid);
+
+    /**
+     * 查询本人都收到过谁的礼物
+     * @param openid
+     * @return
+     */
+    public List<XhnWxVo> getReceivedGiftList(String openid);
+
+    /**
+     * 查询本人都送过过谁礼物
+     * @param openid
+     * @return
+     */
+    public List<XhnWxVo> getGiveGiftList(String openid);
+
+    /**
+     * 获取随机一条盲盒信息
+     * @param registeruserUnpaid
+     * @return
+     */
+    public RegisteruserUnpaid getRandomBlindBox(RegisteruserUnpaid registeruserUnpaid);
+
+    /**
+     * 当然盲盒详情
+     * @param openid
+     * @return
+     */
+    public RegisteruserUnpaid findAppUserByOpenid(String openid);
+
+    /**
+     * 查询客服信息
+     * @return
+     */
+    public List<Map<String,Object>> getKefuInfo();
+
+}

+ 199 - 0
mybusiness/src/main/java/com/ruoyi/business/xhn/service/impl/RedisService.java

@@ -0,0 +1,199 @@
+package com.ruoyi.business.xhn.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.*;
+import org.springframework.stereotype.Service;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @ClassName RedisService
+ * @Description redis工具类
+ * @Author wang_xy
+ * @Date 2022/11/30
+ */
+@Service
+public class RedisService {
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+    /**
+     * 写入缓存
+     * @param key
+     * @param value
+     * @return
+     */
+    public boolean set(final String key, Object value) {
+        boolean result = false;
+        try {
+            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
+            operations.set(key, value);
+            result = true;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+    /**
+     * 写入缓存设置时效时间
+     * @param key
+     * @param value
+     * @return
+     */
+    public boolean set(final String key, Object value, Long expireTime) {
+        boolean result = false;
+        try {
+            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
+            operations.set(key, value);
+            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
+            result = true;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+    /**
+     * 批量删除对应的value
+     * @param keys
+     */
+    public void remove(final String... keys) {
+        for (String key : keys) {
+            remove(key);
+        }
+    }
+
+    /**
+     * 批量删除key
+     * @param pattern
+     */
+    public void removePattern(final String pattern) {
+        Set<Serializable> keys = redisTemplate.keys(pattern);
+        if (keys.size() > 0)
+            redisTemplate.delete(keys);
+    }
+    /**
+     * 删除对应的value
+     * @param key
+     */
+    public void remove(final String key) {
+        if (exists(key)) {
+            redisTemplate.delete(key);
+        }
+    }
+    /**
+     * 判断缓存中是否有对应的value
+     * @param key
+     * @return
+     */
+    public boolean exists(final String key) {
+        return redisTemplate.hasKey(key);
+    }
+    /**
+     * 读取缓存
+     * @param key
+     * @return
+     */
+    public Object get(final String key) {
+        Object result = null;
+        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
+        result = operations.get(key);
+        return result;
+    }
+    /**
+     * 哈希 添加
+     * @param key
+     * @param hashKey
+     * @param value
+     */
+    public void hmSet(String key, Object hashKey, Object value){
+        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
+        hash.put(key,hashKey,value);
+    }
+
+    /**
+     * 哈希获取数据
+     * @param key
+     * @param hashKey
+     * @return
+     */
+    public Object hmGet(String key, Object hashKey){
+        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
+        return hash.get(key,hashKey);
+    }
+
+    /**
+     * 列表添加
+     * @param k
+     * @param v
+     */
+    public void lPush(String k,Object v){
+        ListOperations<String, Object> list = redisTemplate.opsForList();
+        list.rightPush(k,v);
+    }
+
+    /**
+     * 列表获取
+     * @param k
+     * @param l
+     * @param l1
+     * @return
+     */
+    public List<Object> lRange(String k, long l, long l1){
+        ListOperations<String, Object> list = redisTemplate.opsForList();
+        return list.range(k,l,l1);
+    }
+
+    /**
+     * 集合添加
+     * @param key
+     * @param value
+     */
+    public void add(String key,Object value){
+        SetOperations<String, Object> set = redisTemplate.opsForSet();
+        set.add(key,value);
+    }
+
+    /**
+     * 集合获取
+     * @param key
+     * @return
+     */
+    public Set<Object> setMembers(String key){
+        SetOperations<String, Object> set = redisTemplate.opsForSet();
+        return set.members(key);
+    }
+
+    /**
+     * 有序集合添加
+     * @param key
+     * @param value
+     * @param scoure
+     */
+    public void zAdd(String key,Object value,double scoure){
+        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
+        zset.add(key,value,scoure);
+    }
+
+    /**
+     * 有序集合获取
+     * @param key
+     * @param scoure
+     * @param scoure1
+     * @return
+     */
+    public Set<Object> rangeByScore(String key,double scoure,double scoure1){
+        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
+        return zset.rangeByScore(key, scoure, scoure1);
+    }
+
+    /**
+     * 获取所有的KEY
+     */
+    public Set keys(String p){
+		Set keys = redisTemplate.keys(p);
+		return keys;
+	}
+}

+ 61 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/activityMiddle/domain/XhnActivityMiddle.java

@@ -0,0 +1,61 @@
+package com.ruoyi.business.xhnnotsingle.activityMiddle.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 小红娘活动报名人员中间关联对象 xhn_activity_middle
+ *
+ * @author ruoyi
+ * @date 2022-12-08
+ */
+public class XhnActivityMiddle extends BaseEntity
+        {
+private static final long serialVersionUID=1L;
+
+    /** $column.columnComment */
+    private String id;
+
+    /** 活动id */
+            @Excel(name = "活动id")
+    private String hdId;
+
+    /** 人员openid */
+            @Excel(name = "人员openid")
+    private String openid;
+
+            public String getId() {
+                return id;
+            }
+
+            public void setId(String id) {
+                this.id = id;
+            }
+
+            public String getHdId() {
+                return hdId;
+            }
+
+            public void setHdId(String hdId) {
+                this.hdId = hdId;
+            }
+
+            public String getOpenid() {
+                return openid;
+            }
+
+            public void setOpenid(String openid) {
+                this.openid = openid;
+            }
+
+            @Override
+public String toString(){
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id" ,getId())
+            .append("hdId" ,getHdId())
+            .append("openid" ,getOpenid())
+        .toString();
+        }
+        }

+ 61 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/activityMiddle/mapper/XhnActivityMiddleMapper.java

@@ -0,0 +1,61 @@
+package com.ruoyi.business.xhnnotsingle.activityMiddle.mapper;
+
+import java.util.List;
+
+import com.ruoyi.business.xhnnotsingle.activityMiddle.domain.XhnActivityMiddle;
+
+/**
+ * 小红娘活动报名人员中间关联Mapper接口
+ *
+ * @author ruoyi
+ * @date 2022-12-08
+ */
+public interface XhnActivityMiddleMapper {
+    /**
+     * 查询小红娘活动报名人员中间关联
+     *
+     * @param id 小红娘活动报名人员中间关联主键
+     * @return 小红娘活动报名人员中间关联
+     */
+    public XhnActivityMiddle selectXhnActivityMiddleById(String id);
+
+    /**
+     * 查询小红娘活动报名人员中间关联列表
+     *
+     * @param xhnActivityMiddle 小红娘活动报名人员中间关联
+     * @return 小红娘活动报名人员中间关联集合
+     */
+    public List<XhnActivityMiddle> selectXhnActivityMiddleList(XhnActivityMiddle xhnActivityMiddle);
+
+    /**
+     * 新增小红娘活动报名人员中间关联
+     *
+     * @param xhnActivityMiddle 小红娘活动报名人员中间关联
+     * @return 结果
+     */
+    public int insertXhnActivityMiddle(XhnActivityMiddle xhnActivityMiddle);
+
+    /**
+     * 修改小红娘活动报名人员中间关联
+     *
+     * @param xhnActivityMiddle 小红娘活动报名人员中间关联
+     * @return 结果
+     */
+    public int updateXhnActivityMiddle(XhnActivityMiddle xhnActivityMiddle);
+
+    /**
+     * 删除小红娘活动报名人员中间关联
+     *
+     * @param id 小红娘活动报名人员中间关联主键
+     * @return 结果
+     */
+    public int deleteXhnActivityMiddleById(String id);
+
+    /**
+     * 批量删除小红娘活动报名人员中间关联
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteXhnActivityMiddleByIds(String[] ids);
+}

+ 136 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/aliyunSms/domain/XhnAliyunSms.java

@@ -0,0 +1,136 @@
+package com.ruoyi.business.xhnnotsingle.aliyunSms.domain;
+
+import java.util.Date;
+
+public class XhnAliyunSms {
+
+    private String id;
+
+    private String code;
+
+    private String bizId;
+
+    private String requestId;
+
+    private String message;
+
+    private Integer count;
+
+    private String signName;
+
+    private String tempCode;
+
+    private String dataJson;
+
+    private String createBy;
+
+    private Date createDate;
+
+    private String openid;
+
+    private String phone;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id == null ? null : id.trim();
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code == null ? null : code.trim();
+    }
+
+    public String getBizId() {
+        return bizId;
+    }
+
+    public void setBizId(String bizId) {
+        this.bizId = bizId == null ? null : bizId.trim();
+    }
+
+    public String getRequestId() {
+        return requestId;
+    }
+
+    public void setRequestId(String requestId) {
+        this.requestId = requestId == null ? null : requestId.trim();
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message == null ? null : message.trim();
+    }
+
+    public Integer getCount() {
+        return count;
+    }
+
+    public void setCount(Integer count) {
+        this.count = count;
+    }
+
+    public String getSignName() {
+        return signName;
+    }
+
+    public void setSignName(String signName) {
+        this.signName = signName == null ? null : signName.trim();
+    }
+
+    public String getTempCode() {
+        return tempCode;
+    }
+
+    public void setTempCode(String tempCode) {
+        this.tempCode = tempCode == null ? null : tempCode.trim();
+    }
+
+    public String getDataJson() {
+        return dataJson;
+    }
+
+    public void setDataJson(String dataJson) {
+        this.dataJson = dataJson == null ? null : dataJson.trim();
+    }
+
+    public String getCreateBy() {
+        return createBy;
+    }
+
+    public void setCreateBy(String createBy) {
+        this.createBy = createBy == null ? null : createBy.trim();
+    }
+
+    public Date getCreateDate() {
+        return createDate;
+    }
+
+    public void setCreateDate(Date createDate) {
+        this.createDate = createDate;
+    }
+
+    public String getOpenid() {
+        return openid;
+    }
+
+    public void setOpenid(String openid) {
+        this.openid = openid;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone == null ? null : phone.trim();
+    }
+}

+ 14 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/aliyunSms/mapper/XhnAliyunSmsMapper.java

@@ -0,0 +1,14 @@
+package com.ruoyi.business.xhnnotsingle.aliyunSms.mapper;
+
+import com.ruoyi.business.xhnnotsingle.aliyunSms.domain.XhnAliyunSms;
+
+/**
+ * 小红娘活动管理Mapper接口
+ *
+ * @author ruoyi
+ * @date 2022-11-23
+ */
+public interface XhnAliyunSmsMapper {
+
+    public void insertXhnAAliyunSms(XhnAliyunSms xhnAliyunSms);
+}

+ 21 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/aliyunSms/service/IXhnAliyunSmsService.java

@@ -0,0 +1,21 @@
+package com.ruoyi.business.xhnnotsingle.aliyunSms.service;
+
+import com.ruoyi.business.xhnnotsingle.aliyunSms.domain.XhnAliyunSms;
+
+/**
+ * 小红娘短信验证码信息记录
+ *
+ * @author ruoyi
+ * @date 2022-11-30
+ */
+public interface IXhnAliyunSmsService {
+
+    /**
+     * 添加短信验证码发送记录
+     *
+     * @param xhnAliyunSms
+     * @return 结果
+     */
+    public void insertXhnAAliyunSms(XhnAliyunSms xhnAliyunSms);
+
+}

+ 25 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/aliyunSms/service/impl/XhnAliyunSmsServiceImpl.java

@@ -0,0 +1,25 @@
+package com.ruoyi.business.xhnnotsingle.aliyunSms.service.impl;
+
+import com.ruoyi.business.xhnnotsingle.activityInfo.mapper.XhnActivityInfoMapper;
+import com.ruoyi.business.xhnnotsingle.aliyunSms.domain.XhnAliyunSms;
+import com.ruoyi.business.xhnnotsingle.aliyunSms.mapper.XhnAliyunSmsMapper;
+import com.ruoyi.business.xhnnotsingle.aliyunSms.service.IXhnAliyunSmsService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 小红娘活动管理Service业务层处理
+ *
+ * @author ruoyi
+ * @date 2022-11-30
+ */
+@Service
+public class XhnAliyunSmsServiceImpl implements IXhnAliyunSmsService {
+    @Autowired
+    private XhnAliyunSmsMapper xhnAliyunSmsMapper;
+
+    @Override
+    public void insertXhnAAliyunSms(XhnAliyunSms xhnAliyunSms) {
+        xhnAliyunSmsMapper.insertXhnAAliyunSms(xhnAliyunSms);
+    }
+}

+ 120 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/controller/XhnConsumeMiddleController.java

@@ -0,0 +1,120 @@
+package com.ruoyi.business.xhnnotsingle.consumemiddle.controller;
+
+import java.util.List;
+
+import com.ruoyi.business.xhnnotsingle.consumemiddle.domain.XhnConsumeMiddle;
+import com.ruoyi.business.xhnnotsingle.consumemiddle.service.IXhnConsumeMiddleService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 人员消费对应人员的中间关联Controller
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+@Controller
+@RequestMapping("/system/middle")
+public class XhnConsumeMiddleController extends BaseController {
+    private String prefix = "system/middle" ;
+
+    @Autowired
+    private IXhnConsumeMiddleService xhnConsumeMiddleService;
+
+    @RequiresPermissions("system:middle:view")
+    @GetMapping()
+    public String middle() {
+        return prefix + "/middle" ;
+    }
+
+        /**
+         * 查询人员消费对应人员的中间关联列表
+         */
+        @RequiresPermissions("system:middle:list")
+        @PostMapping("/list")
+        @ResponseBody
+        public TableDataInfo list(XhnConsumeMiddle xhnConsumeMiddle) {
+            startPage();
+            List<XhnConsumeMiddle> list = xhnConsumeMiddleService.selectXhnConsumeMiddleList(xhnConsumeMiddle);
+            return getDataTable(list);
+        }
+
+    /**
+     * 导出人员消费对应人员的中间关联列表
+     */
+    @RequiresPermissions("system:middle:export")
+    @Log(title = "人员消费对应人员的中间关联" , businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @ResponseBody
+    public AjaxResult export(XhnConsumeMiddle xhnConsumeMiddle) {
+        List<XhnConsumeMiddle> list = xhnConsumeMiddleService.selectXhnConsumeMiddleList(xhnConsumeMiddle);
+        ExcelUtil<XhnConsumeMiddle> util = new ExcelUtil<XhnConsumeMiddle>(XhnConsumeMiddle. class);
+        return util.exportExcel(list, "人员消费对应人员的中间关联数据");
+    }
+
+        /**
+         * 新增人员消费对应人员的中间关联
+         */
+        @GetMapping("/add")
+        public String add() {
+            return prefix + "/add" ;
+        }
+
+    /**
+     * 新增保存人员消费对应人员的中间关联
+     */
+    @RequiresPermissions("system:middle:add")
+    @Log(title = "人员消费对应人员的中间关联" , businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    @ResponseBody
+    public AjaxResult addSave(XhnConsumeMiddle xhnConsumeMiddle) {
+        return toAjax(xhnConsumeMiddleService.insertXhnConsumeMiddle(xhnConsumeMiddle));
+    }
+
+    /**
+     * 修改人员消费对应人员的中间关联
+     */
+    @RequiresPermissions("system:middle:edit")
+    @GetMapping("/edit/{id}")
+    public String edit(@PathVariable("id") String id, ModelMap mmap) {
+        XhnConsumeMiddle xhnConsumeMiddle =
+            xhnConsumeMiddleService.selectXhnConsumeMiddleById(id);
+        mmap.put("xhnConsumeMiddle" , xhnConsumeMiddle);
+        return prefix + "/edit" ;
+    }
+
+    /**
+     * 修改保存人员消费对应人员的中间关联
+     */
+    @RequiresPermissions("system:middle:edit")
+    @Log(title = "人员消费对应人员的中间关联" , businessType = BusinessType.UPDATE)
+    @PostMapping("/edit")
+    @ResponseBody
+    public AjaxResult editSave(XhnConsumeMiddle xhnConsumeMiddle) {
+        return toAjax(xhnConsumeMiddleService.updateXhnConsumeMiddle(xhnConsumeMiddle));
+    }
+
+        /**
+         * 删除人员消费对应人员的中间关联
+         */
+        @RequiresPermissions("system:middle:remove")
+        @Log(title = "人员消费对应人员的中间关联" , businessType = BusinessType.DELETE)
+        @PostMapping("/remove")
+        @ResponseBody
+        public AjaxResult remove(String ids) {
+            return toAjax(xhnConsumeMiddleService.deleteXhnConsumeMiddleByIds(ids));
+        }
+}

+ 115 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/domain/XhnConsumeMiddle.java

@@ -0,0 +1,115 @@
+package com.ruoyi.business.xhnnotsingle.consumemiddle.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 人员消费对应人员的中间关联对象 xhn_consume_middle
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+public class XhnConsumeMiddle extends BaseEntity
+        {
+private static final long serialVersionUID=1L;
+
+    /** $column.columnComment */
+    private String id;
+
+    /** 消费人的openid */
+            @Excel(name = "消费人的openid")
+    private String consumeOpenid;
+
+    /** 获得人的openid */
+            @Excel(name = "获得人的openid")
+    private String gainOpenid;
+
+    /** 类型  1盲盒  2礼物(只有盲盒和礼物是关系到两个人的) */
+            @Excel(name = "类型  1盲盒  2礼物" , readConverterExp = "只=有盲盒和礼物是关系到两个人的")
+    private String type;
+
+    /** 礼物id */
+            @Excel(name = "礼物id")
+    private String giftId;
+
+    /** 礼物数量 */
+            @Excel(name = "礼物数量")
+    private Integer giftNum;
+
+    private String createDate;
+
+    public void setId(String id)
+            {
+            this.id = id;
+            }
+
+    public String getId()
+            {
+            return id;
+            }
+    public void setConsumeOpenid(String consumeOpenid)
+            {
+            this.consumeOpenid = consumeOpenid;
+            }
+
+    public String getConsumeOpenid()
+            {
+            return consumeOpenid;
+            }
+    public void setGainOpenid(String gainOpenid)
+            {
+            this.gainOpenid = gainOpenid;
+            }
+
+    public String getGainOpenid()
+            {
+            return gainOpenid;
+            }
+    public void setType(String type)
+            {
+            this.type = type;
+            }
+
+    public String getType()
+            {
+            return type;
+            }
+
+            public String getGiftId() {
+                return giftId;
+            }
+
+            public void setGiftId(String giftId) {
+                this.giftId = giftId;
+            }
+
+            public Integer getGiftNum() {
+                return giftNum;
+            }
+
+            public void setGiftNum(Integer giftNum) {
+                this.giftNum = giftNum;
+            }
+
+            public String getCreateDate() {
+                return createDate;
+            }
+
+            public void setCreateDate(String createDate) {
+                this.createDate = createDate;
+            }
+
+            @Override
+public String toString(){
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id" ,getId())
+            .append("consumeOpenid" ,getConsumeOpenid())
+            .append("gainOpenid" ,getGainOpenid())
+            .append("type" ,getType())
+            .append("giftId" ,getGiftId())
+            .append("giftNum" ,getGiftNum())
+        .toString();
+        }
+        }

+ 62 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/mapper/XhnConsumeMiddleMapper.java

@@ -0,0 +1,62 @@
+package com.ruoyi.business.xhnnotsingle.consumemiddle.mapper;
+
+import com.ruoyi.business.xhnnotsingle.consumemiddle.domain.XhnConsumeMiddle;
+
+import java.util.List;
+
+
+/**
+ * 人员消费对应人员的中间关联Mapper接口
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+public interface XhnConsumeMiddleMapper {
+    /**
+     * 查询人员消费对应人员的中间关联
+     *
+     * @param id 人员消费对应人员的中间关联主键
+     * @return 人员消费对应人员的中间关联
+     */
+    public XhnConsumeMiddle selectXhnConsumeMiddleById(String id);
+
+    /**
+     * 查询人员消费对应人员的中间关联列表
+     *
+     * @param xhnConsumeMiddle 人员消费对应人员的中间关联
+     * @return 人员消费对应人员的中间关联集合
+     */
+    public List<XhnConsumeMiddle> selectXhnConsumeMiddleList(XhnConsumeMiddle xhnConsumeMiddle);
+
+    /**
+     * 新增人员消费对应人员的中间关联
+     *
+     * @param xhnConsumeMiddle 人员消费对应人员的中间关联
+     * @return 结果
+     */
+    public int insertXhnConsumeMiddle(XhnConsumeMiddle xhnConsumeMiddle);
+
+    /**
+     * 修改人员消费对应人员的中间关联
+     *
+     * @param xhnConsumeMiddle 人员消费对应人员的中间关联
+     * @return 结果
+     */
+    public int updateXhnConsumeMiddle(XhnConsumeMiddle xhnConsumeMiddle);
+
+    /**
+     * 删除人员消费对应人员的中间关联
+     *
+     * @param id 人员消费对应人员的中间关联主键
+     * @return 结果
+     */
+    public int deleteXhnConsumeMiddleById(String id);
+
+    /**
+     * 批量删除人员消费对应人员的中间关联
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteXhnConsumeMiddleByIds(String[] ids);
+}

+ 62 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/service/IXhnConsumeMiddleService.java

@@ -0,0 +1,62 @@
+package com.ruoyi.business.xhnnotsingle.consumemiddle.service;
+
+import com.ruoyi.business.xhnnotsingle.consumemiddle.domain.XhnConsumeMiddle;
+
+import java.util.List;
+
+
+/**
+ * 人员消费对应人员的中间关联Service接口
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+public interface IXhnConsumeMiddleService {
+    /**
+     * 查询人员消费对应人员的中间关联
+     *
+     * @param id 人员消费对应人员的中间关联主键
+     * @return 人员消费对应人员的中间关联
+     */
+    public XhnConsumeMiddle selectXhnConsumeMiddleById(String id);
+
+    /**
+     * 查询人员消费对应人员的中间关联列表
+     *
+     * @param xhnConsumeMiddle 人员消费对应人员的中间关联
+     * @return 人员消费对应人员的中间关联集合
+     */
+    public List<XhnConsumeMiddle> selectXhnConsumeMiddleList(XhnConsumeMiddle xhnConsumeMiddle);
+
+    /**
+     * 新增人员消费对应人员的中间关联
+     *
+     * @param xhnConsumeMiddle 人员消费对应人员的中间关联
+     * @return 结果
+     */
+    public int insertXhnConsumeMiddle(XhnConsumeMiddle xhnConsumeMiddle);
+
+    /**
+     * 修改人员消费对应人员的中间关联
+     *
+     * @param xhnConsumeMiddle 人员消费对应人员的中间关联
+     * @return 结果
+     */
+    public int updateXhnConsumeMiddle(XhnConsumeMiddle xhnConsumeMiddle);
+
+    /**
+     * 批量删除人员消费对应人员的中间关联
+     *
+     * @param ids 需要删除的人员消费对应人员的中间关联主键集合
+     * @return 结果
+     */
+    public int deleteXhnConsumeMiddleByIds(String ids);
+
+    /**
+     * 删除人员消费对应人员的中间关联信息
+     *
+     * @param id 人员消费对应人员的中间关联主键
+     * @return 结果
+     */
+    public int deleteXhnConsumeMiddleById(String id);
+}

+ 88 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/consumemiddle/service/impl/XhnConsumeMiddleServiceImpl.java

@@ -0,0 +1,88 @@
+package com.ruoyi.business.xhnnotsingle.consumemiddle.service.impl;
+
+import java.util.List;
+
+import com.ruoyi.business.xhnnotsingle.consumemiddle.domain.XhnConsumeMiddle;
+import com.ruoyi.business.xhnnotsingle.consumemiddle.mapper.XhnConsumeMiddleMapper;
+import com.ruoyi.business.xhnnotsingle.consumemiddle.service.IXhnConsumeMiddleService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.core.text.Convert;
+
+/**
+ * 人员消费对应人员的中间关联Service业务层处理
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+@Service
+public class XhnConsumeMiddleServiceImpl implements IXhnConsumeMiddleService {
+    @Autowired
+    private XhnConsumeMiddleMapper xhnConsumeMiddleMapper;
+
+    /**
+     * 查询人员消费对应人员的中间关联
+     *
+     * @param id 人员消费对应人员的中间关联主键
+     * @return 人员消费对应人员的中间关联
+     */
+    @Override
+    public XhnConsumeMiddle selectXhnConsumeMiddleById(String id) {
+        return xhnConsumeMiddleMapper.selectXhnConsumeMiddleById(id);
+    }
+
+    /**
+     * 查询人员消费对应人员的中间关联列表
+     *
+     * @param xhnConsumeMiddle 人员消费对应人员的中间关联
+     * @return 人员消费对应人员的中间关联
+     */
+    @Override
+    public List<XhnConsumeMiddle> selectXhnConsumeMiddleList(XhnConsumeMiddle xhnConsumeMiddle) {
+        return xhnConsumeMiddleMapper.selectXhnConsumeMiddleList(xhnConsumeMiddle);
+    }
+
+    /**
+     * 新增人员消费对应人员的中间关联
+     *
+     * @param xhnConsumeMiddle 人员消费对应人员的中间关联
+     * @return 结果
+     */
+    @Override
+    public int insertXhnConsumeMiddle(XhnConsumeMiddle xhnConsumeMiddle) {
+            return xhnConsumeMiddleMapper.insertXhnConsumeMiddle(xhnConsumeMiddle);
+    }
+
+    /**
+     * 修改人员消费对应人员的中间关联
+     *
+     * @param xhnConsumeMiddle 人员消费对应人员的中间关联
+     * @return 结果
+     */
+    @Override
+    public int updateXhnConsumeMiddle(XhnConsumeMiddle xhnConsumeMiddle) {
+        return xhnConsumeMiddleMapper.updateXhnConsumeMiddle(xhnConsumeMiddle);
+    }
+
+    /**
+     * 批量删除人员消费对应人员的中间关联
+     *
+     * @param ids 需要删除的人员消费对应人员的中间关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteXhnConsumeMiddleByIds(String ids) {
+        return xhnConsumeMiddleMapper.deleteXhnConsumeMiddleByIds(Convert.toStrArray(ids));
+    }
+
+    /**
+     * 删除人员消费对应人员的中间关联信息
+     *
+     * @param id 人员消费对应人员的中间关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteXhnConsumeMiddleById(String id) {
+        return xhnConsumeMiddleMapper.deleteXhnConsumeMiddleById(id);
+    }
+}

+ 120 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/controller/XhnIntegralController.java

@@ -0,0 +1,120 @@
+package com.ruoyi.business.xhnnotsingle.integral.controller;
+
+import java.util.List;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.business.xhnnotsingle.integral.domain.XhnIntegral;
+import com.ruoyi.business.xhnnotsingle.integral.service.IXhnIntegralService;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 小红娘金额、积分充值消费记录Controller
+ *
+ * @author ruoyi
+ * @date 2022-11-30
+ */
+@Controller
+@RequestMapping("/xhnnotsingle/integral")
+public class XhnIntegralController extends BaseController {
+    private String prefix = "xhnnotsingle/integral" ;
+
+    @Autowired
+    private IXhnIntegralService xhnIntegralService;
+
+    @RequiresPermissions("xhnnotsingle:integral:view")
+    @GetMapping()
+    public String integral() {
+        return prefix + "/integral" ;
+    }
+
+        /**
+         * 查询小红娘金额、积分充值消费记录列表
+         */
+        @RequiresPermissions("xhnnotsingle:integral:list")
+        @PostMapping("/list")
+        @ResponseBody
+        public TableDataInfo list(XhnIntegral xhnIntegral) {
+            startPage();
+            List<XhnIntegral> list = xhnIntegralService.selectXhnIntegralList(xhnIntegral);
+            return getDataTable(list);
+        }
+
+    /**
+     * 导出小红娘金额、积分充值消费记录列表
+     */
+    @RequiresPermissions("xhnnotsingle:integral:export")
+    @Log(title = "小红娘金额、积分充值消费记录" , businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @ResponseBody
+    public AjaxResult export(XhnIntegral xhnIntegral) {
+        List<XhnIntegral> list = xhnIntegralService.selectXhnIntegralList(xhnIntegral);
+        ExcelUtil<XhnIntegral> util = new ExcelUtil<XhnIntegral>(XhnIntegral. class);
+        return util.exportExcel(list, "小红娘金额、积分充值消费记录数据");
+    }
+
+        /**
+         * 新增小红娘金额、积分充值消费记录
+         */
+        @GetMapping("/add")
+        public String add() {
+            return prefix + "/add" ;
+        }
+
+    /**
+     * 新增保存小红娘金额、积分充值消费记录
+     */
+    @RequiresPermissions("xhnnotsingle:integral:add")
+    @Log(title = "小红娘金额、积分充值消费记录" , businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    @ResponseBody
+    public AjaxResult addSave(XhnIntegral xhnIntegral) {
+        return toAjax(xhnIntegralService.insertXhnIntegral(xhnIntegral));
+    }
+
+    /**
+     * 修改小红娘金额、积分充值消费记录
+     */
+    @RequiresPermissions("xhnnotsingle:integral:edit")
+    @GetMapping("/edit/{id}")
+    public String edit(@PathVariable("id") String id, ModelMap mmap) {
+        XhnIntegral xhnIntegral =
+            xhnIntegralService.selectXhnIntegralById(id);
+        mmap.put("xhnIntegral" , xhnIntegral);
+        return prefix + "/edit" ;
+    }
+
+    /**
+     * 修改保存小红娘金额、积分充值消费记录
+     */
+    @RequiresPermissions("xhnnotsingle:integral:edit")
+    @Log(title = "小红娘金额、积分充值消费记录" , businessType = BusinessType.UPDATE)
+    @PostMapping("/edit")
+    @ResponseBody
+    public AjaxResult editSave(XhnIntegral xhnIntegral) {
+        return toAjax(xhnIntegralService.updateXhnIntegral(xhnIntegral));
+    }
+
+        /**
+         * 删除小红娘金额、积分充值消费记录
+         */
+        @RequiresPermissions("xhnnotsingle:integral:remove")
+        @Log(title = "小红娘金额、积分充值消费记录" , businessType = BusinessType.DELETE)
+        @PostMapping("/remove")
+        @ResponseBody
+        public AjaxResult remove(String ids) {
+            return toAjax(xhnIntegralService.deleteXhnIntegralByIds(ids));
+        }
+}

+ 107 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/domain/XhnIntegral.java

@@ -0,0 +1,107 @@
+package com.ruoyi.business.xhnnotsingle.integral.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 小红娘金额、积分充值消费记录对象 xhn_integral
+ *
+ * @author ruoyi
+ * @date 2022-11-30
+ */
+public class XhnIntegral extends BaseEntity
+        {
+private static final long serialVersionUID=1L;
+
+    /** id */
+    private String id;
+
+    /** 金额||积分数 */
+            @Excel(name = "金额||积分数")
+    private Double money;
+
+    /** 获得||消费时间 */
+            @Excel(name = "获得||消费时间")
+    private String consumeDate;
+
+    /** 类型  1金额  2积分 */
+            @Excel(name = "类型  1金额  2积分")
+    private String orderType;
+
+    /** 会员表的微信openid */
+            @Excel(name = "会员表的微信openid")
+    private String userOpenid;
+
+    /** 用途汉字描述 */
+            @Excel(name = "用途汉字描述")
+    private String purpose;
+
+    public void setId(String id)
+            {
+            this.id = id;
+            }
+
+    public String getId()
+            {
+            return id;
+            }
+    public void setMoney(Double money)
+            {
+            this.money = money;
+            }
+
+    public Double getMoney()
+            {
+            return money;
+            }
+    public void setConsumeDate(String consumeDate)
+            {
+            this.consumeDate = consumeDate;
+            }
+
+    public String getConsumeDate()
+            {
+            return consumeDate;
+            }
+    public void setOrderType(String orderType)
+            {
+            this.orderType = orderType;
+            }
+
+    public String getOrderType()
+            {
+            return orderType;
+            }
+    public void setUserOpenid(String userOpenid)
+            {
+            this.userOpenid = userOpenid;
+            }
+
+    public String getUserOpenid()
+            {
+            return userOpenid;
+            }
+    public void setPurpose(String purpose)
+            {
+            this.purpose = purpose;
+            }
+
+    public String getPurpose()
+            {
+            return purpose;
+            }
+
+@Override
+public String toString(){
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id" ,getId())
+            .append("money" ,getMoney())
+            .append("consumeDate" ,getConsumeDate())
+            .append("orderType" ,getOrderType())
+            .append("userOpenid" ,getUserOpenid())
+            .append("purpose" ,getPurpose())
+        .toString();
+        }
+        }

+ 61 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/mapper/XhnIntegralMapper.java

@@ -0,0 +1,61 @@
+package com.ruoyi.business.xhnnotsingle.integral.mapper;
+
+import java.util.List;
+
+import com.ruoyi.business.xhnnotsingle.integral.domain.XhnIntegral;
+
+/**
+ * 小红娘金额、积分充值消费记录Mapper接口
+ *
+ * @author ruoyi
+ * @date 2022-11-30
+ */
+public interface XhnIntegralMapper {
+    /**
+     * 查询小红娘金额、积分充值消费记录
+     *
+     * @param id 小红娘金额、积分充值消费记录主键
+     * @return 小红娘金额、积分充值消费记录
+     */
+    public XhnIntegral selectXhnIntegralById(String id);
+
+    /**
+     * 查询小红娘金额、积分充值消费记录列表
+     *
+     * @param xhnIntegral 小红娘金额、积分充值消费记录
+     * @return 小红娘金额、积分充值消费记录集合
+     */
+    public List<XhnIntegral> selectXhnIntegralList(XhnIntegral xhnIntegral);
+
+    /**
+     * 新增小红娘金额、积分充值消费记录
+     *
+     * @param xhnIntegral 小红娘金额、积分充值消费记录
+     * @return 结果
+     */
+    public int insertXhnIntegral(XhnIntegral xhnIntegral);
+
+    /**
+     * 修改小红娘金额、积分充值消费记录
+     *
+     * @param xhnIntegral 小红娘金额、积分充值消费记录
+     * @return 结果
+     */
+    public int updateXhnIntegral(XhnIntegral xhnIntegral);
+
+    /**
+     * 删除小红娘金额、积分充值消费记录
+     *
+     * @param id 小红娘金额、积分充值消费记录主键
+     * @return 结果
+     */
+    public int deleteXhnIntegralById(String id);
+
+    /**
+     * 批量删除小红娘金额、积分充值消费记录
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteXhnIntegralByIds(String[] ids);
+}

+ 61 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/service/IXhnIntegralService.java

@@ -0,0 +1,61 @@
+package com.ruoyi.business.xhnnotsingle.integral.service;
+
+import java.util.List;
+
+import com.ruoyi.business.xhnnotsingle.integral.domain.XhnIntegral;
+
+/**
+ * 小红娘金额、积分充值消费记录Service接口
+ *
+ * @author ruoyi
+ * @date 2022-11-30
+ */
+public interface IXhnIntegralService {
+    /**
+     * 查询小红娘金额、积分充值消费记录
+     *
+     * @param id 小红娘金额、积分充值消费记录主键
+     * @return 小红娘金额、积分充值消费记录
+     */
+    public XhnIntegral selectXhnIntegralById(String id);
+
+    /**
+     * 查询小红娘金额、积分充值消费记录列表
+     *
+     * @param xhnIntegral 小红娘金额、积分充值消费记录
+     * @return 小红娘金额、积分充值消费记录集合
+     */
+    public List<XhnIntegral> selectXhnIntegralList(XhnIntegral xhnIntegral);
+
+    /**
+     * 新增小红娘金额、积分充值消费记录
+     *
+     * @param xhnIntegral 小红娘金额、积分充值消费记录
+     * @return 结果
+     */
+    public int insertXhnIntegral(XhnIntegral xhnIntegral);
+
+    /**
+     * 修改小红娘金额、积分充值消费记录
+     *
+     * @param xhnIntegral 小红娘金额、积分充值消费记录
+     * @return 结果
+     */
+    public int updateXhnIntegral(XhnIntegral xhnIntegral);
+
+    /**
+     * 批量删除小红娘金额、积分充值消费记录
+     *
+     * @param ids 需要删除的小红娘金额、积分充值消费记录主键集合
+     * @return 结果
+     */
+    public int deleteXhnIntegralByIds(String ids);
+
+    /**
+     * 删除小红娘金额、积分充值消费记录信息
+     *
+     * @param id 小红娘金额、积分充值消费记录主键
+     * @return 结果
+     */
+    public int deleteXhnIntegralById(String id);
+}

+ 87 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/integral/service/impl/XhnIntegralServiceImpl.java

@@ -0,0 +1,87 @@
+package com.ruoyi.business.xhnnotsingle.integral.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.business.xhnnotsingle.integral.mapper.XhnIntegralMapper;
+import com.ruoyi.business.xhnnotsingle.integral.domain.XhnIntegral;
+import com.ruoyi.business.xhnnotsingle.integral.service.IXhnIntegralService;
+import com.ruoyi.common.core.text.Convert;
+
+/**
+ * 小红娘金额、积分充值消费记录Service业务层处理
+ *
+ * @author ruoyi
+ * @date 2022-11-30
+ */
+@Service
+public class XhnIntegralServiceImpl implements IXhnIntegralService {
+    @Autowired
+    private XhnIntegralMapper xhnIntegralMapper;
+
+    /**
+     * 查询小红娘金额、积分充值消费记录
+     *
+     * @param id 小红娘金额、积分充值消费记录主键
+     * @return 小红娘金额、积分充值消费记录
+     */
+    @Override
+    public XhnIntegral selectXhnIntegralById(String id) {
+        return xhnIntegralMapper.selectXhnIntegralById(id);
+    }
+
+    /**
+     * 查询小红娘金额、积分充值消费记录列表
+     *
+     * @param xhnIntegral 小红娘金额、积分充值消费记录
+     * @return 小红娘金额、积分充值消费记录
+     */
+    @Override
+    public List<XhnIntegral> selectXhnIntegralList(XhnIntegral xhnIntegral) {
+        return xhnIntegralMapper.selectXhnIntegralList(xhnIntegral);
+    }
+
+    /**
+     * 新增小红娘金额、积分充值消费记录
+     *
+     * @param xhnIntegral 小红娘金额、积分充值消费记录
+     * @return 结果
+     */
+    @Override
+    public int insertXhnIntegral(XhnIntegral xhnIntegral) {
+            return xhnIntegralMapper.insertXhnIntegral(xhnIntegral);
+    }
+
+    /**
+     * 修改小红娘金额、积分充值消费记录
+     *
+     * @param xhnIntegral 小红娘金额、积分充值消费记录
+     * @return 结果
+     */
+    @Override
+    public int updateXhnIntegral(XhnIntegral xhnIntegral) {
+        return xhnIntegralMapper.updateXhnIntegral(xhnIntegral);
+    }
+
+    /**
+     * 批量删除小红娘金额、积分充值消费记录
+     *
+     * @param ids 需要删除的小红娘金额、积分充值消费记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteXhnIntegralByIds(String ids) {
+        return xhnIntegralMapper.deleteXhnIntegralByIds(Convert.toStrArray(ids));
+    }
+
+    /**
+     * 删除小红娘金额、积分充值消费记录信息
+     *
+     * @param id 小红娘金额、积分充值消费记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteXhnIntegralById(String id) {
+        return xhnIntegralMapper.deleteXhnIntegralById(id);
+    }
+}

+ 120 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/controller/XhnOrderController.java

@@ -0,0 +1,120 @@
+package com.ruoyi.business.xhnnotsingle.order.controller;
+
+import java.util.List;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.business.xhnnotsingle.order.domain.XhnOrder;
+import com.ruoyi.business.xhnnotsingle.order.service.IXhnOrderService;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 微信充值订单Controller
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+@Controller
+@RequestMapping("/xhnnotsingle/order")
+public class XhnOrderController extends BaseController {
+    private String prefix = "xhnnotsingle/order" ;
+
+    @Autowired
+    private IXhnOrderService xhnOrderService;
+
+    @RequiresPermissions("xhnnotsingle:order:view")
+    @GetMapping()
+    public String order() {
+        return prefix + "/order" ;
+    }
+
+        /**
+         * 查询微信充值订单列表
+         */
+        @RequiresPermissions("xhnnotsingle:order:list")
+        @PostMapping("/list")
+        @ResponseBody
+        public TableDataInfo list(XhnOrder xhnOrder) {
+            startPage();
+            List<XhnOrder> list = xhnOrderService.selectXhnOrderList(xhnOrder);
+            return getDataTable(list);
+        }
+
+    /**
+     * 导出微信充值订单列表
+     */
+    @RequiresPermissions("xhnnotsingle:order:export")
+    @Log(title = "微信充值订单" , businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @ResponseBody
+    public AjaxResult export(XhnOrder xhnOrder) {
+        List<XhnOrder> list = xhnOrderService.selectXhnOrderList(xhnOrder);
+        ExcelUtil<XhnOrder> util = new ExcelUtil<XhnOrder>(XhnOrder. class);
+        return util.exportExcel(list, "微信充值订单数据");
+    }
+
+        /**
+         * 新增微信充值订单
+         */
+        @GetMapping("/add")
+        public String add() {
+            return prefix + "/add" ;
+        }
+
+    /**
+     * 新增保存微信充值订单
+     */
+    @RequiresPermissions("xhnnotsingle:order:add")
+    @Log(title = "微信充值订单" , businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    @ResponseBody
+    public AjaxResult addSave(XhnOrder xhnOrder) {
+        return toAjax(xhnOrderService.insertXhnOrder(xhnOrder));
+    }
+
+    /**
+     * 修改微信充值订单
+     */
+    @RequiresPermissions("xhnnotsingle:order:edit")
+    @GetMapping("/edit/{orderCode}")
+    public String edit(@PathVariable("orderCode") String orderCode, ModelMap mmap) {
+        XhnOrder xhnOrder =
+            xhnOrderService.selectXhnOrderByOrderCode(orderCode);
+        mmap.put("xhnOrder" , xhnOrder);
+        return prefix + "/edit" ;
+    }
+
+    /**
+     * 修改保存微信充值订单
+     */
+    @RequiresPermissions("xhnnotsingle:order:edit")
+    @Log(title = "微信充值订单" , businessType = BusinessType.UPDATE)
+    @PostMapping("/edit")
+    @ResponseBody
+    public AjaxResult editSave(XhnOrder xhnOrder) {
+        return toAjax(xhnOrderService.updateXhnOrder(xhnOrder));
+    }
+
+        /**
+         * 删除微信充值订单
+         */
+        @RequiresPermissions("xhnnotsingle:order:remove")
+        @Log(title = "微信充值订单" , businessType = BusinessType.DELETE)
+        @PostMapping("/remove")
+        @ResponseBody
+        public AjaxResult remove(String ids) {
+            return toAjax(xhnOrderService.deleteXhnOrderByOrderCodes(ids));
+        }
+}

+ 92 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/domain/XhnOrder.java

@@ -0,0 +1,92 @@
+package com.ruoyi.business.xhnnotsingle.order.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 微信充值订单对象 xhn_order
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+public class XhnOrder extends BaseEntity
+        {
+private static final long serialVersionUID=1L;
+
+    /** 订单号 */
+    private String orderCode;
+
+    /** 充值金额 */
+            @Excel(name = "充值金额")
+    private String money;
+
+    /** 充值时间 */
+            @Excel(name = "充值时间")
+    private String orderDate;
+
+    /** 付款状态   标明是付款中,还是付款成功 */
+            @Excel(name = "付款状态   标明是付款中,还是付款成功")
+    private String state;
+
+    /** 充值成功返回时间 */
+            @Excel(name = "充值成功返回时间")
+    private String orderSuccessDate;
+
+    public void setOrderCode(String orderCode)
+            {
+            this.orderCode = orderCode;
+            }
+
+    public String getOrderCode()
+            {
+            return orderCode;
+            }
+    public void setMoney(String money)
+            {
+            this.money = money;
+            }
+
+    public String getMoney()
+            {
+            return money;
+            }
+    public void setOrderDate(String orderDate)
+            {
+            this.orderDate = orderDate;
+            }
+
+    public String getOrderDate()
+            {
+            return orderDate;
+            }
+    public void setState(String state)
+            {
+            this.state = state;
+            }
+
+    public String getState()
+            {
+            return state;
+            }
+
+    public String getOrderSuccessDate() {
+        return orderSuccessDate;
+    }
+
+    public void setOrderSuccessDate(String orderSuccessDate) {
+        this.orderSuccessDate = orderSuccessDate;
+    }
+
+            @Override
+public String toString(){
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("orderCode" ,getOrderCode())
+            .append("money" ,getMoney())
+            .append("orderDate" ,getOrderDate())
+            .append("state" ,getState())
+            .append("orderSuccessDate" ,getOrderSuccessDate())
+        .toString();
+        }
+        }

+ 61 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/mapper/XhnOrderMapper.java

@@ -0,0 +1,61 @@
+package com.ruoyi.business.xhnnotsingle.order.mapper;
+
+import java.util.List;
+
+import com.ruoyi.business.xhnnotsingle.order.domain.XhnOrder;
+
+/**
+ * 微信充值订单Mapper接口
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+public interface XhnOrderMapper {
+    /**
+     * 查询微信充值订单
+     *
+     * @param orderCode 微信充值订单主键
+     * @return 微信充值订单
+     */
+    public XhnOrder selectXhnOrderByOrderCode(String orderCode);
+
+    /**
+     * 查询微信充值订单列表
+     *
+     * @param xhnOrder 微信充值订单
+     * @return 微信充值订单集合
+     */
+    public List<XhnOrder> selectXhnOrderList(XhnOrder xhnOrder);
+
+    /**
+     * 新增微信充值订单
+     *
+     * @param xhnOrder 微信充值订单
+     * @return 结果
+     */
+    public int insertXhnOrder(XhnOrder xhnOrder);
+
+    /**
+     * 修改微信充值订单
+     *
+     * @param xhnOrder 微信充值订单
+     * @return 结果
+     */
+    public int updateXhnOrder(XhnOrder xhnOrder);
+
+    /**
+     * 删除微信充值订单
+     *
+     * @param orderCode 微信充值订单主键
+     * @return 结果
+     */
+    public int deleteXhnOrderByOrderCode(String orderCode);
+
+    /**
+     * 批量删除微信充值订单
+     *
+     * @param orderCodes 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteXhnOrderByOrderCodes(String[] orderCodes);
+}

+ 61 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/service/IXhnOrderService.java

@@ -0,0 +1,61 @@
+package com.ruoyi.business.xhnnotsingle.order.service;
+
+import java.util.List;
+
+import com.ruoyi.business.xhnnotsingle.order.domain.XhnOrder;
+
+/**
+ * 微信充值订单Service接口
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+public interface IXhnOrderService {
+    /**
+     * 查询微信充值订单
+     *
+     * @param orderCode 微信充值订单主键
+     * @return 微信充值订单
+     */
+    public XhnOrder selectXhnOrderByOrderCode(String orderCode);
+
+    /**
+     * 查询微信充值订单列表
+     *
+     * @param xhnOrder 微信充值订单
+     * @return 微信充值订单集合
+     */
+    public List<XhnOrder> selectXhnOrderList(XhnOrder xhnOrder);
+
+    /**
+     * 新增微信充值订单
+     *
+     * @param xhnOrder 微信充值订单
+     * @return 结果
+     */
+    public int insertXhnOrder(XhnOrder xhnOrder);
+
+    /**
+     * 修改微信充值订单
+     *
+     * @param xhnOrder 微信充值订单
+     * @return 结果
+     */
+    public int updateXhnOrder(XhnOrder xhnOrder);
+
+    /**
+     * 批量删除微信充值订单
+     *
+     * @param orderCodes 需要删除的微信充值订单主键集合
+     * @return 结果
+     */
+    public int deleteXhnOrderByOrderCodes(String orderCodes);
+
+    /**
+     * 删除微信充值订单信息
+     *
+     * @param orderCode 微信充值订单主键
+     * @return 结果
+     */
+    public int deleteXhnOrderByOrderCode(String orderCode);
+}

+ 87 - 0
mybusiness/src/main/java/com/ruoyi/business/xhnnotsingle/order/service/impl/XhnOrderServiceImpl.java

@@ -0,0 +1,87 @@
+package com.ruoyi.business.xhnnotsingle.order.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.business.xhnnotsingle.order.mapper.XhnOrderMapper;
+import com.ruoyi.business.xhnnotsingle.order.domain.XhnOrder;
+import com.ruoyi.business.xhnnotsingle.order.service.IXhnOrderService;
+import com.ruoyi.common.core.text.Convert;
+
+/**
+ * 微信充值订单Service业务层处理
+ *
+ * @author ruoyi
+ * @date 2022-11-29
+ */
+@Service
+public class XhnOrderServiceImpl implements IXhnOrderService {
+    @Autowired
+    private XhnOrderMapper xhnOrderMapper;
+
+    /**
+     * 查询微信充值订单
+     *
+     * @param orderCode 微信充值订单主键
+     * @return 微信充值订单
+     */
+    @Override
+    public XhnOrder selectXhnOrderByOrderCode(String orderCode) {
+        return xhnOrderMapper.selectXhnOrderByOrderCode(orderCode);
+    }
+
+    /**
+     * 查询微信充值订单列表
+     *
+     * @param xhnOrder 微信充值订单
+     * @return 微信充值订单
+     */
+    @Override
+    public List<XhnOrder> selectXhnOrderList(XhnOrder xhnOrder) {
+        return xhnOrderMapper.selectXhnOrderList(xhnOrder);
+    }
+
+    /**
+     * 新增微信充值订单
+     *
+     * @param xhnOrder 微信充值订单
+     * @return 结果
+     */
+    @Override
+    public int insertXhnOrder(XhnOrder xhnOrder) {
+            return xhnOrderMapper.insertXhnOrder(xhnOrder);
+    }
+
+    /**
+     * 修改微信充值订单
+     *
+     * @param xhnOrder 微信充值订单
+     * @return 结果
+     */
+    @Override
+    public int updateXhnOrder(XhnOrder xhnOrder) {
+        return xhnOrderMapper.updateXhnOrder(xhnOrder);
+    }
+
+    /**
+     * 批量删除微信充值订单
+     *
+     * @param orderCodes 需要删除的微信充值订单主键
+     * @return 结果
+     */
+    @Override
+    public int deleteXhnOrderByOrderCodes(String orderCodes) {
+        return xhnOrderMapper.deleteXhnOrderByOrderCodes(Convert.toStrArray(orderCodes));
+    }
+
+    /**
+     * 删除微信充值订单信息
+     *
+     * @param orderCode 微信充值订单主键
+     * @return 结果
+     */
+    @Override
+    public int deleteXhnOrderByOrderCode(String orderCode) {
+        return xhnOrderMapper.deleteXhnOrderByOrderCode(orderCode);
+    }
+}

+ 15 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/Credentials.java

@@ -0,0 +1,15 @@
+package com.wechat.pay.contrib.apache.httpclient;
+
+import java.io.IOException;
+import org.apache.http.client.methods.HttpRequestWrapper;
+
+/**
+ * @author xy-peng
+ */
+public interface Credentials {
+
+    String getSchema();
+
+    String getToken(HttpRequestWrapper request) throws IOException;
+
+}

+ 105 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/SignatureExec.java

@@ -0,0 +1,105 @@
+package com.wechat.pay.contrib.apache.httpclient;
+
+import static org.apache.http.HttpHeaders.AUTHORIZATION;
+import static org.apache.http.HttpStatus.SC_MULTIPLE_CHOICES;
+import static org.apache.http.HttpStatus.SC_OK;
+
+import java.io.IOException;
+import java.util.Arrays;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpExecutionAware;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.entity.BufferedHttpEntity;
+import org.apache.http.impl.execchain.ClientExecChain;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author xy-peng
+ */
+public class SignatureExec implements ClientExecChain {
+
+    private static final String WECHAT_PAY_HOST_NAME_SUFFIX = ".mch.weixin.qq.com";
+    private static final Logger log = LoggerFactory.getLogger(SignatureExec.class);
+    private final ClientExecChain mainExec;
+    private final Credentials credentials;
+    private final Validator validator;
+
+    protected SignatureExec(Credentials credentials, Validator validator, ClientExecChain mainExec) {
+        this.credentials = credentials;
+        this.validator = validator;
+        this.mainExec = mainExec;
+    }
+
+    protected void convertToRepeatableResponseEntity(CloseableHttpResponse response) throws IOException {
+        HttpEntity entity = response.getEntity();
+        if (entity != null) {
+            response.setEntity(new BufferedHttpEntity(entity));
+        }
+    }
+
+    protected void convertToRepeatableRequestEntity(HttpRequestWrapper request) throws IOException {
+        if (isEntityEnclosing(request)) {
+            HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
+            if (entity != null) {
+                ((HttpEntityEnclosingRequest) request).setEntity(new BufferedHttpEntity(entity));
+            }
+        }
+    }
+
+    @Override
+    public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request, HttpClientContext context,
+            HttpExecutionAware execAware) throws IOException, HttpException {
+        if (request.getTarget().getHostName().endsWith(WECHAT_PAY_HOST_NAME_SUFFIX)) {
+            return executeWithSignature(route, request, context, execAware);
+        } else {
+            return mainExec.execute(route, request, context, execAware);
+        }
+    }
+
+    private boolean isEntityEnclosing(HttpRequestWrapper request) {
+        return request instanceof HttpEntityEnclosingRequest;
+    }
+
+    private boolean isUploadHttpPost(HttpRequestWrapper request) {
+        return request.getOriginal() instanceof WechatPayUploadHttpPost;
+    }
+
+    private CloseableHttpResponse executeWithSignature(HttpRoute route, HttpRequestWrapper request,
+            HttpClientContext context,
+            HttpExecutionAware execAware) throws IOException, HttpException {
+        // 上传类不需要消耗两次故不做转换
+        if (!isUploadHttpPost(request)) {
+            convertToRepeatableRequestEntity(request);
+        }
+        // 添加认证信息
+        request.addHeader(AUTHORIZATION, credentials.getSchema() + " " + credentials.getToken(request));
+        // 执行
+        CloseableHttpResponse response = mainExec.execute(route, request, context, execAware);
+        // 对成功应答验签
+        StatusLine statusLine = response.getStatusLine();
+        if (statusLine.getStatusCode() >= SC_OK && statusLine.getStatusCode() < SC_MULTIPLE_CHOICES) {
+            convertToRepeatableResponseEntity(response);
+            if (!validator.validate(response)) {
+                throw new HttpException("应答的微信支付签名验证失败");
+            }
+        } else {
+            // 错误应答需要打日志
+            log.error("应答的状态码不为200-299。status code[{}]\trequest headers[{}]", statusLine.getStatusCode(),
+                    Arrays.toString(request.getAllHeaders()));
+            if (isEntityEnclosing(request) && !isUploadHttpPost(request)) {
+                HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
+                String body = EntityUtils.toString(entity);
+                log.error("应答的状态码不为200-299。request body[{}]", body);
+            }
+        }
+        return response;
+    }
+}

+ 13 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/Validator.java

@@ -0,0 +1,13 @@
+package com.wechat.pay.contrib.apache.httpclient;
+
+import java.io.IOException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+
+/**
+ * @author xy-peng
+ */
+public interface Validator {
+
+    boolean validate(CloseableHttpResponse response) throws IOException;
+
+}

+ 96 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/WechatPayHttpClientBuilder.java

@@ -0,0 +1,96 @@
+package com.wechat.pay.contrib.apache.httpclient;
+
+import com.wechat.pay.contrib.apache.httpclient.auth.CertificatesVerifier;
+import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
+import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
+import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.execchain.ClientExecChain;
+import org.apache.http.HttpHost;
+
+/**
+ * @author xy-peng
+ */
+public class WechatPayHttpClientBuilder extends HttpClientBuilder {
+
+    private static final String OS = System.getProperty("os.name") + "/" + System.getProperty("os.version");
+    private static final String VERSION = System.getProperty("java.version");
+    private Credentials credentials;
+    private Validator validator;
+
+
+    private WechatPayHttpClientBuilder() {
+        super();
+
+        String userAgent = String.format(
+                "WechatPay-Apache-HttpClient/%s (%s) Java/%s",
+                getClass().getPackage().getImplementationVersion(),
+                OS,
+                VERSION == null ? "Unknown" : VERSION);
+        setUserAgent(userAgent);
+    }
+
+    public static WechatPayHttpClientBuilder create() {
+        return new WechatPayHttpClientBuilder();
+    }
+
+    public WechatPayHttpClientBuilder withMerchant(String merchantId, String serialNo, PrivateKey privateKey) {
+        this.credentials = new WechatPay2Credentials(merchantId, new PrivateKeySigner(serialNo, privateKey));
+        return this;
+    }
+
+    public WechatPayHttpClientBuilder withCredentials(Credentials credentials) {
+        this.credentials = credentials;
+        return this;
+    }
+
+    public WechatPayHttpClientBuilder withWechatPay(List<X509Certificate> certificates) {
+        this.validator = new WechatPay2Validator(new CertificatesVerifier(certificates));
+        return this;
+    }
+
+    public WechatPayHttpClientBuilder withProxy(HttpHost proxy) {
+        if (proxy != null) {
+            this.setProxy(proxy);
+        }
+        return this;
+    }
+
+    /**
+     * Please use {@link #withWechatPay(List)} instead
+     *
+     * @param certificates 平台证书list
+     * @return 具有验证器的builder
+     */
+    @SuppressWarnings("SpellCheckingInspection")
+    @Deprecated
+    public WechatPayHttpClientBuilder withWechatpay(List<X509Certificate> certificates) {
+        return withWechatPay(certificates);
+    }
+
+    public WechatPayHttpClientBuilder withValidator(Validator validator) {
+        this.validator = validator;
+        return this;
+    }
+
+    @Override
+    public CloseableHttpClient build() {
+        if (credentials == null) {
+            throw new IllegalArgumentException("缺少身份认证信息");
+        }
+        if (validator == null) {
+            throw new IllegalArgumentException("缺少签名验证信息");
+        }
+        return super.build();
+    }
+
+    @Override
+    protected ClientExecChain decorateProtocolExec(final ClientExecChain requestExecutor) {
+        return new SignatureExec(this.credentials, this.validator, requestExecutor);
+    }
+
+}

+ 96 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/WechatPayUploadHttpPost.java

@@ -0,0 +1,96 @@
+package com.wechat.pay.contrib.apache.httpclient;
+
+import static org.apache.http.HttpHeaders.ACCEPT;
+import static org.apache.http.entity.ContentType.APPLICATION_JSON;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URLConnection;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+//import org.apache.http.entity.mime.HttpMultipartMode;
+//import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author xy-peng
+ */
+public class WechatPayUploadHttpPost extends HttpPost {
+
+    private final String meta;
+    private static final Logger log = LoggerFactory.getLogger(WechatPayUploadHttpPost.class);
+
+    private WechatPayUploadHttpPost(URI uri, String meta) {
+        super(uri);
+        this.meta = meta;
+    }
+
+    public String getMeta() {
+        return meta;
+    }
+
+    public static class Builder {
+
+        private final URI uri;
+        private String fileName;
+        private InputStream fileInputStream;
+        private ContentType fileContentType;
+        private String meta;
+
+        public Builder(URI uri) {
+            if (uri == null) {
+                throw new IllegalArgumentException("上传文件接口URL为空");
+            }
+            this.uri = uri;
+        }
+
+        public Builder withImage(String fileName, String fileSha256, InputStream inputStream) {
+            if (fileSha256 == null || fileSha256.isEmpty()) {
+                throw new IllegalArgumentException("文件摘要为空");
+            }
+            meta = String.format("{\"filename\":\"%s\",\"sha256\":\"%s\"}", fileName, fileSha256);
+            return withFile(fileName, meta, inputStream);
+        }
+
+        public Builder withFile(String fileName, String meta, InputStream inputStream) {
+            this.fileName = fileName;
+            this.fileInputStream = inputStream;
+            String mimeType = URLConnection.guessContentTypeFromName(fileName);
+            if (mimeType == null) {
+                // guess this is a video uploading
+                this.fileContentType = ContentType.APPLICATION_OCTET_STREAM;
+            } else {
+                this.fileContentType = ContentType.create(mimeType);
+            }
+            this.meta = meta;
+            return this;
+        }
+
+//        public WechatPayUploadHttpPost build() {
+//            if (fileName == null || fileName.isEmpty()) {
+//                throw new IllegalArgumentException("文件名称为空");
+//            }
+//            if (fileInputStream == null) {
+//                throw new IllegalArgumentException("文件为空");
+//            }
+//            if (fileContentType == null) {
+//                throw new IllegalArgumentException("文件类型为空");
+//            }
+//            if (meta == null || meta.isEmpty()) {
+//                throw new IllegalArgumentException("媒体文件元信息为空");
+//            }
+//            WechatPayUploadHttpPost request = new WechatPayUploadHttpPost(uri, meta);
+//            MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
+//            entityBuilder.setMode(HttpMultipartMode.RFC6532)
+//                    .addTextBody("meta", meta, APPLICATION_JSON)
+//                    .addBinaryBody("file", fileInputStream, fileContentType, fileName);
+//            HttpEntity entity = entityBuilder.build();
+//            request.setEntity(entity);
+//            request.addHeader(ACCEPT, APPLICATION_JSON.toString());
+//            log.debug("request content-type[{}]", entity.getContentType());
+//            return request;
+//        }
+    }
+}

+ 159 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/AutoUpdateCertificatesVerifier.java

@@ -0,0 +1,159 @@
+package com.wechat.pay.contrib.apache.httpclient.auth;
+
+import static org.apache.http.HttpHeaders.ACCEPT;
+import static org.apache.http.HttpStatus.SC_OK;
+import static org.apache.http.entity.ContentType.APPLICATION_JSON;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.wechat.pay.contrib.apache.httpclient.Credentials;
+import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
+import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 在原有CertificatesVerifier基础上,增加自动更新证书功能
+ * 该类已废弃,请使用CertificatesManager
+ *
+ * @author xy-peng
+ */
+@Deprecated
+public class AutoUpdateCertificatesVerifier implements Verifier {
+
+    protected static final Logger log = LoggerFactory.getLogger(AutoUpdateCertificatesVerifier.class);
+    /**
+     * 证书下载地址
+     */
+    private static final String CERT_DOWNLOAD_PATH = "https://api.mch.weixin.qq.com/v3/certificates";
+    /**
+     * 证书更新间隔时间,单位为分钟
+     */
+    protected final long minutesInterval;
+    protected final Credentials credentials;
+    protected final byte[] apiV3Key;
+    protected final ReentrantLock lock = new ReentrantLock();
+    /**
+     * 上次更新时间
+     */
+    protected volatile Instant lastUpdateTime;
+    protected CertificatesVerifier verifier;
+
+    public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key) {
+        this(credentials, apiV3Key, TimeUnit.HOURS.toMinutes(1));
+    }
+
+    public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key, long minutesInterval) {
+        this.credentials = credentials;
+        this.apiV3Key = apiV3Key;
+        this.minutesInterval = minutesInterval;
+        //构造时更新证书
+        try {
+            autoUpdateCert();
+            lastUpdateTime = Instant.now();
+        } catch (IOException | GeneralSecurityException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean verify(String serialNumber, byte[] message, String signature) {
+        if (lastUpdateTime == null
+                || Duration.between(lastUpdateTime, Instant.now()).toMinutes() >= minutesInterval) {
+            if (lock.tryLock()) {
+                try {
+                    autoUpdateCert();
+                    //更新时间
+                    lastUpdateTime = Instant.now();
+                } catch (GeneralSecurityException | IOException e) {
+                    log.warn("Auto update cert failed: ", e);
+                } finally {
+                    lock.unlock();
+                }
+            }
+        }
+        return verifier.verify(serialNumber, message, signature);
+    }
+
+    @Override
+    public X509Certificate getValidCertificate() {
+        return verifier.getValidCertificate();
+    }
+
+    protected void autoUpdateCert() throws IOException, GeneralSecurityException {
+        try (CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
+                .withCredentials(credentials)
+                .withValidator(verifier == null ? (response) -> true : new WechatPay2Validator(verifier))
+                .build()) {
+
+            HttpGet httpGet = new HttpGet(CERT_DOWNLOAD_PATH);
+            httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString());
+
+            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+                int statusCode = response.getStatusLine().getStatusCode();
+                String body = EntityUtils.toString(response.getEntity());
+                if (statusCode == SC_OK) {
+                    List<X509Certificate> newCertList = deserializeToCerts(apiV3Key, body);
+                    if (newCertList.isEmpty()) {
+                        log.warn("Cert list is empty");
+                        return;
+                    }
+                    this.verifier = new CertificatesVerifier(newCertList);
+                } else {
+                    log.warn("Auto update cert failed, statusCode = {}, body = {}", statusCode, body);
+                }
+            }
+        }
+    }
+
+    protected List<X509Certificate> deserializeToCerts(byte[] apiV3Key, String body)
+            throws GeneralSecurityException, IOException {
+        AesUtil aesUtil = new AesUtil(apiV3Key);
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode dataNode = mapper.readTree(body).get("data");
+        List<X509Certificate> newCertList = new ArrayList<>();
+        if (dataNode != null) {
+            for (int i = 0, count = dataNode.size(); i < count; i++) {
+                JsonNode node = dataNode.get(i).get("encrypt_certificate");
+                //解密
+                String cert = aesUtil.decryptToString(
+                        node.get("associated_data").toString().replace("\"", "")
+                                .getBytes(StandardCharsets.UTF_8),
+                        node.get("nonce").toString().replace("\"", "")
+                                .getBytes(StandardCharsets.UTF_8),
+                        node.get("ciphertext").toString().replace("\"", ""));
+
+                CertificateFactory cf = CertificateFactory.getInstance("X509");
+                X509Certificate x509Cert = (X509Certificate) cf.generateCertificate(
+                        new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8))
+                );
+                try {
+                    x509Cert.checkValidity();
+                } catch (CertificateExpiredException | CertificateNotYetValidException e) {
+                    continue;
+                }
+                newCertList.add(x509Cert);
+            }
+        }
+        return newCertList;
+    }
+
+}

+ 87 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/CertificatesVerifier.java

@@ -0,0 +1,87 @@
+package com.wechat.pay.contrib.apache.httpclient.auth;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author xy-peng
+ */
+public class CertificatesVerifier implements Verifier {
+
+    private static final Logger log = LoggerFactory.getLogger(CertificatesVerifier.class);
+    protected final HashMap<BigInteger, X509Certificate> certificates = new HashMap<>();
+
+    public CertificatesVerifier(List<X509Certificate> list) {
+        for (X509Certificate item : list) {
+            certificates.put(item.getSerialNumber(), item);
+        }
+    }
+
+    public CertificatesVerifier(Map<BigInteger, X509Certificate> certificates) {
+        this.certificates.putAll(certificates);
+    }
+
+
+    public void updateCertificates(Map<BigInteger, X509Certificate> certificates) {
+        this.certificates.clear();
+        this.certificates.putAll(certificates);
+    }
+
+    protected boolean verify(X509Certificate certificate, byte[] message, String signature) {
+        try {
+            Signature sign = Signature.getInstance("SHA256withRSA");
+            sign.initVerify(certificate);
+            sign.update(message);
+            return sign.verify(Base64.getDecoder().decode(signature));
+
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
+        } catch (SignatureException e) {
+            throw new RuntimeException("签名验证过程发生了错误", e);
+        } catch (InvalidKeyException e) {
+            throw new RuntimeException("无效的证书", e);
+        }
+    }
+
+    @Override
+    public boolean verify(String serialNumber, byte[] message, String signature) {
+        BigInteger val = new BigInteger(serialNumber, 16);
+        X509Certificate cert = certificates.get(val);
+        if (cert == null) {
+            log.error("找不到证书序列号对应的证书,序列号:{}", serialNumber);
+            return false;
+        }
+        return verify(cert, message, signature);
+    }
+
+    @Override
+    public X509Certificate getValidCertificate() {
+        X509Certificate latestCert = null;
+        for (X509Certificate x509Cert : certificates.values()) {
+            // 若latestCert为空或x509Cert的证书有效开始时间在latestCert之后,则更新latestCert
+            if (latestCert == null || x509Cert.getNotBefore().after(latestCert.getNotBefore())) {
+                latestCert = x509Cert;
+            }
+        }
+        try {
+            latestCert.checkValidity();
+            return latestCert;
+        } catch (CertificateExpiredException | CertificateNotYetValidException e) {
+            throw new NoSuchElementException("没有有效的微信支付平台证书");
+        }
+    }
+}
+

+ 40 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/PrivateKeySigner.java

@@ -0,0 +1,40 @@
+package com.wechat.pay.contrib.apache.httpclient.auth;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Base64;
+
+/**
+ * @author xy-peng
+ */
+public class PrivateKeySigner implements Signer {
+
+    protected final String certificateSerialNumber;
+    protected final PrivateKey privateKey;
+
+    public PrivateKeySigner(String serialNumber, PrivateKey privateKey) {
+        this.certificateSerialNumber = serialNumber;
+        this.privateKey = privateKey;
+    }
+
+    @Override
+    public SignatureResult sign(byte[] message) {
+        try {
+            Signature sign = Signature.getInstance("SHA256withRSA");
+            sign.initSign(privateKey);
+            sign.update(message);
+            return new SignatureResult(Base64.getEncoder().encodeToString(sign.sign()), certificateSerialNumber);
+
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
+        } catch (SignatureException e) {
+            throw new RuntimeException("签名计算失败", e);
+        } catch (InvalidKeyException e) {
+            throw new RuntimeException("无效的私钥", e);
+        }
+    }
+
+}

+ 29 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/Signer.java

@@ -0,0 +1,29 @@
+package com.wechat.pay.contrib.apache.httpclient.auth;
+
+/**
+ * @author xy-peng
+ */
+public interface Signer {
+
+    SignatureResult sign(byte[] message);
+
+    class SignatureResult {
+
+        protected final String sign;
+        protected final String certificateSerialNumber;
+
+        public String getSign() {
+            return sign;
+        }
+
+        public String getCertificateSerialNumber() {
+            return certificateSerialNumber;
+        }
+
+        public SignatureResult(String sign, String serialNumber) {
+            this.sign = sign;
+            this.certificateSerialNumber = serialNumber;
+        }
+    }
+
+}

+ 18 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/Verifier.java

@@ -0,0 +1,18 @@
+package com.wechat.pay.contrib.apache.httpclient.auth;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * @author xy-peng
+ */
+public interface Verifier {
+
+    boolean verify(String serialNumber, byte[] message, String signature);
+
+    /**
+     * 获取合法的平台证书
+     *
+     * @return 合法证书
+     */
+    X509Certificate getValidCertificate();
+}

+ 95 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/WechatPay2Credentials.java

@@ -0,0 +1,95 @@
+package com.wechat.pay.contrib.apache.httpclient.auth;
+
+import com.wechat.pay.contrib.apache.httpclient.Credentials;
+import com.wechat.pay.contrib.apache.httpclient.WechatPayUploadHttpPost;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author xy-peng
+ */
+public class WechatPay2Credentials implements Credentials {
+
+    protected static final Logger log = LoggerFactory.getLogger(WechatPay2Credentials.class);
+
+    protected static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    protected static final SecureRandom RANDOM = new SecureRandom();
+    protected final String merchantId;
+    protected final Signer signer;
+
+    public WechatPay2Credentials(String merchantId, Signer signer) {
+        this.merchantId = merchantId;
+        this.signer = signer;
+    }
+
+    public String getMerchantId() {
+        return merchantId;
+    }
+
+    protected long generateTimestamp() {
+        return System.currentTimeMillis() / 1000;
+    }
+
+    protected String generateNonceStr() {
+        char[] nonceChars = new char[32];
+        for (int index = 0; index < nonceChars.length; ++index) {
+            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
+        }
+        return new String(nonceChars);
+    }
+
+    @Override
+    public final String getSchema() {
+        return "WECHATPAY2-SHA256-RSA2048";
+    }
+
+    @Override
+    public final String getToken(HttpRequestWrapper request) throws IOException {
+        String nonceStr = generateNonceStr();
+        long timestamp = generateTimestamp();
+
+        String message = buildMessage(nonceStr, timestamp, request);
+        log.debug("authorization message=[{}]", message);
+
+        Signer.SignatureResult signature = signer.sign(message.getBytes(StandardCharsets.UTF_8));
+
+        String token = "mchid=\"" + getMerchantId() + "\","
+                + "nonce_str=\"" + nonceStr + "\","
+                + "timestamp=\"" + timestamp + "\","
+                + "serial_no=\"" + signature.certificateSerialNumber + "\","
+                + "signature=\"" + signature.sign + "\"";
+        log.debug("authorization token=[{}]", token);
+
+        return token;
+    }
+
+    protected String buildMessage(String nonce, long timestamp, HttpRequestWrapper request) throws IOException {
+        URI uri = request.getURI();
+        String canonicalUrl = uri.getRawPath();
+        if (uri.getQuery() != null) {
+            canonicalUrl += "?" + uri.getRawQuery();
+        }
+
+        String body = "";
+        // PATCH,POST,PUT
+        if (request.getOriginal() instanceof WechatPayUploadHttpPost) {
+            body = ((WechatPayUploadHttpPost) request.getOriginal()).getMeta();
+        } else if (request instanceof HttpEntityEnclosingRequest) {
+            body = EntityUtils.toString(((HttpEntityEnclosingRequest) request).getEntity(), StandardCharsets.UTF_8);
+        }
+
+        return request.getRequestLine().getMethod() + "\n"
+                + canonicalUrl + "\n"
+                + timestamp + "\n"
+                + nonce + "\n"
+                + body + "\n";
+    }
+
+}

+ 114 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/auth/WechatPay2Validator.java

@@ -0,0 +1,114 @@
+package com.wechat.pay.contrib.apache.httpclient.auth;
+
+
+import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.REQUEST_ID;
+import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.WECHAT_PAY_NONCE;
+import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.WECHAT_PAY_SERIAL;
+import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE;
+import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.WECHAT_PAY_TIMESTAMP;
+
+import com.wechat.pay.contrib.apache.httpclient.Validator;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Instant;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author xy-peng
+ */
+public class WechatPay2Validator implements Validator {
+
+    protected static final Logger log = LoggerFactory.getLogger(WechatPay2Validator.class);
+    /**
+     * 应答超时时间,单位为分钟
+     */
+    protected static final long RESPONSE_EXPIRED_MINUTES = 5;
+    protected final Verifier verifier;
+
+    public WechatPay2Validator(Verifier verifier) {
+        this.verifier = verifier;
+    }
+
+    protected static IllegalArgumentException parameterError(String message, Object... args) {
+        message = String.format(message, args);
+        return new IllegalArgumentException("parameter error: " + message);
+    }
+
+    protected static IllegalArgumentException verifyFail(String message, Object... args) {
+        message = String.format(message, args);
+        return new IllegalArgumentException("signature verify fail: " + message);
+    }
+
+    @Override
+    public final boolean validate(CloseableHttpResponse response) throws IOException {
+        try {
+            validateParameters(response);
+
+            String message = buildMessage(response);
+            String serial = response.getFirstHeader(WECHAT_PAY_SERIAL).getValue();
+            String signature = response.getFirstHeader(WECHAT_PAY_SIGNATURE).getValue();
+
+            if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
+                throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
+                        serial, message, signature, response.getFirstHeader(REQUEST_ID).getValue());
+            }
+        } catch (IllegalArgumentException e) {
+            log.warn(e.getMessage());
+            return false;
+        }
+
+        return true;
+    }
+
+    protected final void validateParameters(CloseableHttpResponse response) {
+        Header firstHeader = response.getFirstHeader(REQUEST_ID);
+        if (firstHeader == null) {
+            throw parameterError("empty " + REQUEST_ID);
+        }
+        String requestId = firstHeader.getValue();
+
+        // NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
+        String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
+
+        Header header = null;
+        for (String headerName : headers) {
+            header = response.getFirstHeader(headerName);
+            if (header == null) {
+                throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
+            }
+        }
+
+        String timestampStr = header.getValue();
+        try {
+            Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
+            // 拒绝过期应答
+            if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
+                throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
+            }
+        } catch (DateTimeException | NumberFormatException e) {
+            throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
+        }
+    }
+
+    protected final String buildMessage(CloseableHttpResponse response) throws IOException {
+        String timestamp = response.getFirstHeader(WECHAT_PAY_TIMESTAMP).getValue();
+        String nonce = response.getFirstHeader(WECHAT_PAY_NONCE).getValue();
+        String body = getResponseBody(response);
+        return timestamp + "\n"
+                + nonce + "\n"
+                + body + "\n";
+    }
+
+    protected final String getResponseBody(CloseableHttpResponse response) throws IOException {
+        HttpEntity entity = response.getEntity();
+        return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
+    }
+
+}

+ 327 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/cert/CertificatesManager.java

@@ -0,0 +1,327 @@
+package com.wechat.pay.contrib.apache.httpclient.cert;
+
+import static org.apache.http.HttpHeaders.ACCEPT;
+import static org.apache.http.HttpStatus.SC_OK;
+import static org.apache.http.entity.ContentType.APPLICATION_JSON;
+
+import com.wechat.pay.contrib.apache.httpclient.Credentials;
+import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
+import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
+import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
+import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
+import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
+import com.wechat.pay.contrib.apache.httpclient.util.CertSerializeUtil;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.util.Base64;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.apache.http.HttpHost;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 平台证书管理器,定时更新证书(默认值为UPDATE_INTERVAL_MINUTE)
+ *
+ * @author lianup
+ * @since 0.3.0
+ */
+public class CertificatesManager {
+
+    private static final Logger log = LoggerFactory.getLogger(CertificatesManager.class);
+    protected static final int UPDATE_INTERVAL_MINUTE = 1440;
+    /**
+     * 证书下载地址
+     */
+    private static final String CERT_DOWNLOAD_PATH = "https://api.mch.weixin.qq.com/v3/certificates";
+    private static final String SCHEDULE_UPDATE_CERT_THREAD_NAME = "scheduled_update_cert_thread";
+    private volatile static CertificatesManager instance = null;
+    private ConcurrentHashMap<String, byte[]> apiV3Keys = new ConcurrentHashMap<>();
+
+    private HttpHost proxy;
+
+    private ConcurrentHashMap<String, ConcurrentHashMap<BigInteger, X509Certificate>> certificates = new ConcurrentHashMap<>();
+
+    private ConcurrentHashMap<String, Credentials> credentialsMap = new ConcurrentHashMap<>();
+    /**
+     * 执行定时更新平台证书的线程池
+     */
+    private ScheduledExecutorService executor;
+
+    /**
+     * 内部验签器
+     */
+    private class DefaultVerifier implements Verifier {
+
+        private String merchantId;
+
+        private DefaultVerifier(String merchantId) {
+            this.merchantId = merchantId;
+        }
+
+        @Override
+        public boolean verify(String serialNumber, byte[] message, String signature) {
+            if (serialNumber.isEmpty() || message.length == 0 || signature.isEmpty()) {
+                throw new IllegalArgumentException("serialNumber或message或signature为空");
+            }
+            BigInteger serialNumber16Radix = new BigInteger(serialNumber, 16);
+            ConcurrentHashMap<BigInteger, X509Certificate> merchantCertificates = certificates.get(merchantId);
+            X509Certificate certificate = merchantCertificates.get(serialNumber16Radix);
+            if (certificate == null) {
+                log.error("商户证书为空,serialNumber:{}", serialNumber);
+                return false;
+            }
+            try {
+                Signature sign = Signature.getInstance("SHA256withRSA");
+                sign.initVerify(certificate);
+                sign.update(message);
+                return sign.verify(Base64.getDecoder().decode(signature));
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
+            } catch (SignatureException e) {
+                throw new RuntimeException("签名验证过程发生了错误", e);
+            } catch (InvalidKeyException e) {
+                throw new RuntimeException("无效的证书", e);
+            }
+        }
+
+        @Override
+        public X509Certificate getValidCertificate() {
+            X509Certificate certificate;
+            try {
+                certificate = CertificatesManager.this.getLatestCertificate(merchantId);
+            } catch (NotFoundException e) {
+                throw new NoSuchElementException("没有有效的微信支付平台证书");
+            }
+            return certificate;
+        }
+    }
+
+    private CertificatesManager() {
+    }
+
+    public static CertificatesManager getInstance() {
+        if (instance == null) {
+            synchronized (CertificatesManager.class) {
+                if (instance == null) {
+                    instance = new CertificatesManager();
+                }
+            }
+        }
+        return instance;
+    }
+
+    /**
+     * 增加需要自动更新平台证书的商户信息
+     *
+     * @param merchantId 商户号
+     * @param credentials 认证器
+     * @param apiV3Key APIv3密钥
+     * @throws IOException IO错误
+     * @throws GeneralSecurityException 通用安全错误
+     * @throws HttpCodeException HttpCode错误
+     */
+    public synchronized void putMerchant(String merchantId, Credentials credentials, byte[] apiV3Key)
+            throws IOException, GeneralSecurityException, HttpCodeException {
+        if (merchantId == null || merchantId.isEmpty()) {
+            throw new IllegalArgumentException("merchantId为空");
+        }
+        if (credentials == null) {
+            throw new IllegalArgumentException("credentials为空");
+        }
+        if (apiV3Key.length == 0) {
+            throw new IllegalArgumentException("apiV3Key为空");
+        }
+        // 添加或更新商户信息
+        if (certificates.get(merchantId) == null) {
+            certificates.put(merchantId, new ConcurrentHashMap<>());
+        }
+        initCertificates(merchantId, credentials, apiV3Key);
+        credentialsMap.put(merchantId, credentials);
+        apiV3Keys.put(merchantId, apiV3Key);
+        // 若没有executor,则启动定时更新证书任务
+        if (executor == null) {
+            beginScheduleUpdate();
+        }
+    }
+
+    /***
+    * 代理配置
+    *
+    * @param proxy 代理host
+    **/
+    public synchronized void setProxy(HttpHost proxy) {
+        this.proxy = proxy;
+    }
+
+    /**
+     * 停止自动更新平台证书,停止后无法再重新启动
+     */
+    public void stop() {
+        if (executor != null) {
+            try {
+                executor.shutdownNow();
+            } catch (Exception e) {
+                log.error("Executor shutdown now failed", e);
+            }
+        }
+    }
+
+    private X509Certificate getLatestCertificate(String merchantId)
+            throws NotFoundException {
+        if (merchantId == null || merchantId.isEmpty()) {
+            throw new IllegalArgumentException("merchantId为空");
+        }
+        Map<BigInteger, X509Certificate> merchantCertificates = certificates.get(merchantId);
+        if (merchantCertificates == null || merchantCertificates.isEmpty()) {
+            throw new NotFoundException("没有最新的平台证书,merchantId:" + merchantId);
+        }
+        X509Certificate latestCert = null;
+        for (X509Certificate x509Cert : merchantCertificates.values()) {
+            // 若latestCert为空或x509Cert的证书有效开始时间在latestCert之后,则更新latestCert
+            if (latestCert == null || x509Cert.getNotBefore().after(latestCert.getNotBefore())) {
+                latestCert = x509Cert;
+            }
+        }
+        try {
+            latestCert.checkValidity();
+            return latestCert;
+        } catch (CertificateExpiredException | CertificateNotYetValidException e) {
+            log.error("平台证书未生效或已过期,merchantId:{}", merchantId);
+        }
+        throw new NotFoundException("没有最新的平台证书,merchantId:" + merchantId);
+    }
+
+    /**
+     * 获取商户号为merchantId的验签器
+     *
+     * @param merchantId 商户号
+     * @return 验签器
+     * @throws NotFoundException merchantId/merchantCertificates/apiV3Key/credentials为空
+     */
+    public Verifier getVerifier(String merchantId) throws NotFoundException {
+        // 若商户信息不存在,返回错误
+        ConcurrentHashMap<BigInteger, X509Certificate> merchantCertificates = certificates.get(merchantId);
+        byte[] apiV3Key = apiV3Keys.get(merchantId);
+        Credentials credentials = credentialsMap.get(merchantId);
+        if (merchantId == null || merchantId.isEmpty()) {
+            throw new IllegalArgumentException("merchantId为空");
+        }
+        if (merchantCertificates == null || merchantCertificates.size() == 0) {
+            throw new NotFoundException("平台证书为空,merchantId:" + merchantId);
+        }
+        if (apiV3Key.length == 0) {
+            throw new NotFoundException("apiV3Key为空,merchantId:" + merchantId);
+
+        }
+        if (credentials == null) {
+            throw new NotFoundException("credentials为空,merchantId:" + merchantId);
+        }
+        return new DefaultVerifier(merchantId);
+    }
+
+
+    private void beginScheduleUpdate() {
+        executor = new SafeSingleScheduleExecutor();
+        Runnable runnable = () -> {
+            try {
+                Thread.currentThread().setName(SCHEDULE_UPDATE_CERT_THREAD_NAME);
+                log.info("Begin update Certificates.Date:{}", Instant.now());
+                updateCertificates();
+                log.info("Finish update Certificates.Date:{}", Instant.now());
+            } catch (Throwable t) {
+                log.error("Update Certificates failed", t);
+            }
+        };
+        executor.scheduleAtFixedRate(runnable, 0, UPDATE_INTERVAL_MINUTE, TimeUnit.MINUTES);
+    }
+
+    /**
+     * 下载和更新平台证书
+     *
+     * @param merchantId 商户号
+     * @param verifier 验签器
+     * @param credentials 认证器
+     * @param apiV3Key apiv3密钥
+     * @throws HttpCodeException Http返回码异常
+     * @throws IOException IO异常
+     * @throws GeneralSecurityException 通用安全性异常
+     */
+    private synchronized void downloadAndUpdateCert(String merchantId, Verifier verifier, Credentials credentials,
+            byte[] apiV3Key) throws HttpCodeException, IOException, GeneralSecurityException {
+        try (CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
+                .withCredentials(credentials)
+                .withValidator(verifier == null ? (response) -> true
+                        : new WechatPay2Validator(verifier))
+                .withProxy(proxy)
+                .build()) {
+            HttpGet httpGet = new HttpGet(CERT_DOWNLOAD_PATH);
+            httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString());
+            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+                int statusCode = response.getStatusLine().getStatusCode();
+                String body = EntityUtils.toString(response.getEntity());
+                if (statusCode == SC_OK) {
+                    Map<BigInteger, X509Certificate> newCertList = CertSerializeUtil.deserializeToCerts(apiV3Key, body);
+                    if (newCertList.isEmpty()) {
+                        log.warn("Cert list is empty");
+                        return;
+                    }
+                    ConcurrentHashMap<BigInteger, X509Certificate> merchantCertificates = certificates.get(merchantId);
+                    merchantCertificates.clear();
+                    merchantCertificates.putAll(newCertList);
+                } else {
+                    log.error("Auto update cert failed, statusCode = {}, body = {}", statusCode, body);
+                    throw new HttpCodeException("下载平台证书返回状态码异常,状态码为:" + statusCode);
+                }
+            }
+        }
+    }
+
+    /**
+     * 初始化平台证书,商户信息第一次被添加时调用
+     *
+     * @param merchantId 商户号
+     * @param credentials 认证器
+     * @param apiV3Key apiv3密钥
+     * @throws HttpCodeException Http返回码异常
+     * @throws IOException IO异常
+     * @throws GeneralSecurityException 通用安全性异常
+     */
+    private void initCertificates(String merchantId, Credentials credentials, byte[] apiV3Key)
+            throws HttpCodeException, IOException, GeneralSecurityException {
+        downloadAndUpdateCert(merchantId, null, credentials, apiV3Key);
+    }
+
+    /**
+     * 更新平台证书,每UPDATE_INTERVAL_MINUTE调用一次
+     */
+    private void updateCertificates() {
+        for (Map.Entry<String, Credentials> entry : credentialsMap.entrySet()) {
+            String merchantId = entry.getKey();
+            Credentials credentials = entry.getValue();
+            byte[] apiv3Key = apiV3Keys.get(merchantId);
+            Verifier verifier = new DefaultVerifier(merchantId);
+            try {
+                downloadAndUpdateCert(merchantId, verifier, credentials, apiv3Key);
+            } catch (Exception e) {
+                log.error("downloadAndUpdateCert Failed.merchantId:{}, e:{}", merchantId, e);
+            }
+        }
+    }
+}

+ 33 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/cert/SafeSingleScheduleExecutor.java

@@ -0,0 +1,33 @@
+package com.wechat.pay.contrib.apache.httpclient.cert;
+
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author lianup
+ * @since 0.3.0
+ */
+public class SafeSingleScheduleExecutor extends ScheduledThreadPoolExecutor {
+
+    private static final int MAX_QUEUE_CAPACITY = 1;
+    private static final int MAXIMUM_POOL_SIZE = 1;
+    private static final int CORE_POOL_SIZE = 1;
+
+    public SafeSingleScheduleExecutor() {
+        super(CORE_POOL_SIZE);
+        super.setMaximumPoolSize(MAXIMUM_POOL_SIZE);
+    }
+
+
+    @Override
+    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
+            TimeUnit unit) {
+        if (getQueue().size() < MAX_QUEUE_CAPACITY) {
+            return super.scheduleAtFixedRate(command, initialDelay, period, unit);
+        } else {
+            throw new RejectedExecutionException("当前任务数量超过最大队列最大数量");
+        }
+    }
+}

+ 20 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/constant/WechatPayHttpHeaders.java

@@ -0,0 +1,20 @@
+package com.wechat.pay.contrib.apache.httpclient.constant;
+
+/**
+ * 微信支付HTTP请求头相关常量
+ *
+ * @author Eric.Lee
+ */
+public final class WechatPayHttpHeaders {
+
+    public static final String REQUEST_ID = "Request-ID";
+    public static final String WECHAT_PAY_SERIAL = "Wechatpay-Serial";
+    public static final String WECHAT_PAY_SIGNATURE = "Wechatpay-Signature";
+    public static final String WECHAT_PAY_TIMESTAMP = "Wechatpay-Timestamp";
+    public static final String WECHAT_PAY_NONCE = "Wechatpay-Nonce";
+
+    private WechatPayHttpHeaders() {
+        // Don't allow instantiation
+    }
+
+}

+ 14 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/HttpCodeException.java

@@ -0,0 +1,14 @@
+package com.wechat.pay.contrib.apache.httpclient.exception;
+
+/**
+ * @author lianup
+ */
+public class HttpCodeException extends WechatPayException {
+
+
+    private static final long serialVersionUID = -1981192537895425762L;
+
+    public HttpCodeException(String message) {
+        super(message);
+    }
+}

+ 14 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/NotFoundException.java

@@ -0,0 +1,14 @@
+package com.wechat.pay.contrib.apache.httpclient.exception;
+
+/**
+ * @author lianup
+ */
+public class NotFoundException extends WechatPayException {
+
+
+    private static final long serialVersionUID = -1981192537895425762L;
+
+    public NotFoundException(String message) {
+        super(message);
+    }
+}

+ 17 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/ParseException.java

@@ -0,0 +1,17 @@
+package com.wechat.pay.contrib.apache.httpclient.exception;
+
+/**
+ * @author lianup
+ */
+public class ParseException extends WechatPayException {
+
+    private static final long serialVersionUID = 4300538230471368120L;
+
+    public ParseException(String message) {
+        super(message);
+    }
+
+    public ParseException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 15 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/ValidationException.java

@@ -0,0 +1,15 @@
+package com.wechat.pay.contrib.apache.httpclient.exception;
+
+/**
+ * @author lianup
+ */
+public class ValidationException extends WechatPayException {
+
+
+    private static final long serialVersionUID = -3473204321736989263L;
+
+
+    public ValidationException(String message) {
+        super(message);
+    }
+}

+ 18 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/exception/WechatPayException.java

@@ -0,0 +1,18 @@
+package com.wechat.pay.contrib.apache.httpclient.exception;
+
+/**
+ * @author lianup
+ */
+public abstract class WechatPayException extends Exception {
+
+    private static final long serialVersionUID = -5059029681600588999L;
+
+    public WechatPayException(String message) {
+        super(message);
+    }
+
+    public WechatPayException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

+ 119 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/notification/Notification.java

@@ -0,0 +1,119 @@
+package com.wechat.pay.contrib.apache.httpclient.notification;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 请求体解析结果
+ *
+ * @author lianup
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Notification {
+
+    @JsonProperty("id")
+    private String id;
+    @JsonProperty("create_time")
+    private String createTime;
+    @JsonProperty("event_type")
+    private String eventType;
+    @JsonProperty("resource_type")
+    private String resourceType;
+    @JsonProperty("summary")
+    private String summary;
+    @JsonProperty("resource")
+    private Resource resource;
+    private String decryptData;
+
+    @Override
+    public String toString() {
+        return "Notification{" +
+                "id='" + id + '\'' +
+                ", createTime='" + createTime + '\'' +
+                ", eventType='" + eventType + '\'' +
+                ", resourceType='" + resourceType + '\'' +
+                ", decryptData='" + decryptData + '\'' +
+                ", summary='" + summary + '\'' +
+                ", resource=" + resource +
+                '}';
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public String getEventType() {
+        return eventType;
+    }
+
+    public String getDecryptData() {
+        return decryptData;
+    }
+
+    public String getSummary() {
+        return summary;
+    }
+
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    public Resource getResource() {
+        return resource;
+    }
+
+    public void setDecryptData(String decryptData) {
+        this.decryptData = decryptData;
+    }
+
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public class Resource {
+
+        @JsonProperty("algorithm")
+        private String algorithm;
+        @JsonProperty("ciphertext")
+        private String ciphertext;
+        @JsonProperty("associated_data")
+        private String associatedData;
+        @JsonProperty("nonce")
+        private String nonce;
+        @JsonProperty("original_type")
+        private String originalType;
+
+        public String getAlgorithm() {
+            return algorithm;
+        }
+
+        public String getCiphertext() {
+            return ciphertext;
+        }
+
+        public String getAssociatedData() {
+            return associatedData;
+        }
+
+        public String getNonce() {
+            return nonce;
+        }
+
+        public String getOriginalType() {
+            return originalType;
+        }
+
+        @Override
+        public String toString() {
+            return "Resource{" +
+                    "algorithm='" + algorithm + '\'' +
+                    ", ciphertext='" + ciphertext + '\'' +
+                    ", associatedData='" + associatedData + '\'' +
+                    ", nonce='" + nonce + '\'' +
+                    ", originalType='" + originalType + '\'' +
+                    '}';
+        }
+    }
+
+}

+ 172 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/notification/NotificationHandler.java

@@ -0,0 +1,172 @@
+package com.wechat.pay.contrib.apache.httpclient.notification;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
+import com.wechat.pay.contrib.apache.httpclient.exception.ParseException;
+import com.wechat.pay.contrib.apache.httpclient.exception.ValidationException;
+import com.wechat.pay.contrib.apache.httpclient.notification.Notification.Resource;
+import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+
+/**
+ * @author lianup
+ */
+public class NotificationHandler {
+
+    private final Verifier verifier;
+    private final byte[] apiV3Key;
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+
+public NotificationHandler(Verifier verifier, byte[] apiV3Key) {
+    if (verifier == null) {
+        throw new IllegalArgumentException("verifier为空");
+    }
+    if (apiV3Key == null || apiV3Key.length == 0) {
+        throw new IllegalArgumentException("apiV3Key为空");
+    }
+    this.verifier = verifier;
+    this.apiV3Key = apiV3Key;
+}
+
+    /**
+     * 解析微信支付通知请求结果
+     *
+     * @param request 微信支付通知请求
+     * @return 微信支付通知报文解密结果
+     * @throws ValidationException 1.输入参数不合法 2.参数被篡改导致验签失败 3.请求和验证的平台证书不一致导致验签失败
+     * @throws ParseException 1.解析请求体为Json失败 2.请求体无对应参数 3.AES解密失败
+     */
+    public Notification parse(Request request)
+            throws ValidationException, ParseException {
+        // 验签
+        validate(request);
+        // 解析请求体
+        return parseBody(request.getBody());
+    }
+
+    private void validate(Request request) throws ValidationException {
+        if (request == null) {
+            throw new ValidationException("request为空");
+        }
+        String serialNumber = request.getSerialNumber();
+        byte[] message = request.getMessage();
+        String signature = request.getSignature();
+        if (serialNumber == null || serialNumber.isEmpty()) {
+            throw new ValidationException("serialNumber为空");
+        }
+        if (message == null || message.length == 0) {
+            throw new ValidationException("message为空");
+        }
+        if (signature == null || signature.isEmpty()) {
+            throw new ValidationException("signature为空");
+        }
+        if (!verifier.verify(serialNumber, message, signature)) {
+            String errorMessage = String
+                    .format("验签失败:serial=[%s] message=[%s] sign=[%s]", serialNumber, new String(message), signature);
+            throw new ValidationException(errorMessage);
+        }
+    }
+
+    /**
+     * 解析请求体
+     *
+     * @param body 请求体
+     * @return 解析结果
+     * @throws ParseException 解析body失败
+     */
+    private Notification parseBody(String body) throws ParseException {
+        ObjectReader objectReader = objectMapper.reader();
+        Notification notification;
+        try {
+            notification = objectReader.readValue(body, Notification.class);
+        } catch (IOException ioException) {
+            throw new ParseException("解析body失败,body:" + body, ioException);
+        }
+        validateNotification(notification);
+        setDecryptData(notification);
+        return notification;
+    }
+
+    /**
+     * 校验解析后的通知结果
+     *
+     * @param notification 通知结果
+     * @throws ParseException 参数不合法
+     */
+    private void validateNotification(Notification notification) throws ParseException {
+        if (notification == null) {
+            throw new ParseException("body解析为空");
+        }
+        String id = notification.getId();
+        if (id == null || id.isEmpty()) {
+            throw new ParseException("body不合法,id为空。body:" + notification.toString());
+        }
+        String createTime = notification.getCreateTime();
+        if (createTime == null || createTime.isEmpty()) {
+            throw new ParseException("body不合法,createTime为空。body:" + notification.toString());
+        }
+        String eventType = notification.getEventType();
+        if (eventType == null || eventType.isEmpty()) {
+            throw new ParseException("body不合法,eventType为空。body:" + notification.toString());
+        }
+        String summary = notification.getSummary();
+        if (summary == null || summary.isEmpty()) {
+            throw new ParseException("body不合法,summary为空。body:" + notification.toString());
+        }
+        String resourceType = notification.getResourceType();
+        if (resourceType == null || resourceType.isEmpty()) {
+            throw new ParseException("body不合法,resourceType为空。body:" + notification.toString());
+        }
+        Resource resource = notification.getResource();
+        if (resource == null) {
+            throw new ParseException("body不合法,resource为空。notification:" + notification.toString());
+        }
+        String algorithm = resource.getAlgorithm();
+        if (algorithm == null || algorithm.isEmpty()) {
+            throw new ParseException("body不合法,algorithm为空。body:" + notification.toString());
+        }
+        String originalType = resource.getOriginalType();
+        if (originalType == null || originalType.isEmpty()) {
+            throw new ParseException("body不合法,original_type为空。body:" + notification.toString());
+        }
+        String ciphertext = resource.getCiphertext();
+        if (ciphertext == null || ciphertext.isEmpty()) {
+            throw new ParseException("body不合法,ciphertext为空。body:" + notification.toString());
+        }
+        String nonce = resource.getNonce();
+        if (nonce == null || nonce.isEmpty()) {
+            throw new ParseException("body不合法,nonce为空。body:" + notification.toString());
+        }
+    }
+
+    /**
+     * 获取解密数据
+     *
+     * @param notification 解析body得到的通知结果
+     * @throws ParseException 解析body失败
+     */
+    private void setDecryptData(Notification notification) throws ParseException {
+
+        Resource resource = notification.getResource();
+        String getAssociateddData = "";
+        if (resource.getAssociatedData() != null) {
+            getAssociateddData = resource.getAssociatedData();
+        }
+        byte[] associatedData = getAssociateddData.getBytes(StandardCharsets.UTF_8);
+        byte[] nonce = resource.getNonce().getBytes(StandardCharsets.UTF_8);
+        String ciphertext = resource.getCiphertext();
+        AesUtil aesUtil = new AesUtil(apiV3Key);
+        String decryptData;
+        try {
+            decryptData = aesUtil.decryptToString(associatedData, nonce, ciphertext);
+        } catch (GeneralSecurityException e) {
+            throw new ParseException("AES解密失败,resource:" + resource.toString(), e);
+        }
+        notification.setDecryptData(decryptData);
+    }
+
+}

+ 88 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/notification/NotificationRequest.java

@@ -0,0 +1,88 @@
+package com.wechat.pay.contrib.apache.httpclient.notification;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author lianup
+ */
+public class NotificationRequest implements Request {
+
+    private final String serialNumber;
+    private final String signature;
+    private final byte[] message;
+    private final String body;
+
+    private NotificationRequest(String serialNumber, String signature, byte[] message, String body) {
+        this.serialNumber = serialNumber;
+        this.signature = signature;
+        this.message = message;
+        this.body = body;
+    }
+
+    @Override
+    public String getSerialNumber() {
+        return serialNumber;
+    }
+
+    @Override
+    public byte[] getMessage() {
+        return message;
+    }
+
+    @Override
+    public String getSignature() {
+        return signature;
+    }
+
+    @Override
+    public String getBody() {
+        return body;
+    }
+
+    public static class Builder {
+
+        private String serialNumber;
+        private String timestamp;
+        private String nonce;
+        private String signature;
+        private String body;
+
+        public Builder() {
+        }
+
+        public Builder withSerialNumber(String serialNumber) {
+            this.serialNumber = serialNumber;
+            return this;
+        }
+
+        public Builder withTimestamp(String timestamp) {
+            this.timestamp = timestamp;
+            return this;
+        }
+
+        public Builder withNonce(String nonce) {
+            this.nonce = nonce;
+            return this;
+        }
+
+        public Builder withSignature(String signature) {
+            this.signature = signature;
+            return this;
+        }
+
+        public Builder withBody(String body) {
+            this.body = body;
+            return this;
+        }
+
+        public NotificationRequest build() {
+            byte[] message = buildMessage();
+            return new NotificationRequest(serialNumber, signature, message, body);
+        }
+
+        private byte[] buildMessage() {
+            String verifyMessage = timestamp + "\n" + nonce + "\n" + body + "\n";
+            return verifyMessage.getBytes(StandardCharsets.UTF_8);
+        }
+    }
+}

+ 37 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/notification/Request.java

@@ -0,0 +1,37 @@
+package com.wechat.pay.contrib.apache.httpclient.notification;
+
+/**
+ * 通知请求体,包含验签所需信息和报文体
+ *
+ * @author lianup
+ */
+public interface Request {
+
+    /**
+     * 获取请求头Wechatpay-Serial
+     *
+     * @return serialNumber
+     */
+    String getSerialNumber();
+
+    /**
+     * 获取验签串
+     *
+     * @return message
+     */
+    byte[] getMessage();
+
+    /**
+     * 获取请求头Wechatpay-Signature
+     *
+     * @return signature
+     */
+    String getSignature();
+
+    /**
+     * 获取请求体
+     *
+     * @return body
+     */
+    String getBody();
+}

+ 50 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/util/AesUtil.java

@@ -0,0 +1,50 @@
+package com.wechat.pay.contrib.apache.httpclient.util;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * @author xy-peng
+ */
+public class AesUtil {
+
+    private static final String TRANSFORMATION = "AES/GCM/NoPadding";
+
+    private static final int KEY_LENGTH_BYTE = 32;
+    private static final int TAG_LENGTH_BIT = 128;
+
+    private final byte[] aesKey;
+
+    public AesUtil(byte[] key) {
+        if (key.length != KEY_LENGTH_BYTE) {
+            throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
+        }
+        this.aesKey = key;
+    }
+
+    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
+            throws GeneralSecurityException {
+        try {
+            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
+            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
+
+            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
+            cipher.init(Cipher.DECRYPT_MODE, key, spec);
+            cipher.updateAAD(associatedData);
+            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), StandardCharsets.UTF_8);
+
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+            throw new IllegalStateException(e);
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}

+ 64 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/util/CertSerializeUtil.java

@@ -0,0 +1,64 @@
+package com.wechat.pay.contrib.apache.httpclient.util;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author lianup
+ * @since 0.3.0
+ */
+public class CertSerializeUtil {
+
+    /**
+     * 反序列化证书并解密
+     *
+     * @param apiV3Key APIv3密钥
+     * @param body 下载证书的请求返回体
+     * @return 证书list
+     * @throws GeneralSecurityException 当证书过期或尚未生效时
+     * @throws IOException 当body不合法时
+     */
+    public static Map<BigInteger, X509Certificate> deserializeToCerts(byte[] apiV3Key, String body)
+            throws GeneralSecurityException, IOException {
+        AesUtil aesUtil = new AesUtil(apiV3Key);
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode dataNode = mapper.readTree(body).get("data");
+        Map<BigInteger, X509Certificate> newCertList = new HashMap<>();
+        if (dataNode != null) {
+            for (int i = 0, count = dataNode.size(); i < count; i++) {
+                JsonNode node = dataNode.get(i).get("encrypt_certificate");
+                //解密
+                String cert = aesUtil.decryptToString(
+                        node.get("associated_data").toString().replace("\"", "")
+                                .getBytes(StandardCharsets.UTF_8),
+                        node.get("nonce").toString().replace("\"", "")
+                                .getBytes(StandardCharsets.UTF_8),
+                        node.get("ciphertext").toString().replace("\"", ""));
+
+                CertificateFactory cf = CertificateFactory.getInstance("X509");
+                X509Certificate x509Cert = (X509Certificate) cf.generateCertificate(
+                        new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8))
+                );
+                try {
+                    x509Cert.checkValidity();
+                } catch (CertificateExpiredException | CertificateNotYetValidException ignored) {
+                    continue;
+                }
+                newCertList.put(x509Cert.getSerialNumber(), x509Cert);
+            }
+        }
+        return newCertList;
+    }
+
+}

+ 70 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/util/PemUtil.java

@@ -0,0 +1,70 @@
+package com.wechat.pay.contrib.apache.httpclient.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+/**
+ * @author xy-peng
+ */
+public class PemUtil {
+
+    public static PrivateKey loadPrivateKey(String privateKey) {
+        privateKey = privateKey
+                .replace("-----BEGIN PRIVATE KEY-----", "")
+                .replace("-----END PRIVATE KEY-----", "")
+                .replaceAll("\\s+", "");
+
+        try {
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
+
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("当前Java环境不支持RSA", e);
+        } catch (InvalidKeySpecException e) {
+            throw new RuntimeException("无效的密钥格式");
+        }
+    }
+
+    public static PrivateKey loadPrivateKey(InputStream inputStream) {
+        ByteArrayOutputStream os = new ByteArrayOutputStream(2048);
+        byte[] buffer = new byte[1024];
+        String privateKey;
+        try {
+            for (int length; (length = inputStream.read(buffer)) != -1; ) {
+                os.write(buffer, 0, length);
+            }
+            privateKey = os.toString("UTF-8");
+        } catch (IOException e) {
+            throw new IllegalArgumentException("无效的密钥", e);
+        }
+        return loadPrivateKey(privateKey);
+    }
+
+    public static X509Certificate loadCertificate(InputStream inputStream) {
+        try {
+            CertificateFactory cf = CertificateFactory.getInstance("X509");
+            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
+            cert.checkValidity();
+            return cert;
+        } catch (CertificateExpiredException e) {
+            throw new RuntimeException("证书已过期", e);
+        } catch (CertificateNotYetValidException e) {
+            throw new RuntimeException("证书尚未生效", e);
+        } catch (CertificateException e) {
+            throw new RuntimeException("无效的证书", e);
+        }
+    }
+
+}

+ 61 - 0
mybusiness/src/main/java/com/wechat/pay/contrib/apache/httpclient/util/RsaCryptoUtil.java

@@ -0,0 +1,61 @@
+package com.wechat.pay.contrib.apache.httpclient.util;
+
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+
+/**
+ * @author xy-peng
+ */
+public class RsaCryptoUtil {
+
+    private static final String TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
+
+    public static String encryptOAEP(String message, X509Certificate certificate) throws IllegalBlockSizeException {
+        return encrypt(message, certificate, TRANSFORMATION);
+    }
+
+    public static String encrypt(String message, X509Certificate certificate, String transformation) throws IllegalBlockSizeException {
+        try {
+            Cipher cipher = Cipher.getInstance(transformation);
+            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
+            byte[] data = message.getBytes(StandardCharsets.UTF_8);
+            byte[] ciphertext = cipher.doFinal(data);
+            return Base64.getEncoder().encodeToString(ciphertext);
+
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
+        } catch (InvalidKeyException e) {
+            throw new IllegalArgumentException("无效的证书", e);
+        } catch (IllegalBlockSizeException | BadPaddingException e) {
+            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
+        }
+    }
+
+    public static String decryptOAEP(String ciphertext, PrivateKey privateKey) throws BadPaddingException {
+        return decrypt(ciphertext, privateKey, TRANSFORMATION);
+    }
+
+    public static String decrypt(String ciphertext, PrivateKey privateKey, String transformation) throws BadPaddingException {
+        try {
+            Cipher cipher = Cipher.getInstance(transformation);
+            cipher.init(Cipher.DECRYPT_MODE, privateKey);
+            byte[] data = Base64.getDecoder().decode(ciphertext);
+            return new String(cipher.doFinal(data), StandardCharsets.UTF_8);
+
+        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
+            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
+        } catch (InvalidKeyException e) {
+            throw new IllegalArgumentException("无效的私钥", e);
+        } catch (BadPaddingException | IllegalBlockSizeException e) {
+            throw new BadPaddingException("解密失败");
+        }
+    }
+}

+ 172 - 0
mybusiness/src/main/resources/mapper/xhnWecharts/XhnWxMapper.xml

@@ -0,0 +1,172 @@
+<?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.ruoyi.business.xhn.mapper.XhnWxMapper">
+
+    <resultMap type="XhnWxVo" id="XhnWxVoResult">
+            <result property="openid" column="openid"/>
+            <result property="wechatName" column="wechat_name"/>
+            <result property="wechatCode" column="wechat_code"/>
+            <result property="wechatArgot" column="wechat_argot"/>
+            <result property="phone" column="phone"/>
+            <result property="giftName" column="gift_name"/>
+            <result property="giftNum" column="gift_num"/>
+            <result property="integralNum" column="integral_num"/>
+            <result property="createDate" column="create_date"/>
+    </resultMap>
+
+
+    <select id="getOpenBlindBoxList" parameterType="java.lang.String" resultMap="XhnWxVoResult">
+        SELECT
+            a.openid,
+            a.wechat_name,
+            a.wechat_code,
+            a.wechat_argot,
+            a.phone,
+            b.create_date
+        FROM
+            xhn_register_user a
+        JOIN xhn_consume_middle b ON a.openid = b.gain_openid
+        WHERE
+            b.type = '1'
+        and b.consume_openid= #{openid}
+        <if test="boxOpenid != null and boxOpenid != ''">and b.gain_openid=#{boxOpenid}</if>
+    </select>
+
+    <select id="getReceivedGiftList" parameterType="java.lang.String" resultMap="XhnWxVoResult">
+        SELECT
+            a.openid,
+            a.wechat_name,
+            a.wechat_code,
+            a.wechat_argot,
+            a.phone
+            c.gift_name,
+            b.gift_num,
+            b.create_date
+        FROM
+            xhn_register_user a
+        JOIN xhn_consume_middle b ON a.openid = b.consume_openid
+        join xhn_gift c on b.gift_id=c.id
+        WHERE
+            b.type = '2'
+        and b.gain_openid= #{openid}
+    </select>
+
+    <select id="getGiveGiftList" parameterType="java.lang.String" resultMap="XhnWxVoResult">
+        SELECT
+            a.openid,
+            a.wechat_name,
+            a.wechat_code,
+            a.wechat_argot,
+            a.phone,
+            c.gift_name,
+            b.gift_num,
+            concat('-',b.gift_num*c.gift_num) as integral_num,
+            b.create_date
+        FROM
+            xhn_register_user a
+        JOIN xhn_consume_middle b ON a.openid = b.gain_openid
+        JOIN xhn_gift c ON b.gift_id = c.id
+        WHERE
+            b.type = '2'
+        AND b.consume_openid = #{openid}
+        UNION ALL
+        SELECT
+            a.openid,
+            a.wechat_name,
+            a.wechat_code,
+            a.wechat_argot,
+            a.phone,
+            c.gift_name,
+            b.gift_num,
+            concat('+',b.gift_num*c.gift_num) as integral_num,
+            b.create_date
+        FROM
+            xhn_register_user a
+        JOIN xhn_consume_middle b ON a.openid = b.consume_openid
+        JOIN xhn_gift c ON b.gift_id = c.id
+        WHERE
+            b.type = '2'
+        AND b.gain_openid = #{openid}
+    </select>
+    <select id="getRandomBlindBox" parameterType="com.ruoyi.business.xhn.domain.RegisteruserUnpaid" resultType="com.ruoyi.business.xhn.domain.RegisteruserUnpaid">
+        SELECT
+            openid,
+            `code`,
+            height,
+            weight,
+            sex,
+            birth_date as birthDate,
+            constellation,
+            is_married as isMarried,
+            occupation,
+            work_nature as workNature,
+            education,
+            location,
+            ta_location as taLocation,
+            long_distance_love as longDistanceLove,
+            `character`,
+            ideal_type as idealType,
+            no_accepted as noAccepted,
+            delisting,
+            portrait_url as portraitUrl,
+            is_free as isFree,
+            promoter_ewm as promoterEwm
+        FROM
+            xhn_register_user
+
+        <where>
+            is_login != '2' and delisting = '0' and approval = '1' and openid != #{openid}
+            <if test="taLocation != null and taLocation != ''">AND ta_location like concat('%', #{taLocation}, '%')</if>
+            <if test="workNature != null and workNature != ''"> AND work_nature = #{workNature} </if>
+            <if test="education != null and education != ''"> AND education = #{education} </if>
+            <if test="constellation != null and constellation != ''"> AND constellation = #{constellation} </if>
+            <if test="startAge != null and startAge != '' and endAge != null and endAge != ''">
+                AND (DATE_FORMAT(NOW(), '%Y')-DATE_FORMAT(birth_date, '%Y')) BETWEEN #{startAge} AND #{endAge}
+            </if>
+            <if test="startHeight != null and startHeight != '' and endHeight != null and endHeight != ''">
+                AND height BETWEEN #{startHeight} AND #{endHeight}
+            </if>
+        </where>
+        ORDER BY
+            rand()
+        LIMIT 1
+    </select>
+    <select id="findAppUserByOpenid" parameterType="com.ruoyi.business.xhn.domain.RegisteruserUnpaid" resultType="com.ruoyi.business.xhn.domain.RegisteruserUnpaid">
+        SELECT
+        openid,
+        `code`,
+        height,
+        weight,
+        sex,
+        birth_date as birthDate,
+        constellation,
+        is_married as isMarried,
+        occupation,
+        work_nature as workNature,
+        education,
+        location,
+        ta_location as taLocation,
+        long_distance_love as longDistanceLove,
+        `character`,
+        ideal_type as idealType,
+        no_accepted as noAccepted,
+        delisting,
+        portrait_url as portraitUrl,
+        is_free as isFree,
+        promoter_ewm as promoterEwm
+        FROM
+        xhn_register_user
+        where openid = #{openid}
+    </select>
+
+    <select id="getKefuInfo" resultType="java.util.Map">
+        select user_name as userName,
+              email,
+              phonenumber,
+              avatar
+              from sys_user
+              where status='0' and del_flag='0' and remark like concat('%','客服', '%')
+    </select>
+</mapper>

+ 80 - 0
mybusiness/src/main/resources/mapper/xhnnotsingle/XhnActivityMiddleMapper.xml

@@ -0,0 +1,80 @@
+<?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.ruoyi.business.xhnnotsingle.activityMiddle.mapper.XhnActivityMiddleMapper">
+
+    <resultMap type="XhnActivityMiddle" id="XhnActivityMiddleResult">
+            <result property="id" column="id"/>
+            <result property="hdId" column="hd_id"/>
+            <result property="openid" column="openid"/>
+    </resultMap>
+
+    <sql id="selectXhnActivityMiddleVo">
+        select id, hd_id, openid
+        from xhn_activity_middle
+    </sql>
+
+    <select id="selectXhnActivityMiddleList" parameterType="XhnActivityMiddle" resultMap="XhnActivityMiddleResult">
+        <include refid="selectXhnActivityMiddleVo"/>
+        <where>
+                        <if test="hdId != null ">
+                            and hd_id = #{hdId}
+                        </if>
+                        <if test="openid != null ">
+                            and openid = #{openid}
+                        </if>
+        </where>
+    </select>
+
+    <select id="selectXhnActivityMiddleById" parameterType="String"
+            resultMap="XhnActivityMiddleResult">
+            <include refid="selectXhnActivityMiddleVo"/>
+            where id = #{id}
+    </select>
+
+    <insert id="insertXhnActivityMiddle" parameterType="XhnActivityMiddle">
+        insert into xhn_activity_middle
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+                    <if test="id != null">id,
+                    </if>
+                    <if test="hdId != null">hd_id,
+                    </if>
+                    <if test="openid != null">openid,
+                    </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+                    <if test="id != null">#{id},
+                    </if>
+                    <if test="hdId != null">#{hdId},
+                    </if>
+                    <if test="openid != null">#{openid},
+                    </if>
+        </trim>
+    </insert>
+
+    <update id="updateXhnActivityMiddle" parameterType="XhnActivityMiddle">
+        update xhn_activity_middle
+        <trim prefix="SET" suffixOverrides=",">
+                    <if test="hdId != null">hd_id =
+                        #{hdId},
+                    </if>
+                    <if test="openid != null">openid =
+                        #{openid},
+                    </if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteXhnActivityMiddleById" parameterType="String">
+        delete from xhn_activity_middle where id = #{id}
+    </delete>
+
+    <delete id="deleteXhnActivityMiddleByIds" parameterType="String">
+        delete from xhn_activity_middle where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+</mapper>

+ 92 - 0
mybusiness/src/main/resources/mapper/xhnnotsingle/XhnAliyunSmsMapper.xml

@@ -0,0 +1,92 @@
+<?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.ruoyi.business.xhnnotsingle.aliyunSms.mapper.XhnAliyunSmsMapper">
+
+    <insert id="insertXhnAAliyunSms" parameterType="XhnAliyunSms">
+        insert into xhn_aliyun_sms
+        <trim prefix="(" suffix=")" suffixOverrides="," >
+            <if test="id != null" >
+                `id`,
+            </if>
+            <if test="code != null" >
+                `code`,
+            </if>
+            <if test="bizId != null" >
+                `biz_id`,
+            </if>
+            <if test="requestId != null" >
+                `request_id`,
+            </if>
+            <if test="message != null" >
+                `message`,
+            </if>
+            <if test="count != null" >
+                `count`,
+            </if>
+            <if test="signName != null" >
+                `sign_name`,
+            </if>
+            <if test="tempCode != null" >
+                `temp_code`,
+            </if>
+            <if test="dataJson != null" >
+                `data_json`,
+            </if>
+            <if test="createBy != null" >
+                `create_by`,
+            </if>
+            <if test="createDate != null" >
+                `create_date`,
+            </if>
+            <if test="openid != null" >
+                `openid`,
+            </if>
+            <if test="phone != null" >
+                `phone`,
+            </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides="," >
+            <if test="id != null" >
+                #{id},
+            </if>
+            <if test="code != null" >
+                #{code},
+            </if>
+            <if test="bizId != null" >
+                #{bizId},
+            </if>
+            <if test="requestId != null" >
+                #{requestId},
+            </if>
+            <if test="message != null" >
+                #{message},
+            </if>
+            <if test="count != null" >
+                #{count},
+            </if>
+            <if test="signName != null" >
+                #{signName},
+            </if>
+            <if test="tempCode != null" >
+                #{tempCode},
+            </if>
+            <if test="dataJson != null" >
+                #{dataJson},
+            </if>
+            <if test="createBy != null" >
+                #{createBy},
+            </if>
+            <if test="createDate != null" >
+                #{createDate},
+            </if>
+            <if test="openid != null" >
+                #{openid},
+            </if>
+            <if test="phone != null" >
+                #{phone},
+            </if>
+        </trim>
+    </insert>
+</mapper>

+ 124 - 0
mybusiness/src/main/resources/mapper/xhnnotsingle/XhnConsumeMiddleMapper.xml

@@ -0,0 +1,124 @@
+<?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.ruoyi.business.xhnnotsingle.consumemiddle.mapper.XhnConsumeMiddleMapper">
+
+    <resultMap type="XhnConsumeMiddle" id="XhnConsumeMiddleResult">
+            <result property="id" column="id"/>
+            <result property="consumeOpenid" column="consume_openid"/>
+            <result property="gainOpenid" column="gain_openid"/>
+            <result property="type" column="type"/>
+            <result property="giftId" column="gift_id"/>
+            <result property="giftNum" column="gift_num"/>
+            <result property="createDate" column="create_date"/>
+    </resultMap>
+
+    <sql id="selectXhnConsumeMiddleVo">
+        select id, consume_openid, gain_openid, type, gift_id, gift_num,create_date
+        from xhn_consume_middle
+    </sql>
+
+    <select id="selectXhnConsumeMiddleList" parameterType="XhnConsumeMiddle" resultMap="XhnConsumeMiddleResult">
+        <include refid="selectXhnConsumeMiddleVo"/>
+        <where>
+                        <if test="consumeOpenid != null  and consumeOpenid != ''">
+                            and consume_openid = #{consumeOpenid}
+                        </if>
+                        <if test="gainOpenid != null  and gainOpenid != ''">
+                            and gain_openid = #{gainOpenid}
+                        </if>
+                        <if test="type != null  and type != ''">
+                            and type = #{type}
+                        </if>
+                        <if test="giftId != null ">
+                            and gift_id = #{giftId}
+                        </if>
+                        <if test="giftNum != null ">
+                            and gift_num = #{giftNum}
+                        </if>
+                        <if test="createDate != null ">
+                            and create_date = #{createDate}
+                        </if>
+        </where>
+    </select>
+
+    <select id="selectXhnConsumeMiddleById" parameterType="String"
+            resultMap="XhnConsumeMiddleResult">
+            <include refid="selectXhnConsumeMiddleVo"/>
+            where id = #{id}
+    </select>
+
+    <insert id="insertXhnConsumeMiddle" parameterType="XhnConsumeMiddle">
+        insert into xhn_consume_middle
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+                    <if test="id != null">id,
+                    </if>
+                    <if test="consumeOpenid != null">consume_openid,
+                    </if>
+                    <if test="gainOpenid != null">gain_openid,
+                    </if>
+                    <if test="type != null">type,
+                    </if>
+                    <if test="giftId != null">gift_id,
+                    </if>
+                    <if test="giftNum != null">gift_num,
+                    </if>
+                    <if test="createDate != null">create_date,
+                    </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+                    <if test="id != null">#{id},
+                    </if>
+                    <if test="consumeOpenid != null">#{consumeOpenid},
+                    </if>
+                    <if test="gainOpenid != null">#{gainOpenid},
+                    </if>
+                    <if test="type != null">#{type},
+                    </if>
+                    <if test="giftId != null">#{giftId},
+                    </if>
+                    <if test="giftNum != null">#{giftNum},
+                    </if>
+                    <if test="createDate != null">#{createDate},
+                    </if>
+        </trim>
+    </insert>
+
+    <update id="updateXhnConsumeMiddle" parameterType="XhnConsumeMiddle">
+        update xhn_consume_middle
+        <trim prefix="SET" suffixOverrides=",">
+                    <if test="consumeOpenid != null">consume_openid =
+                        #{consumeOpenid},
+                    </if>
+                    <if test="gainOpenid != null">gain_openid =
+                        #{gainOpenid},
+                    </if>
+                    <if test="type != null">type =
+                        #{type},
+                    </if>
+                    <if test="giftId != null">gift_id =
+                        #{giftId},
+                    </if>
+                    <if test="giftNum != null">gift_num =
+                        #{giftNum},
+                    </if>
+                    <if test="createDate != null">create_date =
+                        #{createDate},
+                    </if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteXhnConsumeMiddleById" parameterType="String">
+        delete from xhn_consume_middle where id = #{id}
+    </delete>
+
+    <delete id="deleteXhnConsumeMiddleByIds" parameterType="String">
+        delete from xhn_consume_middle where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+</mapper>

+ 114 - 0
mybusiness/src/main/resources/mapper/xhnnotsingle/XhnIntegralMapper.xml

@@ -0,0 +1,114 @@
+<?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.ruoyi.business.xhnnotsingle.integral.mapper.XhnIntegralMapper">
+
+    <resultMap type="XhnIntegral" id="XhnIntegralResult">
+            <result property="id" column="id"/>
+            <result property="money" column="money"/>
+            <result property="consumeDate" column="consume_date"/>
+            <result property="orderType" column="order_type"/>
+            <result property="userOpenid" column="user_openid"/>
+            <result property="purpose" column="purpose"/>
+    </resultMap>
+
+    <sql id="selectXhnIntegralVo">
+        select id, money, consume_date, order_type, user_openid, purpose
+        from xhn_integral
+    </sql>
+
+    <select id="selectXhnIntegralList" parameterType="XhnIntegral" resultMap="XhnIntegralResult">
+        <include refid="selectXhnIntegralVo"/>
+        <where>
+                        <if test="money != null ">
+                            and money = #{money}
+                        </if>
+                        <if test="consumeDate != null  and consumeDate != ''">
+                            and consume_date = #{consumeDate}
+                        </if>
+                        <if test="orderType != null  and orderType != ''">
+                            and order_type = #{orderType}
+                        </if>
+                        <if test="userOpenid != null  and userOpenid != ''">
+                            and user_openid = #{userOpenid}
+                        </if>
+                        <if test="purpose != null  and purpose != ''">
+                            and purpose = #{purpose}
+                        </if>
+        </where>
+        order by DATE_FORMAT(consume_date,'%Y-%m-%d %H:%i:%S') desc
+    </select>
+
+    <select id="selectXhnIntegralById" parameterType="String"
+            resultMap="XhnIntegralResult">
+            <include refid="selectXhnIntegralVo"/>
+            where id = #{id}
+    </select>
+
+    <insert id="insertXhnIntegral" parameterType="XhnIntegral">
+        insert into xhn_integral
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+                    <if test="id != null">id,
+                    </if>
+                    <if test="money != null">money,
+                    </if>
+                    <if test="consumeDate != null">consume_date,
+                    </if>
+                    <if test="orderType != null">order_type,
+                    </if>
+                    <if test="userOpenid != null">user_openid,
+                    </if>
+                    <if test="purpose != null">purpose,
+                    </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+                    <if test="id != null">#{id},
+                    </if>
+                    <if test="money != null">#{money},
+                    </if>
+                    <if test="consumeDate != null">#{consumeDate},
+                    </if>
+                    <if test="orderType != null">#{orderType},
+                    </if>
+                    <if test="userOpenid != null">#{userOpenid},
+                    </if>
+                    <if test="purpose != null">#{purpose},
+                    </if>
+        </trim>
+    </insert>
+
+    <update id="updateXhnIntegral" parameterType="XhnIntegral">
+        update xhn_integral
+        <trim prefix="SET" suffixOverrides=",">
+                    <if test="money != null">money =
+                        #{money},
+                    </if>
+                    <if test="consumeDate != null">consume_date =
+                        #{consumeDate},
+                    </if>
+                    <if test="orderType != null">order_type =
+                        #{orderType},
+                    </if>
+                    <if test="userOpenid != null">user_openid =
+                        #{userOpenid},
+                    </if>
+                    <if test="purpose != null">purpose =
+                        #{purpose},
+                    </if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteXhnIntegralById" parameterType="String">
+        delete from xhn_integral where id = #{id}
+    </delete>
+
+    <delete id="deleteXhnIntegralByIds" parameterType="String">
+        delete from xhn_integral where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+</mapper>

+ 102 - 0
mybusiness/src/main/resources/mapper/xhnnotsingle/XhnOrderMapper.xml

@@ -0,0 +1,102 @@
+<?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.ruoyi.business.xhnnotsingle.order.mapper.XhnOrderMapper">
+
+    <resultMap type="XhnOrder" id="XhnOrderResult">
+            <result property="orderCode" column="order_code"/>
+            <result property="money" column="money"/>
+            <result property="orderDate" column="order_date"/>
+            <result property="orderSuccessDate" column="order_success_date"/>
+            <result property="state" column="state"/>
+    </resultMap>
+
+    <sql id="selectXhnOrderVo">
+        select order_code, money, order_date, state
+        from xhn_order
+    </sql>
+
+    <select id="selectXhnOrderList" parameterType="XhnOrder" resultMap="XhnOrderResult">
+        <include refid="selectXhnOrderVo"/>
+        <where>
+                        <if test="money != null  and money != ''">
+                            and money = #{money}
+                        </if>
+                        <if test="orderDate != null  and orderDate != ''">
+                            and order_date = #{orderDate}
+                        </if>
+                        <if test="orderSuccessDate != null  and orderSuccessDate != ''">
+                            and order_success_date = #{orderSuccessDate}
+                        </if>
+                        <if test="state != null  and state != ''">
+                            and state = #{state}
+                        </if>
+        </where>
+    </select>
+
+    <select id="selectXhnOrderByOrderCode" parameterType="String"
+            resultMap="XhnOrderResult">
+            <include refid="selectXhnOrderVo"/>
+            where order_code = #{orderCode}
+    </select>
+
+    <insert id="insertXhnOrder" parameterType="XhnOrder">
+        insert into xhn_order
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+                    <if test="orderCode != null">order_code,
+                    </if>
+                    <if test="money != null">money,
+                    </if>
+                    <if test="orderDate != null">order_date,
+                    </if>
+                    <if test="state != null">state,
+                    </if>
+                    <if test="orderSuccessDate != null">order_success_date,
+                    </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+                    <if test="orderCode != null">#{orderCode},
+                    </if>
+                    <if test="money != null">#{money},
+                    </if>
+                    <if test="orderDate != null">#{orderDate},
+                    </if>
+                    <if test="state != null">#{state},
+                    </if>
+                    <if test="orderSuccessDate != null">#{orderSuccessDate},
+                    </if>
+        </trim>
+    </insert>
+
+    <update id="updateXhnOrder" parameterType="XhnOrder">
+        update xhn_order
+        <trim prefix="SET" suffixOverrides=",">
+                    <if test="money != null">money =
+                        #{money},
+                    </if>
+                    <if test="orderDate != null">order_date =
+                        #{orderDate},
+                    </if>
+                    <if test="state != null">state =
+                        #{state},
+                    </if>
+                    <if test="orderSuccessDate != null">order_success_date =
+                        #{orderSuccessDate},
+                    </if>
+        </trim>
+        where order_code = #{orderCode}
+    </update>
+
+    <delete id="deleteXhnOrderByOrderCode" parameterType="String">
+        delete from xhn_order where order_code = #{orderCode}
+    </delete>
+
+    <delete id="deleteXhnOrderByOrderCodes" parameterType="String">
+        delete from xhn_order where order_code in
+        <foreach item="orderCode" collection="array" open="(" separator="," close=")">
+            #{orderCode}
+        </foreach>
+    </delete>
+
+</mapper>

+ 118 - 0
mybusiness/src/main/resources/templates/appxhn/activity.html

@@ -0,0 +1,118 @@
+<!doctype html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+<meta charset="utf-8">
+<title>无标题文档</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no,minimum-scale=1.0, maximum-scale=1.0">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/main.css}">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/jbxx.css}">
+<script th:src="@{/tuodanweb/js/jquery-1.7.2.min.js}"></script>
+<script th:inline="javascript">
+    var ctx = [[@{/}]];
+</script>
+</head>
+
+<body>
+<div class="activity-zt" th:object="${huodongInfo}">
+    <input type="hidden" id="nan" th:value="*{nanMoney}"/>
+    <input type="hidden" id="nv" th:value="*{nvMoney}"/>
+    <input type="hidden" id="huodongid" th:value="${id}"/>
+    <input type="hidden" id="sex" th:value="${brUser.sex}"/>
+
+
+    <div class="banner"><img th:src="*{zsPath}"/></div>
+	<div class="xgjs">
+        <h2 class="hdzt">[[*{name}]]</h2>
+        <div class="bmf">
+        	<i>报名费</i>
+        	<span>男士[[*{nanMoney}]]元</span>
+            <span>女士[[*{nvMoney}]]元</span>
+        </div>
+    </div>
+    <div class="xgjs">
+    	<h3 class="jbxx-title">活动信息</h3>
+        <dl class="hdxx">
+        	<dd><i class="time">时间</i><span>[[*{activityStartDateYm}]]~[[*{activityEndDateYm}]]</span></dd>
+            <dd><i class="place">地点</i><span>[[*{activityAddress}]]</span></dd>
+            <dd><i class="peonum">人数</i><span>已报名[[${bmUserNum}]]人,人数不限</span></dd>
+            <dd class="bor-none"><i class="start">发起</i><span>网站系统</span></dd>
+        </dl>
+    </div>
+    <div class="xgjs">
+    	<p class="dy">获取进展、活动答疑</p>
+        <a class="lxkf"><img th:src="@{/tuodanweb/images/navpic-11.png}" onclick=" window.location.href= ctx+'xhn/getKefuInfo';"/>联系客服</a>
+        <div class="clear"></div>
+    </div>
+    <div class="xgjs">
+    	<h3 class="jbxx-title">活动介绍</h3>
+        <div class="hd-cont"><p>[[*{activityContent}]]</p></div>
+    </div>
+</div>
+<div class="footer">
+	<a class="home" onclick="window.location.href = ctx+'xhn/index'"><img th:src="@{/tuodanweb/images/home.png}"/>首页</a>
+    <th:block th:if="${!date}">
+        <a class="bm_but bmz">报名已结束</a>
+    </th:block>
+    <th:block th:if="${date}">
+        <a th:if="${isBaoMing == 0}" class="bm_but bmz" onclick="wechatPay()">我要报名</a>
+        <a th:if="${isBaoMing == 1}" class="bm_but bmz">已报名</a>
+    </th:block>
+</div>
+<script type="text/javascript">
+    /**
+     * 微信支付
+     */
+    function wechatPay() {
+        var total;
+        if($("#sex").val() == '1'){//男
+            total= parseInt($("#nan").val())*100;
+        }else if($("#sex").val() == '2'){//女
+            total= parseInt($("#nv").val())*100;
+        }
+
+        $.ajax({
+            type: "post",
+            url: ctx + "xhn/getPayPrepayId",
+            data: {
+                description:'脱单便利店活动报名',
+                total:total,
+                purpose:'3',
+                boxOpenid:$("#huodongid").val()
+            },
+            success: function (res) {
+                if (typeof WeixinJSBridge == "undefined") {
+                    if (document.addEventListener) {
+                        document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
+                    } else if (document.attachEvent) {
+                        document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
+                        document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
+                    }
+                } else {
+                    onBridgeReady(res.data);
+                }
+            }
+        });
+    }
+
+    function onBridgeReady(data) {
+        WeixinJSBridge.invoke('getBrandWCPayRequest', {
+                "appId": 'wx19ac981693d1a79a',     //公众号ID,由商户传入
+                "timeStamp": data.timeStamp,     //时间戳,自1970年以来的秒数
+                "nonceStr": data.nonceStr,      //随机串
+                "package": data.package_,
+                "signType": "RSA",     //微信签名方式:
+                "paySign": data.paySign //微信签名
+            },
+            function(res) {
+                if (res.err_msg == "get_brand_wcpay_request:ok") {
+                    // 使用以上方式判断前端返回,微信团队郑重提示:
+                    //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
+                    setTimeout(() => {
+                        window.location.href= ctx+'xhn/activityInfo?huodongId='+$("#huodongid").val();
+                    }, 2000)
+                }
+            });
+    }
+</script>
+</body>
+</html>

+ 29 - 0
mybusiness/src/main/resources/templates/appxhn/bill.html

@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+	<head>
+		<meta charset="utf-8">
+		<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+		<meta content="yes" name="apple-mobile-web-app-capable" />
+		<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+		<meta content="telephone=no" name="format-detection" />
+		<title>账单</title>
+		<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+		<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+	</head>
+	<body>
+
+		<div class="container wid_con2">
+			<div class="yjtx_list">
+				<h1>账单明细</h1>
+				<div class="tx_li" th:each="bill : ${bill}">
+					<h4 th:if="${bill.orderType == '1'}">¥[[${bill.money}]]</h4>
+					<h4 th:if="${bill.orderType == '2'}">$[[${bill.money}]]</h4>
+					<div class="tx_txt">
+						<p>[[${bill.purpose}]]</p>
+						<span>[[${bill.consumeDate}]]</span>
+					</div>
+				</div>
+			</div>
+		</div>
+	</body>
+</html>

+ 235 - 0
mybusiness/src/main/resources/templates/appxhn/chaitamh.html

@@ -0,0 +1,235 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+	<head>
+		<meta charset="utf-8">
+		<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+		<meta content="yes" name="apple-mobile-web-app-capable" />
+		<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+		<meta content="telephone=no" name="format-detection" />
+		<title>拆TA盲盒</title>
+		<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+		<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+	</head>
+	<body>
+		<div class="container wid_con2" th:object="${user}">
+			<div class="ctmh_bg">
+				<div class="ctmh">
+					<i class="nv"></i>
+					<h4>编号:[[*{code}]]</h4>
+					<p>[[*{location}]]</p>
+				</div>
+				<div class="ctmh_txt">
+					<div class="ctmh_t">
+						<h5>[[*{isMarried}]]</h5>
+						<p>婚姻状态</p>
+					</div>
+					<div class="ctmh_t">
+						<h5>[[*{height}]]CM</h5>
+						<p>身高</p>
+					</div>
+					<div class="ctmh_t">
+						<h5>[[*{constellation}]]</h5>
+						<p>星座</p>
+					</div>
+				</div>
+			</div>
+			<div class="ctmh_div">
+				<h4>基本信息</h4>
+				<div class="ctmh_hei">
+				<div class="mh_column">
+					<div class="ctmh_li" onclick="openBox()">
+						<i class="name"></i>
+						<p id="wechat_name_text">拆开后可见</p>
+						<span id="wechat_name">昵称</span>
+					</div>
+					<div class="ctmh_li center">
+						<i class="sex"></i>
+						<p th:if="*{sex=='1'}">男</p>
+						<p th:if="*{sex=='2'}">女</p>
+						<span>性别</span>
+					</div>
+					<div class="ctmh_li">
+						<i class="hyzk"></i>
+						<p>[[*{isMarried}]]</p>
+						<span>婚姻状况</span>
+					</div>
+				</div>
+				<div class="mh_column">
+					<div class="ctmh_li">
+						<i class="data"></i>
+						<p>[[*{birthDate}]]</p>
+						<span>生日</span>
+					</div>
+					<div class="ctmh_li center">
+						<i class="xz"></i>
+						<p>[[*{constellation}]]</p>
+						<span>星座</span>
+					</div>
+					<div class="ctmh_li">
+						<i class="tz"></i>
+						<p>[[*{weight}]]KG</p>
+						<span>体重</span>
+					</div>
+				</div>
+				<div class="mh_column">
+					<div class="ctmh_li">
+						<i class="sg"></i>
+						<p>[[*{height}]]CM</p>
+						<span>身高</span>
+					</div>
+					<div class="ctmh_li center">
+						<i class="zy"></i>
+						<p>[[*{occupation}]]</p>
+						<span>职业</span>
+					</div>
+					<div class="ctmh_li">
+						<i class="gzxz"></i>
+						<p>[[*{workNature}]]</p>
+						<span>工作性质</span>
+					</div>
+				</div>
+				<div class="mh_column">
+					<div class="ctmh_li" onclick="openBox()">
+						<i class="xl"></i>
+						<p id="education_text">拆开后可见</p>
+						<span id="education">学历</span>
+					</div>
+					<div class="ctmh_li center">
+						<i class="dq"></i>
+						<p>[[*{location}]]</p>
+						<span>地区</span>
+					</div>
+					<div class="ctmh_li">
+						<i class="lxwz"></i>
+						<p>[[*{taLocation}]]</p>
+						<span>理想TA所在</span>
+					</div>
+				</div>
+				<div class="mh_column last_column">
+					<div class="ctmh_li">
+						<i class="sfyd"></i>
+						<p>[[*{longDistanceLove}]]</p>
+						<span>是否接受异地</span>
+					</div>
+				</div>
+				</div>
+				<div class="sx_tap">
+					<i class="yc_x"><img th:src="@{/tuodanweb/images/more.png}"/></i>
+				</div>
+			</div>
+
+			<div class="ctmh_div2">
+				<h4>性格介绍</h4>
+				<p class="ctmh_p">[[*{character}]]</p>
+			</div>
+			<div class="ctmh_div2">
+				<h4>理想型</h4>
+				<p class="ctmh_p">[[*{idealType}]]</p>
+			</div>
+			<div class="ctmh_div2">
+				<h4>不能接受类型</h4>
+				<p class="ctmh_p">[[*{noAccepted}]]</p>
+			</div>
+			<div class="ctmh_div2">
+				<h4>微信暗号</h4>
+				<p class="ctmh_p" id="wechat_argot" onclick="openBox()">拆开后可见</p>
+			</div>
+			<div class="ctmh_div2">
+				<h4>微信号</h4>
+				<p class="ctmh_p" id="wechat_code" onclick="openBox()">拆开后可见</p>
+			</div>
+			<div style="display: none;" id="gift">
+				<div th:each="gift : ${giftList}">
+					<img th:src="${gift.giftPath}" style="width: 10%">
+					<span>[[${gift.giftName}]]</span>
+					<span>[[${gift.giftNum}]]</span>
+					<input type="number" th:id="${gift.id}">
+					<button type="button" th:onclick="giftGive([[${gift.id}]])">赠送</button>
+				</div>
+			</div>
+			<input type="hidden" th:value="*{isFree}" id="isFree"/>
+			<input type="hidden" th:value="${bropenid}" id="openid"/>
+			<input type="hidden" th:value="*{openid}" id="boxOpenid"/>
+		</div>
+		<script th:inline="javascript">
+            var ctx = [[@{/}]];
+			var url = window.location.href;
+		</script>
+		<script type="text/javascript">
+			$(function(){
+				$(".sx_tap").on('click',function(){
+
+					if($(this).siblings('.ctmh_hei').css('overflow')=='hidden'){
+					$(this).siblings('.ctmh_hei').css({'overflow':'visible','height':'auto'});
+					$(this).children('i').attr('class','yc_s');
+					}else{
+					$(this).siblings('.ctmh_hei').css({'overflow':'hidden','height':'20rem'});
+					$(this).children('i').attr('class','yc_x');
+					}
+				})
+                getOpenBlindBox();
+			});
+
+            /**
+			 * 初始化加载查询登录人是否已经开启过了当前盲盒信息
+             */
+			function getOpenBlindBox(){
+                $.ajax({
+                    type: "get",
+                    url: ctx + "xhn/getOpenBlindBoxList",
+                    data: {
+                        "openid": $("#openid").val(),
+                        "boxOpenid": $("#boxOpenid").val()
+                    },
+                    success: function (res) {
+                        if(res.data.length > 0){
+                            getBoxUserInfo();
+                        }
+                    }
+                });
+			}
+            /**
+             * 二次调用接口获取盲盒的加密信息
+             */
+			function getBoxUserInfo() {
+                $.ajax({
+                    type: "get",
+                    url: ctx + "xhn/boxUserInfo",
+                    data: {
+                        "boxOpenid": $("#boxOpenid").val()
+                    },
+                    success: function (res) {
+                        $("#wechat_name_text").html("昵称");
+                        $("#wechat_name").html(res.data.wechatName);
+                        $("#education_text").html("学历");
+                        $("#education").html(res.data.education);
+                        $("#wechat_argot").html(res.data.wechatArgot);
+                        $("#wechat_code").html(res.data.wechatCode);
+                        $("#gift").show();
+                    }
+                });
+            }
+            function openBox(){
+                window.location.href = ctx+'xhn/openBox?hdUrl='+url+'&boxOpenid='+$("#boxOpenid").val();
+			}
+
+			function giftGive(giftId){
+                $.ajax({
+                    type: "post",
+                    url: ctx + "xhn/giftGive",
+                    data: {
+                        openid:$("#openid").val(),
+						bOpenid:$("#boxOpenid").val(),
+						giftId:giftId,
+						giftNum:$("#"+giftId).val()
+                    },
+                    success: function (res) {
+                        if(res.code == 0){
+                            alert(res.msg);
+						}
+                    }
+                });
+            }
+		</script>
+	</body>
+</html>

+ 24 - 0
mybusiness/src/main/resources/templates/appxhn/gywm.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+	<head>
+		<meta charset="utf-8">
+		<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+		<meta content="yes" name="apple-mobile-web-app-capable" />
+		<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+		<meta content="telephone=no" name="format-detection" />
+		<title>关于我们</title>
+		<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+		<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+	</head>
+	<body>
+		<img th:src="@{/tuodanweb/images/gywmbanner.jpg}" class="gywmbanner"/>
+		<div class="container">
+			<p class="gywm_p">1、拆开其他会员盲盒得到资料后不可以转赠或传播一经发现后,将会下架你的盲盒并拉入本店黑名单,严重者将追究其法律责任。
+</p>
+			<p class="gywm_p">2、禁止辱骂,淫秽,约P行为,如果您与本店会员接触过程中,发现了以上行为请第一时间截图保存聊天记录,发给本店客服!</p>
+			<p class="gywm_p">3、我们这里不是相亲,盲盒拆开后无法退回所以请确定后拆开,拆开后发现对方已经脱单请联系客服,可以重新更换。</p>
+			<p class="gywm_p">4、一旦您脱单了,或者不想再接触其他人了请联系客服下架您的盲盒。</p>
+
+		</div>
+	</body>
+</html>

+ 694 - 0
mybusiness/src/main/resources/templates/appxhn/index.html

@@ -0,0 +1,694 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+	<head>
+		<meta charset="utf-8">
+		<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+		<meta content="yes" name="apple-mobile-web-app-capable" />
+		<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+		<meta content="telephone=no" name="format-detection" />
+		<title>脱单便利店</title>
+		<link th:href="@{/tuodanweb/css/swiper.min.css}" rel="stylesheet" />
+		<link th:href="@{/tuodanweb/css/public2.css}" rel="stylesheet" />
+		<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+		<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+		<script th:src="@{/tuodanweb/js/swiper.min.js}" type="text/javascript"></script>
+
+	</head>
+	<body>
+		<!-- 首页开始 -->
+	<div class="container">
+			<!-- 图片轮播 -->
+			<div class="m-slider" data-ydui-slider>
+				<div class="slider-wrapper">
+					<div class="slider-item">
+						<a href="javascript:;">
+							<img th:src="@{/tuodanweb/images/td_index_03.png}">
+						</a>
+					</div>
+					<div class="slider-item">
+						<a href="javascript:;">
+							<img th:src="@{/tuodanweb/images/td_index_07.png}">
+						</a>
+					</div>
+
+				</div>
+				<div class="slider-pagination"></div>
+			</div>
+			<!-- 图片轮播 -->
+			<!-- 搜索 开始 -->
+			<form class="shearh">
+				<input type="text" placeholder="拆TA盲盒,搜索编号" class="inp_txt" id="boxCode"/>
+				<input type="button" value="搜索" class="inp_btn"/>
+			</form>
+			<!-- 搜索 结束 -->
+			<!-- 全部盲盒  活动 红娘 开始-->
+			<div class="index_tit">
+				<span class="head-nav on">全部盲盒</span>
+				<span class="head-nav">活动</span>
+				<span class="head-nav">红娘</span>
+				<span class="fr" id="ind_sx">筛选</span>
+			</div>
+			<!-- 全部盲盒  活动 红娘 开始-->
+
+			<!-- 盲盒列表 开始 -->
+			<div class="mh_list" id="boxTable">
+				<div id="userTable">
+					<div th:each="user : ${userList.list}">
+						<th:block th:if="${user.sex == '1'}">
+							<div class="mh_div rz nan vip" th:onclick="lookChaitamh([[${user.openid}]])">
+								<!--<i class="rz_i">颜值认证</i>-->
+								<div class="mh_img fl"></div>
+								<div class="mh_txt fl">
+									<div class="tit">
+										<span th:text="'编号:'+${user.code}"></span>
+										<i></i>
+										<u></u>
+									</div>
+									<h5 th:text="${user.birthDate.substring(0,4)} +' '+ ${user.constellation} +' '+ ${user.workNature} +' '+ ${user.isMarried} +' '+ ${user.location.split('-')[0]}+'内'"></h5>
+									<p th:if="${user.idealType.length > 20}" th:text="${user.idealType.substring(0,20)}+'...'"></p>
+									<p th:if="${user.idealType.length <= 20}" th:text="${user.idealType}"></p>
+								</div>
+							</div>
+						</th:block>
+						<th:block th:if="${user.sex == '2'}">
+							<div class="mh_div rz nv vip" th:onclick="lookChaitamh([[${user.openid}]])">
+								<!--<i class="rz_i">颜值认证</i>-->
+								<div class="mh_img fl"></div>
+								<div class="mh_txt fl">
+									<div class="tit">
+										<span th:text="'编号:'+${user.code}"></span>
+										<i></i>
+										<u></u>
+									</div>
+									<h5 th:text="${user.birthDate.substring(0,4)} +' '+ ${user.constellation} +' '+ ${user.workNature} +' '+ ${user.isMarried} +' '+ ${user.location.split('-')[0]}+'内'"></h5>
+									<p th:if="${user.idealType.length > 20}" th:text="${user.idealType.substring(0,20)}+'...'"></p>
+									<p th:if="${user.idealType.length <= 20}" th:text="${user.idealType}"></p>
+								</div>
+							</div>
+						</th:block>
+					</div>
+				</div>
+			</div>
+			<!-- 盲盒列表 结束 -->
+			<!-- 活动列表 开始 -->
+			<div class="mh_list" id="huodongTable" style="display: none;">
+				<div th:each="huodong : ${huodongList}">
+					<div class="mh_div rz" th:onclick="lookHuodonng([[${huodong.id}]])">
+						<div class="mh_txt fl">
+							<div class="tit">
+								<span th:text="'活动地点:'+${huodong.activityAddress}"></span>
+								<i></i>
+								<u></u>
+							</div>
+							<p>报名开始时间:[[${huodong.activityStartDateYm}]]</p>
+							<p>报名结束时间:[[${huodong.activityEndDateYm}]]</p>
+						</div>
+					</div>
+				</div>
+			</div>
+			<!-- 活动列表 结束 -->
+			<!-- 红娘列表 开始 -->
+			<div class="mh_list" id="hongniangTable" style="display: none;">
+				<div th:each="hongniang : ${hongniangList}">
+					<div class="mh_div rz" th:onclick="lookHongniang([[${hongniang.id}]])">
+						<div class="mh_img fl"></div>
+						<div class="mh_txt fl">
+							<div class="tit">
+								<span th:text="'姓名:'+${hongniang.name}"></span>
+								<i></i>
+								<u></u>
+							</div>
+							<img th:src="${hongniang.wechatUrl}"/>
+						</div>
+					</div>
+				</div>
+			</div>
+			<!-- 红娘列表 结束 -->
+			<div class="tk_zg"></div>
+			<form class="sx_k" id="app">
+				<h3>条件筛选</h3>
+				<p>性别</p>
+				<label class="w3 sex">
+					<input type="radio" name="sex" value="1"/>
+					<span>男</span>
+				</label>
+				<label class="w3 sex">
+					<input type="radio" name="sex" value="2"/>
+					<span>女</span>
+				</label>
+				<label class="w3 sex">
+					<input type="radio" name="sex" value=""/>
+					<span>男女不限</span>
+				</label>
+				<div style="clear: both;"></div>
+				<p>年龄范围</p>
+				<label class="w2 btn9">
+					<input type="button" name="startAge"/>
+					<span>最大出生年份</span>
+				</label>
+				<label class="w2 btn10">
+					<input type="button" name="endAge"/>
+					<span>最小出生年份</span>
+				</label>
+				<div style="clear: both;"></div>
+				<p>工作性质</p>
+				<label class="w3">
+					<input type="checkbox" name="education" value="私企"/>
+					<span>私企</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="国企"/>
+					<span>国企</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="央企"/>
+					<span>央企</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="个体工商户"/>
+					<span>个体工商户</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="企业法人"/>
+					<span>企业法人</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="自主创业"/>
+					<span>自主创业</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="事业单位(合同)"/>
+					<span>事业单位(合同)</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="事业单位(在编)"/>
+					<span>事业单位(在编)</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="公务员"/>
+					<span>公务员</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="军人"/>
+					<span>军人</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="机关单位"/>
+					<span>机关单位</span>
+				</label>
+				<label class="w3">
+					<input type="checkbox" name="education" value="学生"/>
+					<span>学生</span>
+				</label>
+				<div style="clear: both;"></div>
+				<p>地区选择</p>
+				<label class="w2 btn1">
+					<input type="button" name="1"/>
+					<span>所在省</span>
+				</label>
+				<label class="w2 btn2">
+					<input type="button" name="1"/>
+					<span>所在市</span>
+				</label>
+				<label class="w2 btn3">
+					<input type="button" name="1"/>
+					<span>所在区</span>
+				</label>
+				<div style="clear: both;"></div>
+				<p>身高范围</p>
+				<label class="w2 btn11">
+					<input type="button" name="startHeight"/>
+					<span>最小身高</span>
+				</label>
+				<label class="w2 btn12">
+					<input type="button" name="endHeight"/>
+					<span>最大身高</span>
+				</label>
+				<div style="clear: both;"></div>
+				<div class="sx_btn">
+					<input type="button" value="重置筛选" id="czsx"/>
+					<input type="button" value="完成" id="wc"/>
+				</div>
+			</form>
+		</div>
+
+		<!-- 首页结束 -->
+		<!-- 盲盒抽选开始 -->
+		<div class="container">
+			<img th:src="@{/tuodanweb/images/mhcx_03.png}" class="mh_banner"/>
+			<div class="mhcx_form" id="app">
+				<label>
+					<span>地区(省)</span>
+					<button class="btn1">请选择所在省</button>
+				</label>
+				<label>
+					<span>地区(市)</span>
+					<button class="btn2">请选择所在市</button>
+				</label>
+				<label>
+					<span>地区(区/市)</span>
+					<button class="btn3">请选择所在区/市</button>
+				</label>
+				<label>
+					<span>星座</span>
+					<button class="btn4">请选择对方星座</button>
+				</label>
+				<label>
+					<span>学历</span>
+					<button class="btn5">请选择对方学历</button>
+				</label>
+				<label>
+					<span>工作性质</span>
+					<button class="btn6">请选择对方工作性质</button>
+				</label>
+				<label>
+					<span>年龄范围</span>
+					<button class="btn7">请选择对方年龄范围</button>
+				</label>
+				<label>
+					<span>身高范围</span>
+					<button class="btn8">请选择对方身高范围</button>
+				</label>
+
+			</div>
+			<input type="button" value="抽选" class="mhcx_btn" id="mhxc_btn"/>
+
+		</div>
+
+		<!-- 盲盒抽选结束 -->
+		<!-- 推广介绍开始 -->
+		<div class="container wid_con">
+			<img th:src="@{/tuodanweb/images/tgjs.jpg}" class="w_tgjs"/>
+		</div>
+
+		<!-- 推广介绍结束 -->
+		<!-- 个人中心开始 -->
+		<div class="container wid_con2" th:object="${brUser}">
+			<div class="top_bg">
+				<div class="img_txt">
+					<img th:src="@{/tuodanweb/images/grzx_04.png}" class="img_logo" />
+					<div class="txt">
+						<h1>[[*{wechatName}]]</h1>
+						<p>编号:[[*{code}]]</p>
+						<button th:if="*{signIn == '1'}" disabled>今日已签到</button>
+						<button th:if="*{signIn == '0'}" type="button" onclick="signIn()" id="signButton">签到领积分</button>
+					</div>
+				</div>
+				<div class="grzx_ye" onclick="window.location.href = ctx+'xhn/openRecharge';">
+					<i class="ye_icon"></i>
+					<div>
+						<span>余额:[[*{memberMoney}]]</span>
+						<span>积分:<p id="jf">[[*{memberIntegral}]]</p></span>
+					</div>
+				</div>
+				<div class="grzx_it">
+					<span>[[*{birthDate}]]</span>
+					<span>[[*{constellation}]]</span>
+				</div>
+			</div>
+			<div class="nav_con">
+				<div class="nav_div" onclick="window.location.href = ctx+'xhn/updateUser';">
+					<i class="nav_xgzl"></i>
+					<p>修改资料</p>
+				</div>
+				<div class="nav_div">
+					<i class="nav_gmjl" onclick="window.location.href = ctx+'xhn/lookBuy';"></i>
+					<p>购买记录</p>
+				</div>
+				<div class="nav_div" onclick="$('#ewm').show()">
+					<i class="nav_wdfxm"></i>
+					<p>我的分享码</p>
+				</div>
+				<div class="nav_div" onclick="getPayPrepayId('脱单便利店盲盒上架',2990,'0')">
+					<i class="nav_sqsj"></i>
+					<p>申请上架</p>
+				</div>
+			</div>
+			<div id="ewm" style="display: none;">
+				<img th:src="*{promoterEwm}"/>
+			</div>
+			<div class="nav_con2">
+				<div class="nav_tit" onclick="window.location.href = ctx+'xhn/goGywm';">
+					<i class="gywm"></i>
+					<span>关于我们</span>
+				</div>
+				<div class="line"></div>
+				<div class="nav_tit" onclick=" window.location.href= ctx+'xhn/getKefuInfo';">
+					<i class="zxkf"></i>
+					<span>在线客服</span>
+				</div>
+				<div class="line"></div>
+				<div class="nav_tit" onclick="window.location.href = ctx+'xhn/lookExtension';">
+					<i class="tgjl"></i>
+					<span>推广记录</span>
+				</div>
+				<div class="line"></div>
+				<div class="nav_tit" onclick="window.location.href = ctx+'xhn/lookGiveGift';">
+					<i class="yjzd"></i>
+					<span>佣金账单</span>
+				</div>
+				<div class="line"></div>
+				<div class="nav_tit" onclick="window.location.href = ctx+'xhn/getIntegralBill';">
+					<i class="zdmx"></i>
+					<span>账单明细</span>
+				</div>
+			</div>
+
+		</div>
+
+		<!-- 个人中心结束 -->
+
+		<div class="select_box select_box1">
+
+		</div>
+		<div class="select_box select_box2">
+
+		</div>
+		<div class="select_box select_box3">
+
+		</div>
+		<div class="select_box select_box4">
+
+		</div>
+		<div class="select_box select_box5">
+
+		</div>
+		<div class="select_box select_box6">
+
+		</div>
+		<div class="select_box select_box7">
+
+		</div>
+		<div class="select_box select_box8">
+
+		</div>
+		<div class="select_box select_box9">
+
+		</div>
+		<div class="select_box select_box10">
+
+		</div>
+		<div class="select_box select_box11">
+
+		</div>
+		<div class="select_box select_box12">
+
+		</div>
+
+		<div class="db_tab">
+			<div class="index tab on">
+				<i class="icon"></i>
+				<p>首页</p>
+			</div>
+			<div class="mhcx tab">
+				<i class="icon"></i>
+				<p>盲盒抽选</p>
+			</div>
+			<div class="tgjs tab">
+				<i class="icon"></i>
+				<p>推广介绍</p>
+			</div>
+			<div class="grzx tab">
+				<i class="icon"></i>
+				<p>个人中心</p>
+			</div>
+		</div>
+		<script th:src="@{/tuodanweb/js/dySelect.js}"></script>
+		<script th:src="@{/tuodanweb/js/index.js}"></script>
+		<script th:inline="javascript">
+            var ctx = [[@{/}]];
+			var w3clickNum = 0;
+            var pageNum=1;
+		</script>
+		<script type="text/javascript" chartset="UTF-8">
+			$(function(){
+				// 筛选弹窗
+				$("#ind_sx").on('click',function(){
+					$(".tk_zg").show();
+					$("body").css("overflow","hidden")
+					$(".sx_k").animate(
+						{height:"710px"}
+					)
+				});
+				/**
+                 * 首页列表筛选提交
+                 */
+				$("#wc").on('click',function(){
+					$(".tk_zg").hide();
+					$("body").css("overflow","auto")
+					$(".sx_k").animate(
+						{height:"0px"}
+					)
+
+                    var workNature = '';
+                    var keytext = document.getElementsByName("education");
+                    for (var i = 0; i < keytext.length; i++){
+                        var obj = keytext[i];
+                        if (obj.type == "checkbox" && obj.checked) {
+                            var code = obj.value;//获取checkbox的值
+                            workNature += code+",";  //组合完了
+                        }
+                    }
+                    if(workNature.length > 0){workNature = workNature.substring(0,workNature.length-1)}
+                    var sex = $('input:radio[name="sex"]:checked').val();
+                    var taLocation = '';
+                    if(provinceName != ''){
+                        if(cityName != ''){
+                            if(areaName != ''){
+                                taLocation = provinceName+'-'+cityName+'-'+areaName;
+                            }else{
+                                taLocation = provinceName+'-'+cityName;
+                            }
+                        }else{
+                            taLocation = provinceName;
+                        }
+                    }
+                    $.ajax({
+                        type: "get",
+                        url: ctx + "xhn/getBoxList",
+                        data: {
+                            code:$("#boxCode").val(),
+                            sex:sex,
+                            startAge:startAge,
+                            endAge:endAge,
+                            workNature:workNature,
+                            taLocation:taLocation,
+                            startHeight:startHeight,
+                            endHeight:endHeight,
+                            pageNum:1
+                        },
+                        success: function (res) {
+                            if(res.code == 0 && res.data.length > 0){
+                                var userTable = "";
+                                for (var i in res.data){
+                             		userTable+="<div class=\"mh_div rz " ;
+                             		if(res.data[i].sex == '1'){
+                                        userTable+="nan ";
+                             		}else{
+                                        userTable+="nv ";
+                             		}
+                                    userTable+="vip\" onclick=\"lookChaitamh('"+res.data[i].openid+"')\">\n";
+                                    userTable+="<div class=\"mh_img fl\"></div>\n";
+									userTable+="<div class=\"mh_txt fl\">\n";
+									userTable+="<div class=\"tit\">\n";
+									userTable+="<span>编号:"+res.data[i].code+"</span>\n";
+									userTable+="<i></i>\n";
+									userTable+="<u></u>\n";
+									userTable+="</div>\n";
+									userTable+="<h5>"+res.data[i].birthDate.substring(0,4)+" "+res.data[i].constellation+" "+res.data[i].workNature+" "+res.data[i].isMarried+" "+res.data[i].location.split('-')[0]+"内</h5>\n";
+									if(res.data[i].idealType.length > 20){
+                                        userTable+="<p>"+res.data[i].idealType.substring(0,20)+"...</p>\n";
+                                    }else{
+                                        userTable+="<p>"+res.data[i].idealType+"</p>\n";
+									}
+									userTable+="</div>\n";
+									userTable+="</div>"
+                                }
+                                $("#userTable").html(userTable);
+                            }
+                        }
+                    });
+				});
+				//根据条件随机抽选盲盒
+				$("#mhxc_btn").on('click',function(){
+				 	var taLocation = '';
+				    if(provinceName != ''){
+				 		if(cityName != ''){
+				 			if(areaName != ''){
+                                taLocation = provinceName+'-'+cityName+'-'+areaName;
+                            }else{
+                                taLocation = provinceName+'-'+cityName;
+				 			}
+				 		}else{
+                            taLocation = provinceName;
+                        }
+				 	}
+                    var RandomstartAge;
+					var RandomendAge;
+					var RandomstartHeight;
+					var RandomendHeight;
+				 	if(agefw != ''){
+                        RandomstartAge=agefw.split("~")[0];
+                        RandomendAge=agefw.split("~")[1];
+				 	}
+				 	if(heightfw !=''){
+                        RandomstartHeight=heightfw.split("~")[0];
+                        RandomendHeight=heightfw.split("~")[1];
+				 	}
+                    $.ajax({
+                        type: "get",
+                        url: ctx + "xhn/getRandomBlindBox",
+                        data: {
+                            taLocation:taLocation,
+                            constellation:constellation,
+                            education:education,
+                            workNature:workNatureRadio,
+                            startAge:RandomstartAge,
+                            endAge:RandomendAge,
+                            startHeight:RandomstartHeight,
+                            endHeight:RandomendHeight,
+                        },
+                        success: function (res) {
+                            if(res.code == 0 && res.data != null){
+                                window.open(ctx+"xhn/chaitamh?openid="+res.data.openid);
+                            }else{
+                         		alert("该条件下无人员信息");
+                            }
+                        }
+                    });
+                });
+				$(".tk_zg").on('click',function(){
+					$(".tk_zg").hide();
+					$("body").css("overflow","auto")
+					$(".sx_k").animate(
+						{height:"0px"}
+					)
+				});
+				// 底部切换
+				$(".container").eq(0).show().siblings(".container").hide();
+				$(".tab").on('click',function(){
+					$(this).addClass("on").siblings(".tab").removeClass("on");
+					$(".container").eq($(this).index()).show().siblings(".container").hide();
+				});
+				$(".w3").on('click',function(){
+				    if(w3clickNum%2==0){
+				        if($(this).hasClass("sex")){
+							$(".sex").removeClass("on");
+							$(this).addClass("on");
+                        }else{
+                            if($(this).hasClass("on")){
+                                $(this).removeClass("on");
+                            }else{
+                                $(this).addClass("on");
+                            }
+                        }
+				    }
+                    w3clickNum++;
+            	});
+
+                $(".head-nav").on('click',function(){
+                    $(".head-nav").removeClass("on");
+                    $(this).addClass("on");
+                    var navName = $(this).html();
+                   	if(navName == '全部盲盒'){
+					   $("#userTable").show();
+                        $("#huodongTable").hide();
+                        $("#hongniangTable").hide();
+                   	}else if(navName == '活动'){
+                        $("#userTable").hide();
+                        $("#huodongTable").show();
+                        $("#hongniangTable").hide();
+                   	}else{
+                        $("#userTable").hide();
+                        $("#huodongTable").hide();
+                        $("#hongniangTable").show();
+                    }
+                });
+
+			})
+
+            /**
+			 * 点击盲盒跳转盲盒的详情信息
+             * @param openid
+             */
+			function lookChaitamh(openid){
+			    window.location.href = ctx+'xhn/chaitamh?openid='+openid;
+			}
+            /**
+             * 调用微信支付
+             */
+			function getPayPrepayId(description,total,purpose) {
+                $.ajax({
+                    type: "post",
+                    url: ctx + "xhn/getPayPrepayId",
+                    data: {
+                        description:description,
+                        total:total,
+                		purpose:purpose,
+                    	boxOpenid:''
+                    },
+                    success: function (res) {
+                        if (typeof WeixinJSBridge == "undefined") {
+                            if (document.addEventListener) {
+                                document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
+                            } else if (document.attachEvent) {
+                                document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
+                                document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
+                            }
+                        } else {
+                            onBridgeReady(res.data);
+                        }
+                    }
+                });
+            }
+
+            function onBridgeReady(data) {
+                WeixinJSBridge.invoke('getBrandWCPayRequest', {
+                        "appId": 'wx19ac981693d1a79a',     //公众号ID,由商户传入
+                        "timeStamp": data.timeStamp,     //时间戳,自1970年以来的秒数
+                        "nonceStr": data.nonceStr,      //随机串
+                        "package": data.package_,
+                        "signType": "RSA",     //微信签名方式:
+                        "paySign": data.paySign //微信签名
+                    },
+                    function(res) {
+                        if (res.err_msg == "get_brand_wcpay_request:ok") {
+                            // 使用以上方式判断前端返回,微信团队郑重提示:
+                            //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
+                            setTimeout(() => {
+                            window.location.href= ctx+'xhn/index';
+                            }, 2000)
+                        }
+                    });
+            }
+            /**
+             * 跳转活动详情
+             */
+            function lookHuodonng(id) {
+                window.location.href = ctx+'xhn/activityInfo?huodongId='+id;
+            }
+            /**
+             * 跳转红娘个人详情信息
+             */
+            function lookHongniang(id) {
+                window.location.href = ctx+'xhn/matchmaker?hongniangid='+id;
+            }
+            /**
+             * 每日签到
+             */
+            function signIn() {
+                $.ajax({
+                    type: "get",
+                    url: ctx + "xhn/signIn",
+                    success: function (res) {
+                        if(res.code == 0){
+                            $("#jf").html(parseInt($("#jf").html())+1);
+                            $("#signButton").attr("disabled","disabled");
+                            alert("签到成功!");
+                        }
+                    }
+                });
+            }
+		</script>
+	</body>
+</html>

+ 29 - 0
mybusiness/src/main/resources/templates/appxhn/kefu.html

@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+	<head>
+		<meta charset="utf-8">
+		<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+		<meta content="yes" name="apple-mobile-web-app-capable" />
+		<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+		<meta content="telephone=no" name="format-detection" />
+		<title>客服</title>
+		<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+		<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+	</head>
+	<body>
+
+		<div class="container wid_con2">
+			<div class="yjtx_list">
+				<h1>客服列表</h1>
+				<div class="tx_li" th:each="kefu : ${kefu}">
+					<img th:src="*{kefu.avatar}"/>
+					<h4>[[${kefu.userName}]]</h4>
+					<div class="tx_txt">
+						<p>邮箱:[[${kefu.email}]]</p>
+						<span>电话:[[${kefu.phonenumber}]]</span>
+					</div>
+				</div>
+			</div>
+		</div>
+	</body>
+</html>

+ 70 - 0
mybusiness/src/main/resources/templates/appxhn/lookBuy.html

@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+	<meta charset="utf-8">
+	<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+	<meta content="yes" name="apple-mobile-web-app-capable" />
+	<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+	<meta content="telephone=no" name="format-detection" />
+	<title>购买记录</title>
+	<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+	<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+
+</head>
+<body>
+<div class="container">
+	<div class="gmjl_list">
+		<div class="gmjl_li">
+			<div class="gmjl_img">
+				<img th:src="@{/tuodanweb/images/mh_nv.png}"/>
+			</div>
+			<div class="gmjl_txt">
+				<h2>盲盒昵称<i>No.54685512</i></h2>
+				<p>手机号:15689895656</p>
+				<p>微信号:15689895656</p>
+			</div>
+		</div>
+	</div>
+</div>
+	<script th:inline="javascript">
+		var ctx = [[@{/}]];
+        var pageNum = 1;
+		var openid=[[${openid}]];
+	</script>
+	<script type="text/javascript" chartset="UTF-8">
+        $(function(){
+            getList();
+        });
+
+        /**
+		 * 查询本人购买记录信息
+         */
+		function getList() {
+            $.ajax({
+                type: "get",
+                url: ctx + "xhn/getOpenBlindBoxList",
+                data: {
+                    openid:openid,
+                },
+                success: function (res) {
+                    var html = "";
+                 	for (var i in res.data){
+                 	    html+="<div class=\"gmjl_li\">\n" +
+                            "\t\t\t<div class=\"gmjl_img\">\n" +
+                            "\t\t\t\t<img th:src=\"@{/tuodanweb/images/mh_nv.png}\"/>\n" +
+                            "\t\t\t</div>\n" +
+                            "\t\t\t<div class=\"gmjl_txt\">\n" +
+                            "\t\t\t\t<h2>盲盒昵称<i>"+res.data[i].wechatName+"</i></h2>\n" +
+                            "\t\t\t\t<p>手机号:"+res.data[i].phone+"</p>\n" +
+                            "\t\t\t\t<p>微信号:"+res.data[i].wechatCode+"</p>\n" +
+                            "\t\t\t</div>\n" +
+                            "\t\t</div>";
+					}
+
+                    $(".gmjl_list").html(html);
+                }
+            });
+        }
+	</script>
+</body>
+</html>

+ 67 - 0
mybusiness/src/main/resources/templates/appxhn/lookExtension.html

@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+	<meta charset="utf-8">
+	<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+	<meta content="yes" name="apple-mobile-web-app-capable" />
+	<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+	<meta content="telephone=no" name="format-detection" />
+	<title>推广记录</title>
+	<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+	<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+
+</head>
+<body>
+<div class="container">
+	<div class="tgjl_list">
+		<div class="tgjl_left">
+			<h4>被推广人昵称</h4>
+			<p>NO.45622555</p>
+		</div>
+		<div class="tgjl_right">
+			<h5>+5</h5>
+			<p>2022年12月15日 10:25:25</p>
+		</div>
+	</div>
+</div>
+	<script th:inline="javascript">
+		var ctx = [[@{/}]];
+        var pageNum = 1;
+		var openid=[[${openid}]];
+	</script>
+	<script type="text/javascript" chartset="UTF-8">
+        $(function(){
+            getList();
+        });
+
+        /**
+		 * 查询本人的 推广记录信息
+         */
+		function getList() {
+            $.ajax({
+                type: "get",
+                url: ctx + "xhn/getExtensionList",
+                data: {
+                    openid:openid,
+                },
+                success: function (res) {
+                    var html = "";
+                 	for (var i in res.data){
+                 	    html+="<div class=\"tgjl_list\">\n" +
+                            "\t\t<div class=\"tgjl_left\">\n" +
+                            "\t\t\t<h4>被推广人昵称</h4>\n" +
+                            "\t\t\t<p>"+res.data[i].wechatName+"</p>\n" +
+                            "\t\t</div>\n" +
+                            "\t\t<div class=\"tgjl_right\">\n" +
+                            "\t\t\t<h5>+5</h5>\n" +
+                            "\t\t\t<p>"+res.data[i].createDate+"</p>\n" +
+                            "\t\t</div>\n" +
+                            "\t</div>";
+                    }
+                    $(".container").html(html);
+                }
+            });
+        }
+	</script>
+</body>
+</html>

+ 101 - 0
mybusiness/src/main/resources/templates/appxhn/lookGiveGift.html

@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+	<meta charset="utf-8">
+	<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+	<meta content="yes" name="apple-mobile-web-app-capable" />
+	<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+	<meta content="telephone=no" name="format-detection" />
+	<title>佣金提现</title>
+	<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+	<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+</head>
+<body>
+
+<div class="container wid_con2">
+	<div class="yjzd_bg">
+		<div class="tx_img">
+			<img src="images/grzx_04.png" />
+		</div>
+		<div class="yjzd_txt">
+			<h5>[[${user.wechatName}]]</h5>
+			<span>微信号:[[${user.wechatCode}]]</span>
+			<span>手机号:[[${user.phone}]]</span>
+		</div>
+	</div>
+	<div class="yjzd_div">
+		<div class="yjzd_left">
+			<p>收到礼物</p>
+			<h1 id="shou">0</h1>
+		</div>
+		<div class="yjzd_center">
+			<p>送出礼物</p>
+			<h1 id="song">0</h1>
+		</div>
+		<div class="yjzd_right">
+			<p>剩余积分</p>
+			<h1>[[${user.memberIntegral}]]</h1>
+		</div>
+	</div>
+	<div class="yjtx_list">
+		<h1>礼物明细</h1>
+	</div>
+</div>
+
+<script th:inline="javascript">
+    var ctx = [[@{/}]];
+    var pageNum = 1;
+    var openid=[[${user.openid}]];
+</script>
+<script type="text/javascript" chartset="UTF-8">
+    $(function(){
+        getList();
+    });
+
+    /**
+     * 查询积分账单列表(礼物获得消费详细)
+     */
+    function getList() {
+        $.ajax({
+            type: "get",
+            url: ctx + "xhn/getGiveGiftList",
+            data: {
+                openid:openid,
+            },
+            success: function (res) {
+                var songAll=0;
+                var shouAll=0;
+                var html = "";
+                for (var i in res.data){
+                    if(res.data[i].integralNum.contains('+')){
+                        html+="<div class=\"lw_list shl\">\n" +
+                            "\t\t\t<div class=\"tgjl_left\">\n";
+                        html+="\t\t\t\t<h4>收到"+res.data[i].giftName+"<span>×"+res.data[i].giftNum+"</span></h4>\n";
+                        shouAll += parseInt(res.data[i].giftNum);
+                    }else{
+                        html+="<div class=\"lw_list sol\">\n" +
+                            "\t\t\t<div class=\"tgjl_left\">\n";
+                        html+="\t\t\t\t<h4>送出"+res.data[i].giftName+"<span>×"+res.data[i].giftNum+"</span></h4>\n";
+                        songAll += parseInt(res.data[i].giftNum);
+                    }
+                    if(res.data[i].integralNum.contains('+')){
+                        html+="<p>送礼人的昵称:"+res.data[i].wechatName+"</p>n";
+                    }else{
+                        html+="<p>收礼人的昵称:"+res.data[i].wechatName+"</p>";
+                    }
+                    html+="\t\t\t</div>\n" +
+                        "\t\t\t<div class=\"tgjl_right\">\n" +
+                        "\t\t\t\t<h5>"+res.data[i].integralNum+"</h5>\n" +
+                        "\t\t\t\t<p>"+res.data[i].createDate+"</p>\n" +
+                        "\t\t\t</div>\n" +
+                        "\t\t</div>";
+                }
+                $(".yjtx_list").append(html);
+                $("#shou").html(shouAll);
+                $("#song").html(songAll);
+            }
+        });
+    }
+</script>
+</body>
+</html>

+ 33 - 0
mybusiness/src/main/resources/templates/appxhn/matchmaker.html

@@ -0,0 +1,33 @@
+<!doctype html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+<meta charset="utf-8">
+<title>红娘详情</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no,minimum-scale=1.0, maximum-scale=1.0">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/main.css}">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/jbxx.css}">
+<script th:src="@{/tuodanweb/js/jquery-1.7.2.min.js}"></script>
+</head>
+
+<body>
+<div class="activity-zt" th:object="${matchmaker}">
+	<div class="xiaoHNZX">
+    	<p>[[*{introduction}]]</p>
+    </div>
+    <div class="hngrJJ">
+    	<div class="photo"><img th:src="*{wechatUrl}"/></div>
+        <h2>[[*{name}]]</h2>
+        <div class="hngrJJ_in">
+            <p>[[*{talentIntroduction}]]</p>
+            <p>[[*{workingTime}]]</p>
+            <p>[[*{beGood}]]</p>
+            <p>[[*{skill}]]</p>
+            <p>[[*{honor}]]</p>
+            <p>[[*{title}]]</p>
+            <span>联系方式:[[*{phone}]] </span>
+        </div>
+    </div>
+</div>
+
+</body>
+</html>

+ 139 - 0
mybusiness/src/main/resources/templates/appxhn/openBox.html

@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+	<meta charset="utf-8">
+	<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+	<meta content="yes" name="apple-mobile-web-app-capable" />
+	<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+	<meta content="telephone=no" name="format-detection" />
+	<title>支付页面</title>
+	<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+	<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+	<script th:inline="javascript">
+        var ctx = [[@{/}]];
+	</script>
+</head>
+<body>
+
+<div class="container wid_con2" th:object="${brUser}">
+	<input th:value="${boxOpenid}" id="boxOpenid" type="hidden"/>
+	<input th:value="*{openid}" id="openid" type="hidden"/>
+	<div class="yjtx_bg" style="float: left;">
+		<h5>可使用金额(元)</h5>
+		<h1><i>¥</i>[[*{memberMoney}]]</h1>
+	</div>
+	<div class="yjtx_bg" style="float: left;">
+		<h5>可使用积分</h5>
+		<h1><i>$</i>[[*{memberIntegral}]]</h1>
+	</div>
+	<div class="yjtx_bn">
+		<input type="button" value="微信支付" onclick="wechatPay()"/>
+		<input th:disabled="*{memberMoney < 19.90 }" type="button" value="余额支付" onclick="moneyOpenBlindBox()"/>
+		<input th:disabled="*{memberIntegral < 100 }" type="button" value="积分支付" onclick="integralOpenBlindBox()"/>
+		<input type="button" th:if="*{isFree == '1'}" value="免费开盒"  onclick="freeOpenBlindBox()"/>
+	</div>
+</div>
+
+</body>
+<script type="text/javascript">
+    function wechatPay() {
+        $.ajax({
+            type: "post",
+            url: ctx + "xhn/getPayPrepayId",
+            data: {
+                description:'脱单便利店开盲盒',
+                total:'1990',
+                purpose:'1',
+                boxOpenid:$("#boxOpenid").val()
+            },
+            success: function (res) {
+                if (typeof WeixinJSBridge == "undefined") {
+                    if (document.addEventListener) {
+                        document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
+                    } else if (document.attachEvent) {
+                        document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
+                        document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
+                    }
+                } else {
+                    onBridgeReady(res.data);
+                }
+            }
+        });
+    }
+
+    function onBridgeReady(data) {
+        WeixinJSBridge.invoke('getBrandWCPayRequest', {
+                "appId": 'wx19ac981693d1a79a',     //公众号ID,由商户传入
+                "timeStamp": data.timeStamp,     //时间戳,自1970年以来的秒数
+                "nonceStr": data.nonceStr,      //随机串
+                "package": data.package_,
+                "signType": "RSA",     //微信签名方式:
+                "paySign": data.paySign //微信签名
+            },
+            function(res) {
+                if (res.err_msg == "get_brand_wcpay_request:ok") {
+                    // 使用以上方式判断前端返回,微信团队郑重提示:
+                    //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
+                    setTimeout(() => {
+                    window.location.href= ctx+'xhn/chaitamh?openid=' +$("#boxOpenid").val();
+                    }, 2000)
+                }
+            });
+    }
+
+    /**
+	 * 积分支付
+     */
+    function integralOpenBlindBox(){
+        $.ajax({
+            type: "post",
+            url: ctx + "xhn/integralOpenBlindBox",
+            data: {
+                openid:$("#openid").val(),
+                boxOpenid:$("#boxOpenid").val()
+            },
+            success: function (res) {
+                if(res.code == 0){
+                	window.location.href= ctx+'xhn/chaitamh?openid=' +$("#boxOpenid").val();
+                }
+            }
+        });
+    }
+    /**
+	 * 余额支付
+     */
+    function moneyOpenBlindBox(){
+        $.ajax({
+            type: "post",
+            url: ctx + "xhn/moneyOpenBlindBox",
+            data: {
+                openid:$("#openid").val(),
+                boxOpenid:$("#boxOpenid").val()
+            },
+            success: function (res) {
+                if(res.code == 0){
+                	window.location.href= ctx+'xhn/chaitamh?openid=' +$("#boxOpenid").val();
+                }
+            }
+        });
+    }
+    /**
+     * 首充后免费一次开盲盒接口
+     */
+    function freeOpenBlindBox(){
+        $.ajax({
+            type: "post",
+            url: ctx + "xhn/freeOpenBlindBox",
+            data: {
+                openid:$("#openid").val(),
+                boxOpenid:$("#boxOpenid").val()
+            },
+            success: function (res) {
+                if(res.code == 0){
+                	window.location.href= ctx+'xhn/chaitamh?openid=' +$("#boxOpenid").val();
+                }
+            }
+        });
+    }
+</script>
+</html>

+ 109 - 0
mybusiness/src/main/resources/templates/appxhn/openRecharge.html

@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+	<meta charset="utf-8">
+	<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+	<meta content="yes" name="apple-mobile-web-app-capable" />
+	<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+	<meta content="telephone=no" name="format-detection" />
+	<title>支付页面</title>
+	<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+	<script th:src="@{/tuodanweb/js/jquery.min.js}" type="text/javascript"></script>
+	<script th:inline="javascript">
+        var ctx = [[@{/}]];
+	</script>
+</head>
+<body>
+<div class="container wid_con3">
+	<div class="qd_btn" th:object="${brUser}">
+		<span onclick="wechatPay(600)">6元</span>
+		<span onclick="wechatPay(1800)">18元</span>
+		<span onclick="wechatPay(3600)">36元</span>
+		<span onclick="wechatPay(7200)">72元</span>
+		<input th:value="*{openid}" id="openid" type="hidden"/>
+	</div>
+	<div>
+		<span th:each="jf : ${jfList}"
+			  th:onclick="moneyConvertedIntegral([[${brUser.openid}]],[[${jf.id}]],[[${jf.money}]])">
+			[[${jf.money}]]元换[[${jf.integral}]]积分
+		</span>
+	</div>
+</div>
+</body>
+<script type="text/javascript">
+	var brMoney = [[${brUser.memberMoney}]];
+    /**
+     * 余额换积分接口
+     */
+    function moneyConvertedIntegral(openid,mbiId,money) {
+        if(parseFloat(brMoney)>=parseFloat(money)){//本人剩余金额大于当前充值积分金额
+            $.ajax({
+                type: "post",
+                url: ctx + "xhn/moneyConvertedIntegral",
+                data: {
+                    openid:openid,
+                    mbiId:mbiId
+                },
+                success: function (res) {
+                    if(res.code == 0){
+                        window.location.href=ctx+'xhn/index';
+                    }
+                }
+            });
+        }else{
+     		  alert("您的余额不够了,请先充值金额再兑换积分!");
+        }
+
+    }
+    /**
+     * 微信支付发起接口
+	 */
+    function wechatPay(total) {
+        $.ajax({
+            type: "post",
+            url: ctx + "xhn/getPayPrepayId",
+            data: {
+                description:'余额充值',
+                total:total,
+                purpose:'2',
+                boxOpenid:''
+            },
+            success: function (res) {
+                if (typeof WeixinJSBridge == "undefined") {
+                    if (document.addEventListener) {
+                        document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
+                    } else if (document.attachEvent) {
+                        document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
+                        document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
+                    }
+                } else {
+                    onBridgeReady(res.data);
+                }
+            }
+        });
+    }
+    /**
+     * 微信签名成功获取到加密签名值调用位置输入密码页
+     */
+    function onBridgeReady(data) {
+        WeixinJSBridge.invoke('getBrandWCPayRequest', {
+                "appId": 'wx19ac981693d1a79a',     //公众号ID,由商户传入
+                "timeStamp": data.timeStamp,     //时间戳,自1970年以来的秒数
+                "nonceStr": data.nonceStr,      //随机串
+                "package": data.package_,
+                "signType": "RSA",     //微信签名方式:
+                "paySign": data.paySign //微信签名
+            },
+            function(res) {
+                if (res.err_msg == "get_brand_wcpay_request:ok") {
+                    // 使用以上方式判断前端返回,微信团队郑重提示:
+                    //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
+                    setTimeout(() => {
+                    window.location.href= ctx+'xhn/index';
+                    }, 2000)
+                }
+            });
+    }
+
+</script>
+</html>

+ 75 - 0
mybusiness/src/main/resources/templates/appxhn/qidongye.html

@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+	<head>
+		<meta charset="utf-8">
+		<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+		<meta content="yes" name="apple-mobile-web-app-capable" />
+		<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+		<meta content="telephone=no" name="format-detection" />
+		<title>启动页</title>
+		<link th:href="@{/tuodanweb/css/main.css}" rel="stylesheet" />
+		<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
+		<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
+		<script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+		<script th:inline="javascript">
+            var ctx = [[@{/}]];
+		</script>
+	</head>
+	<body>
+		<div class="container wid_con3" id="app">
+			<img th:src="@{/tuodanweb/images/qidongye.jpg}"/>
+			<div class="qd_btn">
+				<input type="button" value="立即体验缘分" @click="getUserInfo"/>
+				<!--<a>取消注册</a>-->
+			</div>
+		</div>
+	</body>
+	<script>
+        new Vue({
+            el: '#app',
+            data: {
+                APPID: "wx19ac981693d1a79a",
+                APPSECRET: "cc7d798b4ce862979bb8f1571ef8be2b",
+                homeUrl: "http://qiyerenzibao.com/xhn/register",//通过微信认证后的回调地址
+                promoter: '',
+            },
+            created() {//   页面加载时触发方法
+                // 判断当前人是否有推广人
+                this.promoter = this.getUrlCode("promoter");
+            },
+            mounted() {//   页面加载完成后触发
+
+            },
+            methods: {// 方法集合
+                // 获取路径上的指定属性值
+                getUrlCode(name) {
+                    return (
+                        decodeURIComponent(
+                            (new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
+                                location.href
+                            ) || [, ""])[1].replace(/\+/g, "%20")
+                        ) || null
+                    );
+                },
+                // 点击授权按钮
+                getUserInfo() {
+                    axios.get(`/xhn/getWxUser`).then((res) => {
+                        if(res.data.data != null && res.data.data != undefined && res.data.data.phone != null && res.data.data.phone != ''){
+							window.location.href = ctx+"xhn/index";
+                        }else{
+                            // 获取当前页面地址作为回调地址,并且对地址进行urlEncode处理
+                            let local = encodeURIComponent(this.homeUrl+"?promoter="+this.promoter);
+                            // 跳转到授权页面
+                            window.location.href =
+                                "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" +
+                                this.APPID +
+                                "&redirect_uri=" +
+                                local +
+                                "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";
+                        }
+                    });
+                },
+            },
+        })
+	</script>
+</html>

+ 264 - 0
mybusiness/src/main/resources/templates/appxhn/register.html

@@ -0,0 +1,264 @@
+<!doctype html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+<meta charset="utf-8">
+<title>注册</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no,minimum-scale=1.0, maximum-scale=1.0">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/main.css}">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/jbxx.css}">
+<link rel="stylesheet" type="text/css" href="http://www.jq22.com/jquery/bootstrap-3.3.4.css">
+<link rel="stylesheet" th:href="@{/tuodanweb/css/mpicker.css}">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/public.css}">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/mobileSelect.css}">
+<script th:src="@{/tuodanweb/js/jquery-1.7.2.min.js}"></script>
+<script th:src="@{/tuodanweb/js/mobileSelect.js}" type="text/javascript"></script>
+    <script th:inline="javascript">
+        var ctx = [[@{/}]];
+    </script>
+    <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
+    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
+    <script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+</head>
+
+<body>
+<div class="zcbj"><img th:src="@{/tuodanweb/images/zcbj.jpg}"/></div>
+<div class="zhuce" id="app">
+    <form id="xhnRegisterUser">
+        <div class="zhuce_in">
+            <div>
+                <input th:value="${user.openid}" id="openid" name="openid" type="hidden"/>
+                <h1 class="zc_title">微信头像</h1>
+                <img th:src="${user.portraitUrl}" id="portraitUrl" name="portraitUrl"/>
+                <h1 class="zc_title">昵称</h1>
+                <input type="text" class="date2" th:value="${user.wechatName}" id="wechatName" name="wechatName">
+                <h1 class="zc_title">真实姓名</h1>
+                <input type="text" class="date2" value="" name="name">
+                <h1 class="zc_title">身份证号</h1>
+                <input type="text" class="date2" value="" name="idCard" onchange="cardtest(this.value)">
+                <h1 class="zc_title">出生年月</h1>
+                <input type="text" class="date2" value="" name="birthDate" id="birthDate" readonly>
+                <h1 class="zc_title">年龄</h1>
+                <input type="text" class="date2" value="" name="age" id="age" readonly>
+                <h1 class="zc_title">您的性别</h1>
+                <div class="xingbie">
+                    <span class="man"><input type="radio" name="sex" value="1"/>男</span>
+                    <span class="woman"><input type="radio" name="sex" value="2"/>女</span>
+                    <div class="clear"></div>
+                </div>
+                <h1 class="zc_title">常驻地址</h1>
+                <input type="text" class="select-value form-control" value="广东省-东莞市" name="location">
+                <h1 class="zc_title">星座</h1>
+                <select class="xingzuo" name="constellation">
+                    <option value="水瓶座">水瓶座</option>
+                    <option value="双鱼座">双鱼座</option>
+                    <option value="白羊座">白羊座</option>
+                    <option value="金牛座">金牛座</option>
+                    <option value="双子座">双子座</option>
+                    <option value="巨蟹座">巨蟹座</option>
+                    <option value="狮子座">狮子座</option>
+                    <option value="处女座">处女座</option>
+                    <option value="天秤座">天秤座</option>
+                    <option value="天蝎座">天蝎座</option>
+                    <option value="射手座">射手座</option>
+                    <option value="摩羯座">摩羯座</option>
+                </select>
+                <h1 class="zc_title">身高CM</h1>
+                <input type="text" class="date2" name="height">
+
+                <h1 class="zc_title">婚姻状态</h1>
+                <div class="xingbie">
+                    <span class="man"><input type="radio" name="isMarried" value="未婚"/>未婚</span>
+                    <span class="woman"><input type="radio" name="isMarried" value="离异"/>离异</span>
+                    <div class="clear"></div>
+                </div>
+                <h1 class="zc_title">体重Kg</h1>
+                <input type="text" class="date2" name="weight">
+
+                <h1 class="zc_title">性格介绍</h1>
+                <div class="xinggeJS">
+                    <textarea name="character"></textarea>
+                </div>
+                <h1 class="zc_title">工作性质</h1>
+                <select class="xingzuo" name="workNature">
+                    <option value="私企">私企</option>
+                    <option value="国企">国企</option>
+                    <option value="央企">央企</option>
+                    <option value="个体工商户">个体工商户</option>
+                    <option value="企业法人">企业法人</option>
+                    <option value="自主创业">自主创业</option>
+                    <option value="事业单位(合同)">事业单位(合同)</option>
+                    <option value="事业单位(在编)">事业单位(在编)</option>
+                    <option value="公务员">公务员</option>
+                    <option value="军人">军人</option>
+                    <option value="机关单位">机关单位</option>
+                    <option value="学生">学生</option>
+                </select>
+                <h1 class="zc_title">学历</h1>
+                <select class="xingzuo" name="education">
+                    <option value="博士研究生">博士研究生</option>
+                    <option value="硕士研究生">硕士研究生</option>
+                    <option value="本科">本科</option>
+                    <option value="大专">大专</option>
+                    <option value="高中">高中</option>
+                    <option value="中专">中专</option>
+                    <option value="初中">初中</option>
+                    <option value="小学">小学</option>
+                </select>
+                <h1 class="zc_title">职业</h1>
+                <div class="xinggeJS">
+                    <textarea name="occupation"></textarea>
+                </div>
+                <h1 class="zc_title">是否接受异地</h1>
+                <div class="xingbie">
+                    <span class="man"><input type="radio" name="longDistanceLove" value="是"/>是</span>
+                    <span class="woman"><input type="radio" name="longDistanceLove" value="否"/>否</span>
+                    <div class="clear"></div>
+                </div>
+                <h1 class="zc_title">理想TA所在地</h1>
+                <input type="text" class="select-value01 form-control" value="广东省-东莞市" name="taLocation">
+                <h1 class="zc_title">不接受的TA</h1>
+                <div class="xinggeJS">
+                    <textarea name="noAccepted"></textarea>
+                </div>
+                <h1 class="zc_title">理想型的TA</h1>
+                <div class="xinggeJS">
+                    <textarea name="idealType"></textarea>
+                </div>
+                <h1 class="zc_title">微信号或微信绑定手机号</h1>
+                <div class="weixin">
+                    <textarea name="wechatCode"></textarea>
+                </div>
+                <h1 class="zc_title">微信添加暗号</h1>
+                <div class="weixin">
+                    <textarea name="wechatArgot"></textarea>
+                </div>
+                <h1 class="zc_title">手机号<input type="button" id="btn" value="获取验证码" onclick="sendSmsVerify(this)" /></h1>
+                <input type="text" class="date2" id="phone" name="phone">
+                <h1 class="zc_title">验证码</h1>
+                <input type="text" class="date2" name="shortMsg">
+                <input type="button" class="next" value="保存" onclick="submitUser()"/>
+            </div>
+        </div>
+    </form>
+
+</div>
+
+<script src="http://www.jq22.com/jquery/jquery-1.10.2.js"></script>
+<script th:src="@{/tuodanweb/js/json.js}"></script>
+<script th:src="@{/tuodanweb/js/jsonExchange.js}"></script>
+<script th:src="@{/tuodanweb/js/mPicker.min.js}"></script>
+<script chartset="UTF-8">
+    $(function() {
+        /**
+         * 联动的picker
+         * 三级
+         */
+        $('.select-value').mPicker({
+            level:3,
+            dataJson: city3,
+            Linkage:true,
+            rows:6,
+            idDefault:true,
+            splitStr:'-',
+            header:'<div class="mPicker-header">常驻地址</div>',
+            confirm:function(json){
+                $(".select-value").val(json.values);
+            },
+            cancel:function(json){
+                $(".select-value").val(json.values);
+            }
+        })
+
+        /**
+         * 联动的picker
+         * 三级
+         */
+        $('.select-value01').mPicker({
+            level:3,
+            dataJson: city3,
+            Linkage:true,
+            rows:6,
+            idDefault:true,
+            splitStr:'-',
+            header:'<div class="mPicker-header">理想TA所在地</div>',
+            confirm:function(json){
+                $(".select-value01").val(json.values);
+            },
+            cancel:function(json){
+                $(".select-value01").val(json.values);
+            }
+        })
+    });
+    /**
+     * 根据手机获取验证码
+     */
+    function sendSmsVerify(val) {
+        if($("#phone").val() == ""){
+            alert("请输入正确的手机号");
+        }else{
+            $.ajax({
+                type: "get",
+                url: ctx + "xhn/sendSmsVerify",
+                data: {
+                    "phone": $("#phone").val()
+                },
+                success: function (data) {
+                    settime(val);
+                }
+            });
+        }
+
+    }
+
+    /**
+     * 成功返回后等待短信验证码时间并表明不可重复发送
+     */
+    var countdown=60;
+    function settime(val) {
+        if (countdown == 0) {
+            val.removeAttribute("disabled");
+            val.value="免费获取验证码";
+            countdown = 60;
+            return;
+        } else {
+            val.setAttribute("disabled", true);
+            val.value="重新发送(" + countdown + ")";
+            countdown--;
+        }
+        setTimeout(function() {
+            settime(val)
+        },1000)
+    }
+
+    /**
+     * 保存个人信息
+     */
+    function submitUser() {
+        $.ajax({
+            type: "post",
+            url: ctx + "xhn/updateRegisterUser",
+            data: $('#xhnRegisterUser').serialize(),
+            success: function (data) {
+                if(data.code == 0){
+                    window.location.href = ctx+"xhn/index";
+                }
+            }
+        });
+    }
+
+    var id=/^[1-9][0-9]{5}(19|20)[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}([0-9]|x|X)$/;
+    function cardtest (idCard){
+        if (id.test(idCard)===true){
+            var year = idCard.substring(6,10);
+            var month = idCard.substring(10,12);
+            var day = idCard.substring(12,14);
+            $("#birthDate").val(year+"-"+month+"-"+day);
+            var current = new Date().getFullYear();
+            $("#age").val(current-parseInt(year));
+        }else{
+            alert('身份证不正确');
+        }
+    };
+</script>
+</body>
+</html>

+ 248 - 0
mybusiness/src/main/resources/templates/appxhn/updateuser.html

@@ -0,0 +1,248 @@
+<!doctype html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+<meta charset="utf-8">
+<title>注册</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no,minimum-scale=1.0, maximum-scale=1.0">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/main.css}">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/jbxx.css}">
+<link rel="stylesheet" type="text/css" href="http://www.jq22.com/jquery/bootstrap-3.3.4.css">
+<link rel="stylesheet" th:href="@{/tuodanweb/css/mpicker.css}">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/public.css}">
+<link rel="stylesheet" type="text/css" th:href="@{/tuodanweb/css/mobileSelect.css}">
+<script th:src="@{/tuodanweb/js/jquery-1.7.2.min.js}"></script>
+<script th:src="@{/tuodanweb/js/mobileSelect.js}" type="text/javascript"></script>
+    <script th:inline="javascript">
+        var ctx = [[@{/}]];
+    </script>
+    <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
+    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
+    <script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+</head>
+
+<body>
+<div class="zcbj"><img th:src="@{/tuodanweb/images/zcbj.jpg}"/></div>
+<div class="zhuce" id="app">
+    <form id="xhnRegisterUser">
+        <div class="zhuce_in">
+            <div th:object="${user}">
+                <input th:value="*{openid}" id="openid" name="openid" type="hidden"/>
+                <h1 class="zc_title">微信头像</h1>
+                <img th:src="*{portraitUrl}" id="portraitUrl" name="portraitUrl"/>
+                <h1 class="zc_title">微信昵称</h1>
+                <input type="text" class="date2" th:value="*{wechatName}" id="wechatName" name="wechatName">
+                <h1 class="zc_title">真实姓名</h1>
+                <input type="text" class="date2" th:value="*{name}" name="name">
+                <h1 class="zc_title">身份证号</h1>
+                <input type="text" class="date2" th:value="*{idCard}" name="idCard" onchange="cardtest(this.value)">
+                <h1 class="zc_title">出生年月</h1>
+                <input type="text" class="date2" th:value="*{birthDate}" name="birthDate" id="birthDate" readonly>
+                <h1 class="zc_title">年龄</h1>
+                <input type="text" class="date2" th:value="*{age}" name="age" id="age" readonly>
+                <h1 class="zc_title">您的性别</h1>
+                <div class="xingbie">
+                    <span class="man"><input type="radio" name="sex" value="1" th:checked="*{sex == '1'}"/>男</span>
+                    <span class="woman"><input type="radio" name="sex" value="2" th:checked="*{sex == '2'}"/>女</span>
+                    <div class="clear"></div>
+                </div>
+                <h1 class="zc_title">常驻地址</h1>
+                <input type="text" class="select-value form-control" th:value="*{location}" name="location">
+                <h1 class="zc_title">星座</h1>
+                <select class="xingzuo" name="constellation">
+                    <option value="水瓶座" th:selected="*{constellation == '水瓶座'}">水瓶座</option>
+                    <option value="双鱼座" th:selected="*{constellation == '双鱼座'}">双鱼座</option>
+                    <option value="白羊座" th:selected="*{constellation == '白羊座'}">白羊座</option>
+                    <option value="金牛座" th:selected="*{constellation == '金牛座'}">金牛座</option>
+                    <option value="双子座" th:selected="*{constellation == '双子座'}">双子座</option>
+                    <option value="巨蟹座" th:selected="*{constellation == '巨蟹座'}">巨蟹座</option>
+                    <option value="狮子座" th:selected="*{constellation == '狮子座'}">狮子座</option>
+                    <option value="处女座" th:selected="*{constellation == '处女座'}">处女座</option>
+                    <option value="天秤座" th:selected="*{constellation == '天秤座'}">天秤座</option>
+                    <option value="天蝎座" th:selected="*{constellation == '天蝎座'}">天蝎座</option>
+                    <option value="射手座" th:selected="*{constellation == '射手座'}">射手座</option>
+                    <option value="摩羯座" th:selected="*{constellation == '摩羯座'}">摩羯座</option>
+                </select>
+                <h1 class="zc_title">身高CM</h1>
+                <input type="text" class="date2" name="height" th:value="*{height}">
+
+                <h1 class="zc_title">婚姻状态</h1>
+                <div class="xingbie">
+                    <span class="man"><input type="radio" name="isMarried" value="未婚" th:checked="*{isMarried == '未婚'}"/>未婚</span>
+                    <span class="woman"><input type="radio" name="isMarried" value="离异" th:checked="*{isMarried == '离异'}"/>离异</span>
+                    <div class="clear"></div>
+                </div>
+                <h1 class="zc_title">体重Kg</h1>
+                <input type="text" class="date2" name="weight" th:value="*{weight}">
+
+                <h1 class="zc_title">性格介绍</h1>
+                <div class="xinggeJS">
+                    <textarea name="character" th:value="*{character}">[[*{character}]]</textarea>
+                </div>
+                <h1 class="zc_title">工作性质</h1>
+                <select class="xingzuo" name="workNature">
+                    <option value="私企" th:selected="*{workNature == '私企'}">私企</option>
+                    <option value="国企" th:selected="*{workNature == '国企'}">国企</option>
+                    <option value="央企" th:selected="*{workNature == '央企'}">央企</option>
+                    <option value="个体工商户" th:selected="*{workNature == '个体工商户'}">个体工商户</option>
+                    <option value="企业法人" th:selected="*{workNature == '企业法人'}">企业法人</option>
+                    <option value="自主创业" th:selected="*{workNature == '自主创业'}">自主创业</option>
+                    <option value="事业单位(合同)" th:selected="*{workNature == '事业单位(合同)'}">事业单位(合同)</option>
+                    <option value="事业单位(在编)" th:selected="*{workNature == '事业单位(在编)'}">事业单位(在编)</option>
+                    <option value="公务员" th:selected="*{workNature == '公务员'}">公务员</option>
+                    <option value="军人" th:selected="*{workNature == '军人'}">军人</option>
+                    <option value="机关单位" th:selected="*{workNature == '机关单位'}">机关单位</option>
+                    <option value="学生" th:selected="*{workNature == '学生'}">学生</option>
+                </select>
+                <h1 class="zc_title">学历</h1>
+                <select class="xingzuo" name="education">
+                    <option value="博士研究生" th:selected="*{education == '博士研究生'}">博士研究生</option>
+                    <option value="硕士研究生" th:selected="*{education == '硕士研究生'}">硕士研究生</option>
+                    <option value="本科" th:selected="*{education == '本科'}">本科</option>
+                    <option value="大专" th:selected="*{education == '大专'}">大专</option>
+                    <option value="高中" th:selected="*{education == '高中'}">高中</option>
+                    <option value="中专" th:selected="*{education == '中专'}">中专</option>
+                    <option value="初中" th:selected="*{education == '初中'}">初中</option>
+                    <option value="小学" th:selected="*{education == '小学'}">小学</option>
+                </select>
+                <h1 class="zc_title">职业</h1>
+                <div class="xinggeJS">
+                    <textarea name="occupation" th:value="*{character}">[[*{character}]]</textarea>
+                </div>
+                <h1 class="zc_title">是否接受异地</h1>
+                <div class="xingbie">
+                    <span class="man"><input type="radio" name="longDistanceLove" value="是" th:checked="*{longDistanceLove == '是'}"/>是</span>
+                    <span class="woman"><input type="radio" name="longDistanceLove" value="否" th:checked="*{longDistanceLove == '否'}"/>否</span>
+                    <div class="clear"></div>
+                </div>
+                <h1 class="zc_title">理想TA所在地</h1>
+                <input type="text" class="select-value01 form-control" th:value="*{taLocation}" name="taLocation">
+                <h1 class="zc_title">不接受的TA</h1>
+                <div class="xinggeJS">
+                    <textarea name="noAccepted" th:value="*{character}">[[*{noAccepted}]]</textarea>
+                </div>
+                <h1 class="zc_title">理想型的TA</h1>
+                <div class="xinggeJS">
+                    <textarea name="idealType" th:value="*{character}">[[*{idealType}]]</textarea>
+                </div>
+                <h1 class="zc_title">微信号或微信绑定手机号</h1>
+                <div class="weixin">
+                    <textarea name="wechatCode" th:value="*{character}">[[*{wechatCode}]]</textarea>
+                </div>
+                <h1 class="zc_title">微信添加暗号</h1>
+                <div class="weixin">
+                    <textarea name="wechatArgot" th:value="*{character}">[[*{wechatArgot}]]</textarea>
+                </div>
+                <h1 class="zc_title">手机号<input type="button" id="btn" value="获取验证码" onclick="sendSmsVerify(this)" /></h1>
+                <input type="text" class="date2" id="phone" name="phone" th:value="*{phone}">
+                <h1 class="zc_title">验证码</h1>
+                <input type="text" class="date2" name="shortMsg">
+                <input type="button" class="next" value="保存" onclick="submitUser()"/>
+            </div>
+        </div>
+    </form>
+
+</div>
+
+<script src="http://www.jq22.com/jquery/jquery-1.10.2.js"></script>
+<script th:src="@{/tuodanweb/js/json.js}"></script>
+<script th:src="@{/tuodanweb/js/jsonExchange.js}"></script>
+<script th:src="@{/tuodanweb/js/mPicker.min.js}"></script>
+<script chartset="UTF-8">
+    $(function() {
+        /**
+         * 联动的picker
+         * 三级
+         */
+        $('.select-value').mPicker({
+            level:3,
+            dataJson: city3,
+            Linkage:true,
+            rows:6,
+            idDefault:true,
+            splitStr:'-',
+            header:'<div class="mPicker-header">常驻地址</div>',
+            confirm:function(json){
+                $(".select-value").val(json.values);
+            },
+            cancel:function(json){
+                $(".select-value").val(json.values);
+            }
+        })
+
+        /**
+         * 联动的picker
+         * 三级
+         */
+        $('.select-value01').mPicker({
+            level:3,
+            dataJson: city3,
+            Linkage:true,
+            rows:6,
+            idDefault:true,
+            splitStr:'-',
+            header:'<div class="mPicker-header">理想TA所在地</div>',
+            confirm:function(json){
+                $(".select-value01").val(json.values);
+            },
+            cancel:function(json){
+                $(".select-value01").val(json.values);
+            }
+        })
+    });
+    function sendSmsVerify(val) {
+        settime(val);
+        $.ajax({
+            type: "get",
+            url: ctx + "xhn/sendSmsVerify",
+            data: {
+                "phone": $("#phone").val()
+            },
+            success: function (data) {
+            }
+        });
+    }
+    var countdown=60;
+    function settime(val) {
+        if (countdown == 0) {
+            val.removeAttribute("disabled");
+            val.value="免费获取验证码";
+            countdown = 60;
+        } else {
+            val.setAttribute("disabled", true);
+            val.value="重新发送(" + countdown + ")";
+            countdown--;
+        }
+        setTimeout(function() {
+            settime(val)
+        },1000)
+    }
+
+    function submitUser() {
+        $.ajax({
+            type: "post",
+            url: ctx + "xhn/updateRegisterUser",
+            data: $('#xhnRegisterUser').serialize(),
+            success: function (data) {
+                if(data.code == 0){
+                    window.location.href = ctx+"xhn/index";
+                }
+            }
+        });
+    }
+
+    var id=/^[1-9][0-9]{5}(19|20)[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}([0-9]|x|X)$/;
+    function cardtest (idCard){
+        if (id.test(idCard)===true){
+            var year = idCard.substring(6,10);
+            var month = idCard.substring(10,12);
+            var day = idCard.substring(12,14);
+            $("#birthDate").val(year+"-"+month+"-"+day);
+            var current = new Date().getFullYear();
+            $("#age").val(current-parseInt(year));
+        }else{
+            alert('身份证不正确');
+        }
+    };
+</script>
+</body>
+</html>

+ 19 - 0
mybusiness/src/main/resources/templates/appxhn/vipkt.html

@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8">
+		<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
+		<meta content="yes" name="apple-mobile-web-app-capable" />
+		<meta content="black" name="apple-mobile-web-app-status-bar-style" />
+		<meta content="telephone=no" name="format-detection" />
+		<title>关于我们</title>
+		<link href="../../../../../../ruoyi-admin/src/main/resources/static/tuodanweb/css/main.css" rel="stylesheet" />
+		<script src="../../../../../../ruoyi-admin/src/main/resources/static/tuodanweb/js/jquery.min.js" type="text/javascript"></script>
+	</head>
+	<body>
+
+		<div class="container">
+			<img src="../../../../../../ruoyi-admin/src/main/resources/static/tuodanweb/images/gjhy_03.png"/>
+		</div>
+	</body>
+</html>

ファイルの差分が大きいため隠しています
+ 79 - 0
mybusiness/src/main/resources/templates/appxhn/红娘列表.html


+ 53 - 0
mybusiness/src/main/resources/templates/xhnnotsingle/integral/add.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <th:block th:include="include :: header('新增小红娘金额、积分充值消费记录')"/>
+</head>
+<body class="white-bg">
+<div class="wrapper wrapper-content animated fadeInRight ibox-content">
+    <form class="form-horizontal m" id="form-integral-add">
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">金额||积分数
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="money" class="form-control" type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">获得||消费时间
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="consumeDate" class="form-control" type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">会员表的微信openid
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="userOpenid" class="form-control" type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">用途汉字描述
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="purpose" class="form-control" type="text">
+                            </div>
+                        </div>
+    </form>
+</div>
+<th:block th:include="include :: footer"/>
+<script th:inline="javascript">
+    var prefix = ctx + "xhnnotsingle/integral"
+    $("#form-integral-add").validate({
+        focusCleanup: true
+    });
+
+    function submitHandler() {
+        if ($.validate.form()) {
+            $.operate.save(prefix + "/add", $('#form-integral-add').serialize());
+        }
+    }
+</script>
+</body>
+</html>

+ 58 - 0
mybusiness/src/main/resources/templates/xhnnotsingle/integral/edit.html

@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <th:block th:include="include :: header('修改小红娘金额、积分充值消费记录')"/>
+</head>
+<body class="white-bg">
+<div class="wrapper wrapper-content animated fadeInRight ibox-content">
+    <form class="form-horizontal m" id="form-integral-edit" th:object="${xhnIntegral}">
+        <input name="id" th:field="*{id}" type="hidden">
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">金额||积分数
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="money" th:field="*{money}" class="form-control"
+                                       type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">获得||消费时间
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="consumeDate" th:field="*{consumeDate}" class="form-control"
+                                       type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">会员表的微信openid
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="userOpenid" th:field="*{userOpenid}" class="form-control"
+                                       type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">用途汉字描述
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="purpose" th:field="*{purpose}" class="form-control"
+                                       type="text">
+                            </div>
+                        </div>
+    </form>
+</div>
+<th:block th:include="include :: footer"/>
+<script th:inline="javascript">
+    var prefix = ctx + "xhnnotsingle/integral";
+    $("#form-integral-edit").validate({
+        focusCleanup: true
+    });
+
+    function submitHandler() {
+        if ($.validate.form()) {
+            $.operate.save(prefix + "/edit", $('#form-integral-edit').serialize());
+        }
+    }
+</script>
+</body>
+</html>

+ 118 - 0
mybusiness/src/main/resources/templates/xhnnotsingle/integral/integral.html

@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+<head>
+    <th:block th:include="include :: header('小红娘金额、积分充值消费记录列表')"/>
+</head>
+<body class="gray-bg">
+<div class="container-div">
+    <div class="row">
+        <div class="col-sm-12 search-collapse">
+            <form id="formId">
+                <div class="select-list">
+                    <ul>
+                                    <li>
+                                        <label>金额||积分数:</label>
+                                        <input type="text" name="money"/>
+                                    </li>
+                                    <li>
+                                        <label>获得||消费时间:</label>
+                                        <input type="text" name="consumeDate"/>
+                                    </li>
+                                    <li>
+                                        <label>会员表的微信openid:</label>
+                                        <input type="text" name="userOpenid"/>
+                                    </li>
+                                    <li>
+                                        <label>用途汉字描述:</label>
+                                        <input type="text" name="purpose"/>
+                                    </li>
+                        <li>
+                            <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i
+                                    class="fa fa-search"></i>&nbsp;搜索</a>
+                            <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i
+                                    class="fa fa-refresh"></i>&nbsp;重置</a>
+                        </li>
+                    </ul>
+                </div>
+            </form>
+        </div>
+
+        <div class="btn-group-sm" id="toolbar" role="group">
+            <a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="xhnnotsingle:integral:add">
+                <i class="fa fa-plus"></i> 添加
+            </a>
+            <a class="btn btn-primary single disabled" onclick="$.operate.edit()"
+               shiro:hasPermission="xhnnotsingle:integral:edit">
+                <i class="fa fa-edit"></i> 修改
+            </a>
+            <a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()"
+               shiro:hasPermission="xhnnotsingle:integral:remove">
+                <i class="fa fa-remove"></i> 删除
+            </a>
+            <a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="xhnnotsingle:integral:export">
+                <i class="fa fa-download"></i> 导出
+            </a>
+        </div>
+        <div class="col-sm-12 select-table table-striped">
+            <table id="bootstrap-table"></table>
+        </div>
+    </div>
+</div>
+<th:block th:include="include :: footer"/>
+<script th:inline="javascript">
+    var editFlag = [[${@permission.hasPermi('xhnnotsingle:integral:edit')}]];
+    var removeFlag = [[${@permission.hasPermi('xhnnotsingle:integral:remove')}]];
+    var prefix = ctx + "xhnnotsingle/integral";
+
+    $(function () {
+        var options = {
+            url: prefix + "/list",
+            createUrl: prefix + "/add",
+            updateUrl: prefix + "/edit/{id}",
+            removeUrl: prefix + "/remove",
+            exportUrl: prefix + "/export",
+            modalName: "小红娘金额、积分充值消费记录",
+            columns: [{
+                checkbox: true
+            },
+                        {
+                            field: 'id',
+                            title: 'id',
+                            visible: false
+                        },
+                        {
+                            field: 'money',
+                            title: '金额||积分数'
+                        },
+                        {
+                            field: 'consumeDate',
+                            title: '获得||消费时间'
+                        },
+                        {
+                            field: 'orderType',
+                            title: '类型  1金额  2积分'
+                        },
+                        {
+                            field: 'userOpenid',
+                            title: '会员表的微信openid'
+                        },
+                        {
+                            field: 'purpose',
+                            title: '用途汉字描述'
+                        },
+                {
+                    title: '操作',
+                    align: 'center',
+                    formatter: function (value, row, index) {
+                        var actions = [];
+                        actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.id + '\')"><i class="fa fa-edit"></i>编辑</a> ');
+                        actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.id + '\')"><i class="fa fa-remove"></i>删除</a>');
+                        return actions.join('');
+                    }
+                }]
+        };
+        $.table.init(options);
+    });
+</script>
+</body>
+</html>

+ 53 - 0
mybusiness/src/main/resources/templates/xhnnotsingle/order/add.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <th:block th:include="include :: header('新增微信充值订单')"/>
+</head>
+<body class="white-bg">
+<div class="wrapper wrapper-content animated fadeInRight ibox-content">
+    <form class="form-horizontal m" id="form-order-add">
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">订单号
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="orderCode" class="form-control" maxlength="20"  type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">充值金额
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="money" class="form-control" maxlength="20"  type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">充值时间
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="orderDate" class="form-control" maxlength="20"  type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">付款状态   标明是付款中,还是付款成功
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="state" class="form-control" type="text">
+                            </div>
+                        </div>
+    </form>
+</div>
+<th:block th:include="include :: footer"/>
+<script th:inline="javascript">
+    var prefix = ctx + "xhnnotsingle/order"
+    $("#form-order-add").validate({
+        focusCleanup: true
+    });
+
+    function submitHandler() {
+        if ($.validate.form()) {
+            $.operate.save(prefix + "/add", $('#form-order-add').serialize());
+        }
+    }
+</script>
+</body>
+</html>

+ 50 - 0
mybusiness/src/main/resources/templates/xhnnotsingle/order/edit.html

@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <th:block th:include="include :: header('修改微信充值订单')"/>
+</head>
+<body class="white-bg">
+<div class="wrapper wrapper-content animated fadeInRight ibox-content">
+    <form class="form-horizontal m" id="form-order-edit" th:object="${xhnOrder}">
+        <input name="orderCode" th:field="*{orderCode}" type="hidden">
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">充值金额
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="money" th:field="*{money}" maxlength="20"  class="form-control"
+                                       type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">充值时间
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="orderDate" th:field="*{orderDate}" class="form-control"
+                                       type="text">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-3 control-label">付款状态   标明是付款中,还是付款成功
+                                :</label>
+                            <div class="col-sm-8">
+                                <input name="state" th:field="*{state}" class="form-control"
+                                       type="text">
+                            </div>
+                        </div>
+    </form>
+</div>
+<th:block th:include="include :: footer"/>
+<script th:inline="javascript">
+    var prefix = ctx + "xhnnotsingle/order";
+    $("#form-order-edit").validate({
+        focusCleanup: true
+    });
+
+    function submitHandler() {
+        if ($.validate.form()) {
+            $.operate.save(prefix + "/edit", $('#form-order-edit').serialize());
+        }
+    }
+</script>
+</body>
+</html>

+ 101 - 0
mybusiness/src/main/resources/templates/xhnnotsingle/order/order.html

@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+<head>
+    <th:block th:include="include :: header('微信充值订单列表')"/>
+</head>
+<body class="gray-bg">
+<div class="container-div">
+    <div class="row">
+        <div class="col-sm-12 search-collapse">
+            <form id="formId">
+                <div class="select-list">
+                    <ul>
+                                    <li>
+                                        <label>充值金额:</label>
+                                        <input type="text" name="money"/>
+                                    </li>
+                                    <li>
+                                        <label>充值时间:</label>
+                                        <input type="text" name="orderDate"/>
+                                    </li>
+                                    <li>
+                                        <label>付款状态   标明是付款中,还是付款成功:</label>
+                                        <input type="text" name="state"/>
+                                    </li>
+                        <li>
+                            <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i
+                                    class="fa fa-search"></i>&nbsp;搜索</a>
+                            <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i
+                                    class="fa fa-refresh"></i>&nbsp;重置</a>
+                        </li>
+                    </ul>
+                </div>
+            </form>
+        </div>
+
+        <div class="btn-group-sm" id="toolbar" role="group">
+            <a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="xhnnotsingle:order:add">
+                <i class="fa fa-plus"></i> 添加
+            </a>
+            <a class="btn btn-primary single disabled" onclick="$.operate.edit()"
+               shiro:hasPermission="xhnnotsingle:order:edit">
+                <i class="fa fa-edit"></i> 修改
+            </a>
+            <a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()"
+               shiro:hasPermission="xhnnotsingle:order:remove">
+                <i class="fa fa-remove"></i> 删除
+            </a>
+            <a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="xhnnotsingle:order:export">
+                <i class="fa fa-download"></i> 导出
+            </a>
+        </div>
+        <div class="col-sm-12 select-table table-striped">
+            <table id="bootstrap-table"></table>
+        </div>
+    </div>
+</div>
+<th:block th:include="include :: footer"/>
+<script th:inline="javascript">
+    var editFlag = [[${@permission.hasPermi('xhnnotsingle:order:edit')}]];
+    var removeFlag = [[${@permission.hasPermi('xhnnotsingle:order:remove')}]];
+    var prefix = ctx + "xhnnotsingle/order";
+
+    $(function () {
+        var options = {
+            url: prefix + "/list",
+            createUrl: prefix + "/add",
+            updateUrl: prefix + "/edit/{id}",
+            removeUrl: prefix + "/remove",
+            exportUrl: prefix + "/export",
+            modalName: "微信充值订单",
+            columns: [{
+                checkbox: true
+            },
+                        {
+                            field: 'money',
+                            title: '充值金额'
+                        },
+                        {
+                            field: 'orderDate',
+                            title: '充值时间'
+                        },
+                        {
+                            field: 'state',
+                            title: '付款状态   标明是付款中,还是付款成功'
+                        },
+                {
+                    title: '操作',
+                    align: 'center',
+                    formatter: function (value, row, index) {
+                        var actions = [];
+                        actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.orderCode + '\')"><i class="fa fa-edit"></i>编辑</a> ');
+                        actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.orderCode + '\')"><i class="fa fa-remove"></i>删除</a>');
+                        return actions.join('');
+                    }
+                }]
+        };
+        $.table.init(options);
+    });
+</script>
+</body>
+</html>

BIN
ruoyi-admin/src/main/resources/apiclient_cert.p12


+ 23 - 0
ruoyi-admin/src/main/resources/apiclient_cert.pem

@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID6TCCAtGgAwIBAgIUL1ilzCX9F2CEDxqkztHsYbWS6Q4wDQYJKoZIhvcNAQEL
+BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
+FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
+Q0EwHhcNMjIxMTIzMDkyMjU3WhcNMjcxMTIyMDkyMjU3WjB7MRMwEQYDVQQDDAox
+NTQwODEzMDkxMRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xJzAlBgNVBAsM
+HumVv+aYpemmluS9s+enkeaKgOaciemZkOWFrOWPuDELMAkGA1UEBgwCQ04xETAP
+BgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+m9FkUcstckSKHE7hH0OKsZsSukWol3diFm/fRZjEz7NGMNc/K5iFDu5lQ+NBHr0s
+T5v5BzdfILdzJNE2pAFFWs6D/aVJBuZPAtsChUIfFZJsHbsUnaFSP+5NJU4GTDwJ
+5BGwxD9chYmyF3LpQbCDRkxoUu97qfH3cvjyaZRqYgdwO25hIXoQEbEa0gq1iTDd
+opy3kDAjCZlgYun7Pl63sUvghtdcRc8Nr5Ix5C26F5ViAymSkuKPD0y9GyqX9FrP
+bOisTpDKb/wcAdJxuf+pxmwghcNWK6wEo8img4UEL1AV1RAGM1kydspYy8LppqlB
+83enn/hcTTjuT5uF6QwmeQIDAQABo4GBMH8wCQYDVR0TBAIwADALBgNVHQ8EBAMC
+A/gwZQYDVR0fBF4wXDBaoFigVoZUaHR0cDovL2V2Y2EuaXRydXMuY29tLmNuL3B1
+YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRCMDZBRDM5NzU0OTg0NkMw
+MUMzRThFQkQyMA0GCSqGSIb3DQEBCwUAA4IBAQAvBJvqvZIByBTp3p1RzUkLGz4/
+iErOYkb+6Fp6zfKNqj+C6e+O7Fv6bOLuhNyfphHObD2DB0Tl3wWDo0xzfnfp6X2G
+aSympDljEeRuAbOipsmabRIT4ivilLddASHr2Dl1Dxx7iOziOI22jN/Aqu5/xUVi
+qhAsbm2mi1v4IbJXHUy/uuIchgzwUjZt05VScMhm4eKCzH+52a2kwHerG/kS8HmN
+ZRKVB6vOLLrnM8JH+2Lq03S0HqCR9otrgFMOBXTnkyi1lGv+WUDspTfDcr/ZBeUj
+wCZmsGy4IXZQKmK8VSlbyIyc3NxvyelDOzcXtMH5Ix5s6WTEESaKQ9fRWOwe
+-----END CERTIFICATE-----

+ 28 - 0
ruoyi-admin/src/main/resources/apiclient_key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCb0WRRyy1yRIoc
+TuEfQ4qxmxK6RaiXd2IWb99FmMTPs0Yw1z8rmIUO7mVD40EevSxPm/kHN18gt3Mk
+0TakAUVazoP9pUkG5k8C2wKFQh8VkmwduxSdoVI/7k0lTgZMPAnkEbDEP1yFibIX
+culBsINGTGhS73up8fdy+PJplGpiB3A7bmEhehARsRrSCrWJMN2inLeQMCMJmWBi
+6fs+XrexS+CG11xFzw2vkjHkLboXlWIDKZKS4o8PTL0bKpf0Ws9s6KxOkMpv/BwB
+0nG5/6nGbCCFw1YrrASjyKaDhQQvUBXVEAYzWTJ2yljLwummqUHzd6ef+FxNOO5P
+m4XpDCZ5AgMBAAECggEAUnEYhhpdIk9XT6EAhZ0j3dKxtvKyP+m/cSWh2V36C/bL
+lX3dg4CALiU+WQv9YXkv37MjsIjAgTvu5TT4lw/BFUmdFnAEUm06ssUT8VPJxI0j
+PRvFzZ0UOpUoJivBM0a06QHfKAZqOpFfM/7toX72BhTZQcBKCuHmTeOfxvLq0pBP
+lnqN49yRWbEmBUTnBwYIFAxBJtIyIhLZMZqLwA5LpeDwSHf8AnEnbJ8l/diQNuzC
+KHwbO0XxdIoij+n8DbV7nfyItMKeNqXNhfFgpF4fkcqC/wFEqKJx6BPnhC9wzERR
+0ktApITeJshJD8xo+6FZFO3FFq9/qyfWFUeqBvMyrQKBgQDLBImZm0jvoW2hdREo
+z80BAEHEUMdmSy97dVHwP8MkQOVUvIU7G7/UPX7CEB6qq2cAkEpMEcKe/BEI3YTu
+DaDrYVL05vRI6/HEUZP1HQ/7l/q25U7n67CGr1RW5CIb3KzdkTGTdqYwTfbkKWPT
+M0Bv4U4DWApfYC/SDpoDsTo24wKBgQDEe3jJsRFsWNOl95vlbBOYSIS2pDJ1f1W1
+JJzeY+/WprJwwjwQ6zzYDYZYoaq2VIqg7pWgVCADfQor/XiwCebC5JC+HvY47uCb
+J5xPm+8VNafL0nbXByYFQ4TBb5FpgoldSvxGfG2H7KNKhZyeLx2g5E0yhdZ6eUL0
+Altild5P8wKBgQCrWjoVl1Z5Q4Q32YpRKws6BBB+dTvH4D/EcWJmaDr513h5C+Fb
+4XEI35qki1yG6sK2XBMJn5fn/pMWK3puPryKzwtov6KCBft6muxJ32zFCyLGykbO
+Q0UseMGz0Tvkzl5taIYyk9tzO+rd6v1hnomNpNnmwqGGnLXgEwj1gtnCxwKBgATC
+BKvFqOlZwU5Mj67fjh3q66PMmBg2TCoFx/Kustkc0kdevLugJqoIsc8low+lPnIl
+NORcBSZwebWU/r4sjzT6+fJQp5+7UR8Qt31KBmEaGoUaBJyyZJ5OA0H35KqMaa0j
+qrY+PxzCgzXDxIYkF/CUdrYZfZcbpp5oDDR38pk3AoGBAIU7SZLWL4xN2cBMDOSQ
+hdtu68yMBAEOa3CWoL6FUzXrl0cWLCyC1yMu8GPwK1aV9yB2lJkrCb3ebb9KSoqe
+hxJsUOL6FYFhcC93TGCPdPyXD/v4+SnGD2GVexP5pYquDlT+838QWzg5KdppOvLk
+fi544DxB4gSiMmDiPMPo4Dm5
+-----END PRIVATE KEY-----

ファイルの差分が大きいため隠しています
+ 6313 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinputPrint.min.js


ファイルの差分が大きいため隠しています
+ 10 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/columns/bootstrap-table-fixed-columns.min.js


ファイルの差分が大きいため隠しています
+ 10 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/editable/bootstrap-table-editable.min.js


ファイルの差分が大きいため隠しています
+ 2257 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/export/tableExport.js


+ 117 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/reorder/bootstrap-table-reorder.js

@@ -0,0 +1,117 @@
+/**
+ * @author: Dennis Hernández
+ * 实现表格拖拽功能
+ * @version: v1.0.1
+ */
+(function ($) {
+
+    'use strict';
+
+    var isSearch = false;
+
+    var rowAttr = function (row, index) {
+        return {
+            id: 'customId_' + index
+        };
+    };
+
+    $.extend($.fn.bootstrapTable.defaults, {
+        reorderableRows: false,
+        onDragStyle: null,
+        onDropStyle: null,
+        onDragClass: "reorder_rows_onDragClass",
+        dragHandle: null,
+        useRowAttrFunc: false,
+        onReorderRowsDrag: function (table, row) {
+            return false;
+        },
+        onReorderRowsDrop: function (table, row) {
+            return false;
+        },
+        onReorderRow: function (newData) {
+             return false;
+        }
+    });
+
+    $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
+        'reorder-row.bs.table': 'onReorderRow'
+    });
+
+    var BootstrapTable = $.fn.bootstrapTable.Constructor,
+        _init = BootstrapTable.prototype.init,
+        _initSearch = BootstrapTable.prototype.initSearch;
+
+    BootstrapTable.prototype.init = function () {
+
+        if (!this.options.reorderableRows) {
+            _init.apply(this, Array.prototype.slice.apply(arguments));
+            return;
+        }
+
+        var that = this;
+        if (this.options.useRowAttrFunc) {
+            this.options.rowAttributes = rowAttr;
+        }
+
+        var onPostBody = this.options.onPostBody;
+        this.options.onPostBody = function () {
+            setTimeout(function () {
+                that.makeRowsReorderable();
+                onPostBody.apply();
+            }, 1);
+        };
+
+        _init.apply(this, Array.prototype.slice.apply(arguments));
+    };
+
+    BootstrapTable.prototype.initSearch = function () {
+        _initSearch.apply(this, Array.prototype.slice.apply(arguments));
+
+        if (!this.options.reorderableRows) {
+            return;
+        }
+
+        //Known issue after search if you reorder the rows the data is not display properly
+        //isSearch = true;
+    };
+
+    BootstrapTable.prototype.makeRowsReorderable = function () {
+        if (this.options.cardView) {
+            return;
+        }
+
+        var that = this;
+        this.$el.tableDnD({
+            onDragStyle: that.options.onDragStyle,
+            onDropStyle: that.options.onDropStyle,
+            onDragClass: that.options.onDragClass,
+            onDrop: that.onDrop,
+            onDragStart: that.options.onReorderRowsDrag,
+            dragHandle: that.options.dragHandle
+        });
+    };
+
+    BootstrapTable.prototype.onDrop = function (table, droppedRow) {
+        var tableBs = $(table),
+            tableBsData = tableBs.data('bootstrap.table'),
+            tableBsOptions = tableBs.data('bootstrap.table').options,
+            row = null,
+            newData = [];
+
+        for (var i = 0; i < table.tBodies[0].rows.length; i++) {
+            row = $(table.tBodies[0].rows[i]);
+            newData.push(tableBsOptions.data[row.data('index')]);
+            row.data('index', i).attr('data-index', i);
+        }
+
+        tableBsOptions.data = tableBsOptions.data.slice(0, tableBsData.pageFrom - 1)
+            .concat(newData)
+            .concat(tableBsOptions.data.slice(tableBsData.pageTo));
+
+        //Call the user defined function
+        tableBsOptions.onReorderRowsDrop.apply(table, [table, droppedRow]);
+
+        //Call the event reorder-row
+        tableBsData.trigger('reorder-row', newData);
+    };
+})(jQuery);

+ 598 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/reorder/jquery.tablednd.js

@@ -0,0 +1,598 @@
+/**
+ * TableDnD plug-in for JQuery, allows you to drag and drop table rows
+ * You can set up various options to control how the system will work
+ * Copyright (c) Denis Howlett <denish@isocra.com>
+ * License: MIT.
+ * See https://github.com/isocra/TableDnD
+ */
+!function ($, window, document, undefined) {
+
+var startEvent = 'touchstart mousedown',
+    moveEvent  = 'touchmove mousemove',
+    endEvent   = 'touchend mouseup';
+
+$(document).ready(function () {
+    function parseStyle(css) {
+        var objMap = {},
+            parts = css.match(/([^;:]+)/g) || [];
+        while (parts.length)
+            objMap[parts.shift()] = parts.shift().trim();
+
+        return objMap;
+    }
+    $('table').each(function () {
+        if ($(this).data('table') === 'dnd') {
+
+            $(this).tableDnD({
+                onDragStyle: $(this).data('ondragstyle') && parseStyle($(this).data('ondragstyle')) || null,
+                onDropStyle: $(this).data('ondropstyle') && parseStyle($(this).data('ondropstyle')) || null,
+                onDragClass: $(this).data('ondragclass') === undefined && "tDnD_whileDrag" || $(this).data('ondragclass'),
+                onDrop: $(this).data('ondrop') && new Function('table', 'row', $(this).data('ondrop')), // 'return eval("'+$(this).data('ondrop')+'");') || null,
+                onDragStart: $(this).data('ondragstart') && new Function('table', 'row' ,$(this).data('ondragstart')), // 'return eval("'+$(this).data('ondragstart')+'");') || null,
+                onDragStop: $(this).data('ondragstop') && new Function('table', 'row' ,$(this).data('ondragstop')),
+                scrollAmount: $(this).data('scrollamount') || 5,
+                sensitivity: $(this).data('sensitivity') || 10,
+                hierarchyLevel: $(this).data('hierarchylevel') || 0,
+                indentArtifact: $(this).data('indentartifact') || '<div class="indent">&nbsp;</div>',
+                autoWidthAdjust: $(this).data('autowidthadjust') || true,
+                autoCleanRelations: $(this).data('autocleanrelations') || true,
+                jsonPretifySeparator: $(this).data('jsonpretifyseparator') || '\t',
+                serializeRegexp: $(this).data('serializeregexp') && new RegExp($(this).data('serializeregexp')) || /[^\-]*$/,
+                serializeParamName: $(this).data('serializeparamname') || false,
+                dragHandle: $(this).data('draghandle') || null
+            });
+        }
+
+
+    });
+});
+
+jQuery.tableDnD = {
+    /** Keep hold of the current table being dragged */
+    currentTable: null,
+    /** Keep hold of the current drag object if any */
+    dragObject: null,
+    /** The current mouse offset */
+    mouseOffset: null,
+    /** Remember the old value of X and Y so that we don't do too much processing */
+    oldX: 0,
+    oldY: 0,
+
+    /** Actually build the structure */
+    build: function(options) {
+        // Set up the defaults if any
+
+        this.each(function() {
+            // This is bound to each matching table, set up the defaults and override with user options
+            this.tableDnDConfig = $.extend({
+                onDragStyle: null,
+                onDropStyle: null,
+                // Add in the default class for whileDragging
+                onDragClass: "tDnD_whileDrag",
+                onDrop: null,
+                onDragStart: null,
+                onDragStop: null,
+                scrollAmount: 5,
+                /** Sensitivity setting will throttle the trigger rate for movement detection */
+                sensitivity: 10,
+                /** Hierarchy level to support parent child. 0 switches this functionality off */
+                hierarchyLevel: 0,
+                /** The html artifact to prepend the first cell with as indentation */
+                indentArtifact: '<div class="indent">&nbsp;</div>',
+                /** Automatically adjust width of first cell */
+                autoWidthAdjust: true,
+                /** Automatic clean-up to ensure relationship integrity */
+                autoCleanRelations: true,
+                /** Specify a number (4) as number of spaces or any indent string for JSON.stringify */
+                jsonPretifySeparator: '\t',
+                /** The regular expression to use to trim row IDs */
+                serializeRegexp: /[^\-]*$/,
+                /** If you want to specify another parameter name instead of the table ID */
+                serializeParamName: false,
+                /** If you give the name of a class here, then only Cells with this class will be draggable */
+                dragHandle: null
+            }, options || {});
+
+            // Now make the rows draggable
+            $.tableDnD.makeDraggable(this);
+            // Prepare hierarchy support
+            this.tableDnDConfig.hierarchyLevel
+                && $.tableDnD.makeIndented(this);
+        });
+
+        // Don't break the chain
+        return this;
+    },
+    makeIndented: function (table) {
+        var config = table.tableDnDConfig,
+            rows = table.rows,
+            firstCell = $(rows).first().find('td:first')[0],
+            indentLevel = 0,
+            cellWidth = 0,
+            longestCell,
+            tableStyle;
+
+        if ($(table).hasClass('indtd'))
+            return null;
+
+        tableStyle = $(table).addClass('indtd').attr('style');
+        $(table).css({whiteSpace: "nowrap"});
+
+        for (var w = 0; w < rows.length; w++) {
+            if (cellWidth < $(rows[w]).find('td:first').text().length) {
+                cellWidth = $(rows[w]).find('td:first').text().length;
+                longestCell = w;
+            }
+        }
+        $(firstCell).css({width: 'auto'});
+        for (w = 0; w < config.hierarchyLevel; w++)
+            $(rows[longestCell]).find('td:first').prepend(config.indentArtifact);
+        firstCell && $(firstCell).css({width: firstCell.offsetWidth});
+        tableStyle && $(table).css(tableStyle);
+
+        for (w = 0; w < config.hierarchyLevel; w++)
+            $(rows[longestCell]).find('td:first').children(':first').remove();
+
+        config.hierarchyLevel
+            && $(rows).each(function () {
+                indentLevel = $(this).data('level') || 0;
+                indentLevel <= config.hierarchyLevel
+                    && $(this).data('level', indentLevel)
+                    || $(this).data('level', 0);
+                for (var i = 0; i < $(this).data('level'); i++)
+                    $(this).find('td:first').prepend(config.indentArtifact);
+            });
+
+        return this;
+    },
+    /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
+    makeDraggable: function(table) {
+        var config = table.tableDnDConfig;
+
+        config.dragHandle
+            // We only need to add the event to the specified cells
+            && $(config.dragHandle, table).each(function() {
+                // The cell is bound to "this"
+                $(this).bind(startEvent, function(e) {
+                    $.tableDnD.initialiseDrag($(this).parents('tr')[0], table, this, e, config);
+                    return false;
+                });
+            })
+            // For backwards compatibility, we add the event to the whole row
+            // get all the rows as a wrapped set
+            || $(table.rows).each(function() {
+                // Iterate through each row, the row is bound to "this"
+                if (! $(this).hasClass("nodrag")) {
+                    $(this).bind(startEvent, function(e) {
+                        if (e.target.tagName === "TD" && event.target.className !== "nodrag") {
+                            $.tableDnD.initialiseDrag(this, table, this, e, config);
+                            return false;
+                        }
+                    }).css("cursor", "move"); // Store the tableDnD object
+                } else {
+                    $(this).css("cursor", ""); // Remove the cursor if we don't have the nodrag class
+                }
+            });
+    },
+    currentOrder: function() {
+        var rows = this.currentTable.rows;
+        return $.map(rows, function (val) {
+            return ($(val).data('level') + val.id).replace(/\s/g, '');
+        }).join('');
+    },
+    initialiseDrag: function(dragObject, table, target, e, config) {
+        this.dragObject    = dragObject;
+        this.currentTable  = table;
+        this.mouseOffset   = this.getMouseOffset(target, e);
+        this.originalOrder = this.currentOrder();
+
+        // Now we need to capture the mouse up and mouse move event
+        // We can use bind so that we don't interfere with other event handlers
+        $(document)
+            .bind(moveEvent, this.mousemove)
+            .bind(endEvent, this.mouseup);
+
+        // Call the onDragStart method if there is one
+        config.onDragStart
+            && config.onDragStart(table, target);
+    },
+    updateTables: function() {
+        this.each(function() {
+            // this is now bound to each matching table
+            if (this.tableDnDConfig)
+                $.tableDnD.makeDraggable(this);
+        });
+    },
+    /** Get the mouse coordinates from the event (allowing for browser differences) */
+    mouseCoords: function(e) {
+        if (e.originalEvent.changedTouches)
+            return {
+                x: e.originalEvent.changedTouches[0].clientX,
+                y: e.originalEvent.changedTouches[0].clientY
+            };
+
+        if(e.pageX || e.pageY)
+            return {
+                x: e.pageX,
+                y: e.pageY
+            };
+
+        return {
+            x: e.clientX + document.body.scrollLeft - document.body.clientLeft,
+            y: e.clientY + document.body.scrollTop  - document.body.clientTop
+        };
+    },
+    /** Given a target element and a mouse eent, get the mouse offset from that element.
+     To do this we need the element's position and the mouse position */
+    getMouseOffset: function(target, e) {
+        var mousePos,
+            docPos;
+
+        e = e || window.event;
+
+        docPos    = this.getPosition(target);
+        mousePos  = this.mouseCoords(e);
+
+        return {
+            x: mousePos.x - docPos.x,
+            y: mousePos.y - docPos.y
+        };
+    },
+    /** Get the position of an element by going up the DOM tree and adding up all the offsets */
+    getPosition: function(element) {
+        var left = 0,
+            top  = 0;
+
+        // Safari fix -- thanks to Luis Chato for this!
+        // Safari 2 doesn't correctly grab the offsetTop of a table row
+        // this is detailed here:
+        // http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
+        // the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
+        // note that firefox will return a text node as a first child, so designing a more thorough
+        // solution may need to take that into account, for now this seems to work in firefox, safari, ie
+        if (element.offsetHeight === 0)
+            element = element.firstChild; // a table cell
+
+        while (element.offsetParent) {
+            left   += element.offsetLeft;
+            top    += element.offsetTop;
+            element = element.offsetParent;
+        }
+
+        left += element.offsetLeft;
+        top  += element.offsetTop;
+
+        return {
+            x: left,
+            y: top
+        };
+    },
+    autoScroll: function (mousePos) {
+      var config       = this.currentTable.tableDnDConfig,
+          yOffset      = window.pageYOffset,
+          windowHeight = window.innerHeight
+            ? window.innerHeight
+            : document.documentElement.clientHeight
+            ? document.documentElement.clientHeight
+            : document.body.clientHeight;
+
+        // Windows version
+        // yOffset=document.body.scrollTop;
+        if (document.all)
+            if (typeof document.compatMode !== 'undefined'
+                && document.compatMode !== 'BackCompat')
+                yOffset = document.documentElement.scrollTop;
+            else if (typeof document.body !== 'undefined')
+                yOffset = document.body.scrollTop;
+
+        mousePos.y - yOffset < config.scrollAmount
+            && window.scrollBy(0, - config.scrollAmount)
+        || windowHeight - (mousePos.y - yOffset) < config.scrollAmount
+            && window.scrollBy(0, config.scrollAmount);
+
+    },
+    moveVerticle: function (moving, currentRow) {
+
+        if (0 !== moving.vertical
+            // If we're over a row then move the dragged row to there so that the user sees the
+            // effect dynamically
+            && currentRow
+            && this.dragObject !== currentRow
+            && this.dragObject.parentNode === currentRow.parentNode)
+            0 > moving.vertical
+                && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow.nextSibling)
+            || 0 < moving.vertical
+                && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow);
+
+    },
+    moveHorizontal: function (moving, currentRow) {
+        var config       = this.currentTable.tableDnDConfig,
+            currentLevel;
+
+        if (!config.hierarchyLevel
+            || 0 === moving.horizontal
+            // We only care if moving left or right on the current row
+            || !currentRow
+            || this.dragObject !== currentRow)
+                return null;
+
+            currentLevel = $(currentRow).data('level');
+
+            0 < moving.horizontal
+                && currentLevel > 0
+                && $(currentRow).find('td:first').children(':first').remove()
+                && $(currentRow).data('level', --currentLevel);
+
+            0 > moving.horizontal
+                && currentLevel < config.hierarchyLevel
+                && $(currentRow).prev().data('level') >= currentLevel
+                && $(currentRow).children(':first').prepend(config.indentArtifact)
+                && $(currentRow).data('level', ++currentLevel);
+
+    },
+    mousemove: function(e) {
+        var dragObj      = $($.tableDnD.dragObject),
+            config       = $.tableDnD.currentTable.tableDnDConfig,
+            currentRow,
+            mousePos,
+            moving,
+            x,
+            y;
+
+        e && e.preventDefault();
+
+        if (!$.tableDnD.dragObject)
+            return false;
+
+        // prevent touch device screen scrolling
+        e.type === 'touchmove'
+            && event.preventDefault(); // TODO verify this is event and not really e
+
+        // update the style to show we're dragging
+        config.onDragClass
+            && dragObj.addClass(config.onDragClass)
+            || dragObj.css(config.onDragStyle);
+
+        mousePos = $.tableDnD.mouseCoords(e);
+        x = mousePos.x - $.tableDnD.mouseOffset.x;
+        y = mousePos.y - $.tableDnD.mouseOffset.y;
+
+        // auto scroll the window
+        $.tableDnD.autoScroll(mousePos);
+
+        currentRow = $.tableDnD.findDropTargetRow(dragObj, y);
+        moving = $.tableDnD.findDragDirection(x, y);
+
+        $.tableDnD.moveVerticle(moving, currentRow);
+        $.tableDnD.moveHorizontal(moving, currentRow);
+
+        return false;
+    },
+    findDragDirection: function (x,y) {
+        var sensitivity = this.currentTable.tableDnDConfig.sensitivity,
+            oldX        = this.oldX,
+            oldY        = this.oldY,
+            xMin        = oldX - sensitivity,
+            xMax        = oldX + sensitivity,
+            yMin        = oldY - sensitivity,
+            yMax        = oldY + sensitivity,
+            moving      = {
+                horizontal: x >= xMin && x <= xMax ? 0 : x > oldX ? -1 : 1,
+                vertical  : y >= yMin && y <= yMax ? 0 : y > oldY ? -1 : 1
+            };
+
+        // update the old value
+        if (moving.horizontal !== 0)
+            this.oldX    = x;
+        if (moving.vertical   !== 0)
+            this.oldY    = y;
+
+        return moving;
+    },
+    /** We're only worried about the y position really, because we can only move rows up and down */
+    findDropTargetRow: function(draggedRow, y) {
+        var rowHeight = 0,
+            rows      = this.currentTable.rows,
+            config    = this.currentTable.tableDnDConfig,
+            rowY      = 0,
+            row       = null;
+
+        for (var i = 0; i < rows.length; i++) {
+            row       = rows[i];
+            rowY      = this.getPosition(row).y;
+            rowHeight = parseInt(row.offsetHeight) / 2;
+            if (row.offsetHeight === 0) {
+                rowY      = this.getPosition(row.firstChild).y;
+                rowHeight = parseInt(row.firstChild.offsetHeight) / 2;
+            }
+            // Because we always have to insert before, we need to offset the height a bit
+            if (y > (rowY - rowHeight) && y < (rowY + rowHeight))
+                // that's the row we're over
+                // If it's the same as the current row, ignore it
+                if (draggedRow.is(row)
+                    || (config.onAllowDrop
+                    && !config.onAllowDrop(draggedRow, row))
+                    // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
+                    || $(row).hasClass("nodrop"))
+                        return null;
+                else
+                    return row;
+        }
+        return null;
+    },
+    processMouseup: function() {
+        if (!this.currentTable || !this.dragObject)
+            return null;
+
+        var config      = this.currentTable.tableDnDConfig,
+            droppedRow  = this.dragObject,
+            parentLevel = 0,
+            myLevel     = 0;
+
+        // Unbind the event handlers
+        $(document)
+            .unbind(moveEvent, this.mousemove)
+            .unbind(endEvent,  this.mouseup);
+
+        config.hierarchyLevel
+            && config.autoCleanRelations
+            && $(this.currentTable.rows).first().find('td:first').children().each(function () {
+                myLevel = $(this).parents('tr:first').data('level');
+                myLevel
+                    && $(this).parents('tr:first').data('level', --myLevel)
+                    && $(this).remove();
+            })
+            && config.hierarchyLevel > 1
+            && $(this.currentTable.rows).each(function () {
+                myLevel = $(this).data('level');
+                if (myLevel > 1) {
+                    parentLevel = $(this).prev().data('level');
+                    while (myLevel > parentLevel + 1) {
+                        $(this).find('td:first').children(':first').remove();
+                        $(this).data('level', --myLevel);
+                    }
+                }
+            });
+
+        // If we have a dragObject, then we need to release it,
+        // The row will already have been moved to the right place so we just reset stuff
+        config.onDragClass
+            && $(droppedRow).removeClass(config.onDragClass)
+            || $(droppedRow).css(config.onDropStyle);
+
+        this.dragObject = null;
+        // Call the onDrop method if there is one
+        config.onDrop
+            && this.originalOrder !== this.currentOrder()
+            && $(droppedRow).hide().fadeIn('fast')
+            && config.onDrop(this.currentTable, droppedRow);
+
+        // Call the onDragStop method if there is one
+        config.onDragStop
+            && config.onDragStop(this.currentTable, droppedRow);
+
+        this.currentTable = null; // let go of the table too
+    },
+    mouseup: function(e) {
+        e && e.preventDefault();
+        $.tableDnD.processMouseup();
+        return false;
+    },
+    jsonize: function(pretify) {
+        var table = this.currentTable;
+        if (pretify)
+            return JSON.stringify(
+                this.tableData(table),
+                null,
+                table.tableDnDConfig.jsonPretifySeparator
+            );
+        return JSON.stringify(this.tableData(table));
+    },
+    serialize: function() {
+        return $.param(this.tableData(this.currentTable));
+    },
+    serializeTable: function(table) {
+        var result = "";
+        var paramName = table.tableDnDConfig.serializeParamName || table.id;
+        var rows = table.rows;
+        for (var i=0; i<rows.length; i++) {
+            if (result.length > 0) result += "&";
+            var rowId = rows[i].id;
+            if (rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
+                rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
+                result += paramName + '[]=' + rowId;
+            }
+        }
+        return result;
+    },
+    serializeTables: function() {
+        var result = [];
+        $('table').each(function() {
+            this.id && result.push($.param($.tableDnD.tableData(this)));
+        });
+        return result.join('&');
+    },
+    tableData: function (table) {
+        var config = table.tableDnDConfig,
+            previousIDs  = [],
+            currentLevel = 0,
+            indentLevel  = 0,
+            rowID        = null,
+            data         = {},
+            getSerializeRegexp,
+            paramName,
+            currentID,
+            rows;
+
+        if (!table)
+            table = this.currentTable;
+        if (!table || !table.rows || !table.rows.length)
+            return {error: { code: 500, message: "Not a valid table."}};
+        if (!table.id && !config.serializeParamName)
+            return {error: { code: 500, message: "No serializable unique id provided."}};
+        
+        rows      = config.autoCleanRelations
+                        && table.rows
+                        || $.makeArray(table.rows);
+        paramName = config.serializeParamName || table.id;
+        currentID = paramName;
+
+        getSerializeRegexp = function (rowId) {
+            if (rowId && config && config.serializeRegexp)
+                return rowId.match(config.serializeRegexp)[0];
+            return rowId;
+        };
+
+        data[currentID] = [];
+        !config.autoCleanRelations
+            && $(rows[0]).data('level')
+            && rows.unshift({id: 'undefined'});
+
+
+
+        for (var i=0; i < rows.length; i++) {
+            if (config.hierarchyLevel) {
+                indentLevel = $(rows[i]).data('level') || 0;
+                if (indentLevel === 0) {
+                    currentID   = paramName;
+                    previousIDs = [];
+                }
+                else if (indentLevel > currentLevel) {
+                    previousIDs.push([currentID, currentLevel]);
+                    currentID = getSerializeRegexp(rows[i-1].id);
+                }
+                else if (indentLevel < currentLevel) {
+                    for (var h = 0; h < previousIDs.length; h++) {
+                        if (previousIDs[h][1] === indentLevel)
+                            currentID         = previousIDs[h][0];
+                        if (previousIDs[h][1] >= currentLevel)
+                            previousIDs[h][1] = 0;
+                    }
+                }
+                currentLevel = indentLevel;
+
+                if (!$.isArray(data[currentID]))
+                    data[currentID] = [];
+                rowID = getSerializeRegexp(rows[i].id);
+                rowID && data[currentID].push(rowID);
+            }
+            else {
+                rowID = getSerializeRegexp(rows[i].id);
+                rowID && data[currentID].push(rowID);
+            }
+        }
+        return data;
+    }
+};
+
+jQuery.fn.extend(
+    {
+        tableDnD             : $.tableDnD.build,
+        tableDnDUpdate       : $.tableDnD.updateTables,
+        tableDnDSerialize    : $.proxy($.tableDnD.serialize, $.tableDnD),
+        tableDnDSerializeAll : $.tableDnD.serializeTables,
+        tableDnDData         : $.proxy($.tableDnD.tableData, $.tableDnD)
+    }
+);
+
+}(jQuery, window, window.document);

+ 747 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-treetable/bootstrap-treetable.js

@@ -0,0 +1,747 @@
+/**
+ * 基于bootstrapTreeTable/bootstrap-table-treegrid修改
+ * Copyright (c) 2019 LeiSP
+ */
+(function($) {
+    "use strict";
+
+    $.fn.bootstrapTreeTable = function(options, param) {
+        var target = $(this).data('bootstrap.tree.table');
+        target = target ? target : $(this);
+        // 如果是调用方法
+        if (typeof options == 'string') {
+            return $.fn.bootstrapTreeTable.methods[options](target, param);
+        }
+        // 如果是初始化组件
+        options = $.extend({}, $.fn.bootstrapTreeTable.defaults, options || {});
+        target.hasSelectItem = false;// 是否有radio或checkbox
+        target.data_list = null; //用于缓存格式化后的数据-按父分组
+        target.data_obj = null; //用于缓存格式化后的数据-按id存对象
+        target.hiddenColumns = []; //用于存放被隐藏列的field
+        target.lastAjaxParams; //用户最后一次请求的参数
+        target.isFixWidth=false; //是否有固定宽度
+        // 初始化
+        var init = function() {
+            // 初始化容器
+            initContainer();
+            // 初始化工具栏
+            initToolbar();
+            // 初始化表头
+            initHeader();
+            // 初始化表体
+            initBody();
+            // 初始化数据服务
+            initServer();
+            // 动态设置表头宽度
+            autoTheadWidth(true);
+            // 缓存target对象
+            target.data('bootstrap.tree.table', target);
+        }
+        // 初始化容器
+        var initContainer = function() {
+            // 在外层包装一下div,样式用的bootstrap-table的
+            var $main_div = $("<div class='bootstrap-tree-table'></div>");
+            var $treetable = $("<div class='treetable-table'></div>");
+            target.before($main_div);
+            $main_div.append($treetable);
+            $treetable.append(target);
+            target.addClass("table");
+            if (options.striped) {
+                target.addClass('table-striped');
+            }
+            if (options.bordered) {
+                target.addClass('table-bordered');
+            }
+            if (options.hover) {
+                target.addClass('table-hover');
+            }
+            if (options.condensed) {
+                target.addClass('table-condensed');
+            }
+            target.html("");
+        }
+        // 初始化工具栏
+        var initToolbar = function() {
+            var $toolbar = $("<div class='treetable-bars'></div>");
+            if (options.toolbar) {
+                $(options.toolbar).addClass('tool-left');
+                $toolbar.append($(options.toolbar));
+            }
+            var $rightToolbar = $('<div class="btn-group tool-right">');
+            $toolbar.append($rightToolbar);
+            target.parent().before($toolbar);
+            // ruoyi 是否显示检索信息
+            if (options.showSearch) {
+                var $searchBtn = $('<button class="btn btn-default btn-outline" type="button" aria-label="search" title="搜索"><i class="glyphicon glyphicon-search"></i></button>');
+                $rightToolbar.append($searchBtn);
+                registerSearchBtnClickEvent($searchBtn);
+            }
+            // 是否显示刷新按钮
+            if (options.showRefresh) {
+                var $refreshBtn = $('<button class="btn btn-default btn-outline" type="button" aria-label="refresh" title="刷新"><i class="glyphicon glyphicon-repeat"></i></button>');
+                $rightToolbar.append($refreshBtn);
+                registerRefreshBtnClickEvent($refreshBtn);
+            }
+            // 是否显示列选项
+            if (options.showColumns) {
+                var $columns_div = $('<div class="btn-group pull-right" title="列"><button type="button" aria-label="columns" class="btn btn-default btn-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><i class="glyphicon glyphicon-list"></i> <span class="caret"></span></button></div>');
+                var $columns_ul = $('<ul class="dropdown-menu columns" role="menu"></ul>');
+                $.each(options.columns, function(i, column) {
+                    if (column.field != 'selectItem') {
+                        var _li = null;
+                        if(typeof column.visible == "undefined"||column.visible==true){
+                            _li = $('<li role="menuitem"><label><input type="checkbox" checked="checked" data-field="'+column.field+'" value="'+column.field+'" > '+column.title+'</label></li>');
+                        }else{
+                            _li = $('<li role="menuitem"><label><input type="checkbox" data-field="'+column.field+'" value="'+column.field+'" > '+column.title+'</label></li>');
+                            target.hiddenColumns.push(column.field);
+                        }
+                        $columns_ul.append(_li);
+                    }
+                });
+                $columns_div.append($columns_ul);
+                $rightToolbar.append($columns_div);
+                // 注册列选项事件
+                registerColumnClickEvent();
+            }else{
+                $.each(options.columns, function(i, column) {
+                    if (column.field != 'selectItem') {
+                        if(!(typeof column.visible == "undefined"||column.visible==true)){
+                            target.hiddenColumns.push(column.field);
+                        }
+                    }
+                });
+            }
+        }
+        // 初始化隐藏列
+        var initHiddenColumns = function(){
+            $.each(target.hiddenColumns, function(i, field) {
+                target.find("."+field+"_cls").hide();
+            });
+        }
+        // 初始化表头
+        var initHeader = function() {
+            var $thr = $('<tr></tr>');
+            $.each(options.columns, function(i, column) {
+                var $th = null;
+                // 判断有没有选择列
+                if (i == 0 && column.field == 'selectItem') {
+                    target.hasSelectItem = true;
+                    $th = $('<th style="width:36px"></th>');
+                } else {
+                    $th = $('<th style="' + ((column.width) ? ('width:' + column.width) : '') + '" class="' + column.field + '_cls"></th>');
+                }
+                if((!target.isFixWidth)&& column.width){
+                    target.isFixWidth = column.width.indexOf("px")>-1?true:false;
+                }
+                $th.text(column.title);
+                $thr.append($th);
+            });
+            var $thead = $('<thead class="treetable-thead"></thead>');
+            $thead.append($thr);
+            target.append($thead);
+        }
+        // 初始化表体
+        var initBody = function() {
+            var $tbody = $('<tbody class="treetable-tbody"></tbody>');
+            target.append($tbody);
+            // 默认高度
+            if (options.height) {
+                $tbody.css("height", options.height);
+            }
+        }
+        // 初始化数据服务
+        var initServer = function(parms) {
+            // 加载数据前先清空
+            target.data_list = {};
+            target.data_obj = {};
+            var $tbody = target.find("tbody");
+            // 添加加载loading
+            var $loading = '<tr><td colspan="' + options.columns.length + '"><div style="display: block;text-align: center;">正在努力地加载数据中,请稍候……</div></td></tr>'
+            $tbody.html($loading);
+            if (options.url) {
+                $.ajax({
+                    type: options.type,
+                    url: options.url,
+                    data: parms ? parms : options.ajaxParams,
+                    dataType: "JSON",
+                    success: function(data, textStatus, jqXHR) {
+                    	data = calculateObjectValue(options, options.responseHandler, [data], data);
+                        renderTable(data);
+                        calculateObjectValue(options, options.onLoadSuccess, [data], data);
+                    },
+                    error: function(xhr, textStatus) {
+                        var _errorMsg = '<tr><td colspan="' + options.columns.length + '"><div style="display: block;text-align: center;">' + xhr.responseText + '</div></td></tr>'
+                        $tbody.html(_errorMsg);
+                    },
+                });
+            } else {
+                renderTable(options.data);
+            }
+        }
+        // 加载完数据后渲染表格
+        var renderTable = function(data) {
+            var $tbody = target.find("tbody");
+            // 先清空
+            $tbody.html("");
+            if (!data || data.length <= 0) {
+                var _empty = '<tr><td colspan="' + options.columns.length + '"><div style="display: block;text-align: center;">没有找到匹配的记录</div></td></tr>'
+                $tbody.html(_empty);
+                return;
+            }
+            // 缓存并格式化数据
+            formatData(data);
+            // 获取所有根节点
+            var rootNode = target.data_list["_root_"];
+            // 开始绘制
+            if (rootNode) {
+                $.each(rootNode, function(i, item) {
+                    var _child_row_id = "row_id_" + i
+                    recursionNode(item, 1, _child_row_id, "row_root");
+                });
+            }
+            // 下边的操作主要是为了查询时让一些没有根节点的节点显示
+            $.each(data, function(i, item) {
+                if (!item.isShow) {
+                    var tr = renderRow(item, false, 1, "", "");
+                    $tbody.append(tr);
+                }
+            });
+            target.append($tbody);
+            registerExpanderEvent();
+            registerRowClickEvent();
+            initHiddenColumns();
+            // 动态设置表头宽度
+            autoTheadWidth()
+        }
+        // 动态设置表头宽度
+        var autoTheadWidth = function(initFlag) {
+            if(options.height>0){
+                var $thead = target.find("thead");
+                var $tbody = target.find("tbody");
+                var borderWidth = parseInt(target.css("border-left-width")) + parseInt(target.css("border-right-width"))
+                
+                $thead.css("width", $tbody.children(":first").width());
+                if(initFlag){
+                    var resizeWaiter = false;
+                    $(window).resize(function() {
+                        if(!resizeWaiter){
+                            resizeWaiter = true;
+                            setTimeout(function(){
+                                if(!target.isFixWidth){
+                                    $tbody.css("width", target.parent().width()-borderWidth);
+                                }
+                                $thead.css("width", $tbody.children(":first").width());
+                                resizeWaiter = false;
+                            }, 300);
+                        }
+                    });
+                }
+            }
+        
+        }
+        // 缓存并格式化数据
+        var formatData = function(data) {
+            var _root = options.rootIdValue ? options.rootIdValue : null;
+            // 父节点属性列表
+            var parentCodes = [];
+            var rootFlag = false;
+            $.each(data, function(index, item) {
+            	if($.inArray(item[options.parentCode], parentCodes) == -1){
+            		parentCodes.push(item[options.parentCode]);
+                }
+            });
+            $.each(data, function(index, item) {
+                // 添加一个默认属性,用来判断当前节点有没有被显示
+                item.isShow = false;
+                // 顶级节点校验判断,兼容0,'0','',null
+                var _defaultRootFlag = item[options.parentCode] == '0' ||
+                item[options.parentCode] == 0 ||
+                item[options.parentCode] == null ||
+                item[options.parentCode] == '' ||
+                $.inArray(item[options.code], parentCodes) > 0 && !rootFlag;
+                if (!item[options.parentCode] || (_root ? (item[options.parentCode] == options.rootIdValue) : _defaultRootFlag)) {
+                	rootFlag = true;
+                	if (!target.data_list["_root_"]) {
+                        target.data_list["_root_"] = [];
+                    }
+                    if (!target.data_obj["id_" + item[options.code]]) {
+                        target.data_list["_root_"].push(item);
+                    }
+                } else {
+                    if (!target.data_list["_n_" + item[options.parentCode]]) {
+                        target.data_list["_n_" + item[options.parentCode]] = [];
+                    }
+                    if (!target.data_obj["id_" + item[options.code]]) {
+                        target.data_list["_n_" + item[options.parentCode]].push(item);
+                    }
+                }
+                target.data_obj["id_" + item[options.code]] = item;
+            });
+        }
+        // 递归获取子节点并且设置子节点
+        var recursionNode = function(parentNode, lv, row_id, p_id) {
+            var $tbody = target.find("tbody");
+            var _ls = target.data_list["_n_" + parentNode[options.code]];
+            var $tr = renderRow(parentNode, _ls ? true : false, lv, row_id, p_id);
+            $tbody.append($tr);
+            if (_ls) {
+                $.each(_ls, function(i, item) {
+                    var _child_row_id = row_id + "_" + i
+                    recursionNode(item, (lv + 1), _child_row_id, row_id)
+                });
+            }
+        };
+        // 绘制行
+        var renderRow = function(item, isP, lv, row_id, p_id) {
+            // 标记已显示
+            item.isShow = true;
+            item.row_id = row_id;
+            item.p_id = p_id;
+            item.lv = lv;
+            var $tr = $('<tr id="' + row_id + '" pid="' + p_id + '"></tr>');
+            var _icon = options.expanderCollapsedClass;
+            if (options.expandAll) {
+                $tr.css("display", "table");
+                _icon = options.expanderExpandedClass;
+            } else if (lv == 1) {
+                $tr.css("display", "table");
+                _icon = (options.expandFirst) ? options.expanderExpandedClass : options.expanderCollapsedClass;
+            } else if (lv == 2) {
+                if (options.expandFirst) {
+                    $tr.css("display", "table");
+                } else {
+                    $tr.css("display", "none");
+                }
+                _icon = options.expanderCollapsedClass;
+            } else {
+                $tr.css("display", "none");
+                _icon = options.expanderCollapsedClass;
+            }
+            $.each(options.columns, function(index, column) {
+                // 判断有没有选择列
+                if (column.field == 'selectItem') {
+                    target.hasSelectItem = true;
+                    var $td = $('<td style="text-align:center;width:36px"></td>');
+                    if (column.radio) {
+                        var _ipt = $('<input name="select_item" type="radio" value="' + item[options.code] + '"></input>');
+                        $td.append(_ipt);
+                    }
+                    if (column.checkbox) {
+                        var _ipt = $('<input name="select_item" type="checkbox" value="' + item[options.code] + '"></input>');
+                        $td.append(_ipt);
+                    }
+                    $tr.append($td);
+                } else {
+                    var $td = $('<td name="' + column.field + '" class="' + column.field + '_cls"></td>');
+                    if(column.width){
+                        $td.css("width",column.width);
+                    }
+                    if(column.align){
+                        $td.css("text-align",column.align);
+                    }
+                    if(options.expandColumn == index){
+                        $td.css("text-align","left");
+                    }
+                    if(column.valign){
+                        $td.css("vertical-align",column.valign);
+                    }
+                    if(options.showTitle){
+                        $td.addClass("ellipsis");
+                    }
+                    // 增加formatter渲染
+                    if (column.formatter) {
+                        $td.html(column.formatter.call(this, getItemField(item, column.field), item, index));
+                    } else {
+                        if(options.showTitle){
+                            // 只在字段没有formatter时才添加title属性
+                            $td.attr("title",item[column.field]);
+                        }
+                        $td.text(getItemField(item, column.field));
+                    }
+                    if (options.expandColumn == index) {
+                        if (!isP) {
+                            $td.prepend('<span class="treetable-expander"></span>')
+                        } else {
+                            $td.prepend('<span class="treetable-expander ' + _icon + '"></span>')
+                        }
+                        for (var int = 0; int < (lv - 1); int++) {
+                            $td.prepend('<span class="treetable-indent"></span>')
+                        }
+                    }
+                    $tr.append($td);
+                }
+            });
+            return $tr;
+        }
+        // 检索信息按钮点击事件
+        var registerSearchBtnClickEvent = function(btn) {
+            $(btn).off('click').on('click', function () {
+                $(".search-collapse").slideToggle();
+            });
+        }
+        // 注册刷新按钮点击事件
+        var registerRefreshBtnClickEvent = function(btn) {
+            $(btn).off('click').on('click', function () {
+                target.refresh();
+            });
+        }
+        // 注册列选项事件
+        var registerColumnClickEvent = function() {
+            $(".bootstrap-tree-table .treetable-bars .columns label input").off('click').on('click', function () {
+                var $this = $(this);
+                if($this.prop('checked')){
+                    target.showColumn($(this).val());
+                }else{
+                    target.hideColumn($(this).val());
+                }
+            });
+        }
+        // 注册行点击选中事件
+        var registerRowClickEvent = function() {
+            target.find("tbody").find("tr").unbind();
+            target.find("tbody").find("tr").click(function() {
+                if (target.hasSelectItem) {
+                    var _ipt = $(this).find("input[name='select_item']");
+                    if (_ipt.attr("type") == "radio") {
+                        _ipt.prop('checked', true);
+                        target.find("tbody").find("tr").removeClass("treetable-selected");
+                        $(this).addClass("treetable-selected");
+                    } else if (_ipt.attr("type") == "checkbox") {
+                    	if (_ipt.prop('checked')) {
+                    		_ipt.prop('checked', true);
+                    		target.find("tbody").find("tr").removeClass("treetable-selected");
+                    		$(this).addClass("treetable-selected");
+                    	} else {
+                    		_ipt.prop('checked', false);
+                    		target.find("tbody").find("tr").removeClass("treetable-selected");
+                    	}
+                    } else {
+                        if (_ipt.prop('checked')) {
+                            _ipt.prop('checked', false);
+                            $(this).removeClass("treetable-selected");
+                        } else {
+                            _ipt.prop('checked', true);
+                            $(this).addClass("treetable-selected");
+                        }
+                    }
+                }
+            });
+        }
+        // 注册小图标点击事件--展开缩起
+        var registerExpanderEvent = function() {
+            target.find("tbody").find("tr").find(".treetable-expander").unbind();
+            target.find("tbody").find("tr").find(".treetable-expander").click(function() {
+                var _isExpanded = $(this).hasClass(options.expanderExpandedClass);
+                var _isCollapsed = $(this).hasClass(options.expanderCollapsedClass);
+                if (_isExpanded || _isCollapsed) {
+                    var tr = $(this).parent().parent();
+                    var row_id = tr.attr("id");
+                    var _ls = target.find("tbody").find("tr[id^='" + row_id + "_']"); //下所有
+                    if (_isExpanded) {
+                        $(this).removeClass(options.expanderExpandedClass);
+                        $(this).addClass(options.expanderCollapsedClass);
+                        if (_ls && _ls.length > 0) {
+                            $.each(_ls, function(index, item) {
+                                $(item).css("display", "none");
+                            });
+                        }
+                    } else {
+                        $(this).removeClass(options.expanderCollapsedClass);
+                        $(this).addClass(options.expanderExpandedClass);
+                        if (_ls && _ls.length > 0) {
+                            $.each(_ls, function(index, item) {
+                                // 父icon
+                                var _p_icon = $("#" + $(item).attr("pid")).children().eq(options.expandColumn).find(".treetable-expander");
+                                if (_p_icon.hasClass(options.expanderExpandedClass)) {
+                                    $(item).css("display", "table");
+                                }
+                            });
+                        }
+                    }
+                }
+            });
+        }
+        // 刷新数据
+        target.refresh = function(parms) {
+            if(parms){
+                target.lastAjaxParams=parms;
+            }
+            initServer(target.lastAjaxParams);
+        }
+        // 添加数据刷新表格
+        target.appendData = function(data) {
+            // 下边的操作主要是为了查询时让一些没有根节点的节点显示
+            $.each(data, function(i, item) {
+                var _data = target.data_obj["id_" + item[options.code]];
+                var _p_data = target.data_obj["id_" + item[options.parentCode]];
+                var _c_list = target.data_list["_n_" + item[options.parentCode]];
+                var row_id = ""; //行id
+                var p_id = ""; //父行id
+                var _lv = 1; //如果没有父就是1默认显示
+                var tr; //要添加行的对象
+                if (_data && _data.row_id && _data.row_id != "") {
+                    row_id = _data.row_id; // 如果已经存在了,就直接引用原来的
+                }
+                if (_p_data) {
+                    p_id = _p_data.row_id;
+                    if (row_id == "") {
+                        var _tmp = 0
+                        if (_c_list && _c_list.length > 0) {
+                            _tmp = _c_list.length;
+                        }
+                        row_id = _p_data.row_id + "_" + _tmp;
+                    }
+                    _lv = _p_data.lv + 1; //如果有父
+                    // 绘制行
+                    tr = renderRow(item, false, _lv, row_id, p_id);
+
+                    var _p_icon = $("#" + _p_data.row_id).children().eq(options.expandColumn).find(".treetable-expander");
+                    var _isExpanded = _p_icon.hasClass(options.expanderExpandedClass);
+                    var _isCollapsed = _p_icon.hasClass(options.expanderCollapsedClass);
+                    // 父节点有没有展开收缩按钮
+                    if (_isExpanded || _isCollapsed) {
+                        // 父节点展开状态显示新加行
+                        if (_isExpanded) {
+                            tr.css("display", "table");
+                        }
+                    } else {
+                        // 父节点没有展开收缩按钮则添加
+                        _p_icon.addClass(options.expanderCollapsedClass);
+                    }
+
+                    if (_data) {
+                        $("#" + _data.row_id).before(tr);
+                        $("#" + _data.row_id).remove();
+                    } else {
+                        // 计算父的同级下一行
+                        var _tmp_ls = _p_data.row_id.split("_");
+                        var _p_next = _p_data.row_id.substring(0, _p_data.row_id.length - 1) + (parseInt(_tmp_ls[_tmp_ls.length - 1]) + 1);
+                        // 画上
+                        $("#" + _p_next).before(tr);
+                    }
+                } else {
+                    tr = renderRow(item, false, _lv, row_id, p_id);
+                    if (_data) {
+                        $("#" + _data.row_id).before(tr);
+                        $("#" + _data.row_id).remove();
+                    } else {
+                        // 画上
+                        var tbody = target.find("tbody");
+                        tbody.append(tr);
+                    }
+                }
+                item.isShow = true;
+                // 缓存并格式化数据
+                formatData([item]);
+            });
+            registerExpanderEvent();
+            registerRowClickEvent();
+            initHiddenColumns();
+        }
+
+        // 展开/折叠指定的行
+        target.toggleRow=function(id) {
+            var _rowData = target.data_obj["id_" + id];
+            var $row_expander = $("#"+_rowData.row_id).find(".treetable-expander");
+            $row_expander.trigger("click");
+        }
+        // 展开指定的行
+        target.expandRow=function(id) {
+            var _rowData = target.data_obj["id_" + id];
+            var $row_expander = $("#"+_rowData.row_id).find(".treetable-expander");
+            var _isCollapsed = $row_expander.hasClass(target.options.expanderCollapsedClass);
+            if (_isCollapsed) {
+                $row_expander.trigger("click");
+            }
+        }
+        // 折叠 指定的行
+        target.collapseRow=function(id) {
+            var _rowData = target.data_obj["id_" + id];
+            var $row_expander = $("#"+_rowData.row_id).find(".treetable-expander");
+            var _isExpanded = $row_expander.hasClass(target.options.expanderExpandedClass);
+            if (_isExpanded) {
+                $row_expander.trigger("click");
+            }
+        }
+        // 展开所有的行
+        target.expandAll=function() {
+            target.find("tbody").find("tr").find(".treetable-expander").each(function(i,n){
+                var _isCollapsed = $(n).hasClass(options.expanderCollapsedClass);
+                if (_isCollapsed) {
+                    $(n).trigger("click");
+                }
+            })
+        }
+        // 折叠所有的行
+        target.collapseAll=function() {
+            target.find("tbody").find("tr").find(".treetable-expander").each(function(i,n){
+                var _isExpanded = $(n).hasClass(options.expanderExpandedClass);
+                if (_isExpanded) {
+                    $(n).trigger("click");
+                }
+            })
+        }
+        // 显示指定列
+        target.showColumn=function(field,flag) {
+            var _index = $.inArray(field, target.hiddenColumns);
+            if (_index > -1) {
+                target.hiddenColumns.splice(_index, 1);
+            }
+            target.find("."+field+"_cls").show();
+            //是否更新列选项状态
+            if(flag&&options.showColumns){
+                var $input = $(".bootstrap-tree-table .treetable-bars .columns label").find("input[value='"+field+"']")
+                $input.prop("checked", 'checked');
+            }
+        }
+        // 隐藏指定列
+        target.hideColumn=function(field,flag) {
+            target.hiddenColumns.push(field);
+            target.find("."+field+"_cls").hide();
+            //是否更新列选项状态
+            if(flag&&options.showColumns){
+                var $input = $(".bootstrap-tree-table .treetable-bars .columns label").find("input[value='"+field+"']")
+                $input.prop("checked", '');
+            }
+        }
+        // ruoyi 解析数据,支持多层级访问
+        var getItemField = function (item, field) {
+            var value = item;
+
+            if (typeof field !== 'string' || item.hasOwnProperty(field)) {
+                return item[field];
+            }
+            var props = field.split('.');
+            for (var p in props) {
+                value = value && value[props[p]];
+            }
+            return value;
+        };
+        // ruoyi 发起对目标(target)函数的调用
+        var calculateObjectValue = function (self, name, args, defaultValue) {
+            var func = name;
+
+            if (typeof name === 'string') {
+                var names = name.split('.');
+
+                if (names.length > 1) {
+                    func = window;
+                    $.each(names, function (i, f) {
+                        func = func[f];
+                    });
+                } else {
+                    func = window[name];
+                }
+            }
+            if (typeof func === 'object') {
+                return func;
+            }
+            if (typeof func === 'function') {
+                return func.apply(self, args);
+            }
+            if (!func && typeof name === 'string' && sprintf.apply(this, [name].concat(args))) {
+                return sprintf.apply(this, [name].concat(args));
+            }
+            return defaultValue;
+        };
+        // 初始化
+        init();
+        return target;
+    };
+
+    // 组件方法封装........
+    $.fn.bootstrapTreeTable.methods = {
+        // 为了兼容bootstrap-table的写法,统一返回数组,这里返回了表格显示列的数据
+        getSelections: function(target, data) {
+            // 所有被选中的记录input
+            var _ipt = target.find("tbody").find("tr").find("input[name='select_item']:checked");
+            var chk_value = [];
+            // 如果是radio
+            if (_ipt.attr("type") == "radio") {
+                var _data = target.data_obj["id_" + _ipt.val()];
+                chk_value.push(_data);
+            } else {
+                _ipt.each(function(_i, _item) {
+                    var _data = target.data_obj["id_" + $(_item).val()];
+                    chk_value.push(_data);
+                });
+            }
+            return chk_value;
+        },
+        // 刷新记录
+        refresh: function(target, parms) {
+            if (parms) {
+                target.refresh(parms);
+            } else {
+                target.refresh();
+            }
+        },
+        // 添加数据到表格
+        appendData: function(target, data) {
+            if (data) {
+                target.appendData(data);
+            }
+        },
+        // 展开/折叠指定的行
+        toggleRow: function(target, id) {
+            target.toggleRow(id);
+        },
+        // 展开指定的行
+        expandRow: function(target, id) {
+            target.expandRow(id);
+        },
+        // 折叠 指定的行
+        collapseRow: function(target, id) {
+            target.collapseRow(id);
+        },
+        // 展开所有的行
+        expandAll: function(target) {
+            target.expandAll();
+        },
+        // 折叠所有的行
+        collapseAll: function(target) {
+            target.collapseAll();
+        },
+        // 显示指定列
+        showColumn: function(target,field) {
+            target.showColumn(field,true);
+        },
+        // 隐藏指定列
+        hideColumn: function(target,field) {
+            target.hideColumn(field,true);
+        }
+        // 组件的其他方法也可以进行类似封装........
+    };
+
+    $.fn.bootstrapTreeTable.defaults = {
+        code: 'code',              // 选取记录返回的值,用于设置父子关系
+        parentCode: 'parentCode',  // 用于设置父子关系
+        rootIdValue: null,         // 设置根节点id值----可指定根节点,默认为null,"",0,"0"
+        data: null,                // 构造table的数据集合
+        type: "GET",               // 请求数据的ajax类型
+        url: null,                 // 请求数据的ajax的url
+        ajaxParams: {},            // 请求数据的ajax的data属性
+        expandColumn: 0,           // 在哪一列上面显示展开按钮
+        expandAll: false,          // 是否全部展开
+        expandFirst: true,         // 是否默认第一级展开--expandAll为false时生效
+        striped: false,            // 是否各行渐变色
+        bordered: true,            // 是否显示边框
+        hover: true,               // 是否鼠标悬停
+        condensed: false,          // 是否紧缩表格
+        columns: [],               // 列
+        toolbar: null,             // 顶部工具条
+        height: 0,                 // 表格高度
+        showTitle: true,           // 是否采用title属性显示字段内容(被formatter格式化的字段不会显示)
+        showSearch: true,          // 是否显示检索信息
+        showColumns: true,         // 是否显示内容列下拉框
+        showRefresh: true,         // 是否显示刷新按钮
+        expanderExpandedClass: 'glyphicon glyphicon-chevron-down', // 展开的按钮的图标
+        expanderCollapsedClass: 'glyphicon glyphicon-chevron-right', // 缩起的按钮的图标
+        responseHandler: function(res) {
+            return false;
+        },
+        onLoadSuccess: function(res) {
+            return false;
+        }
+    };
+})(jQuery);

ファイルの差分が大きいため隠しています
+ 2 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/layui/lay/modules/laydate.js


ファイルの差分が大きいため隠しています
+ 2 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/layui/layui.js


ファイルの差分が大きいため隠しています
+ 52 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/report/echarts/echarts-all.js


ファイルの差分が大きいため隠しています
+ 7 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/select2/select2-bootstrap.css


+ 0 - 0
ruoyi-admin/src/main/resources/static/ajax/libs/staps/jquery.steps.css


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません