yangdamao пре 5 дана
родитељ
комит
6744bf294e
96 измењених фајлова са 6204 додато и 0 уклоњено
  1. 57 0
      src/main/java/com/zhongzheng/bo/ChannelBo.java
  2. 19 0
      src/main/java/com/zhongzheng/bo/ChannelDeleteBo.java
  3. 60 0
      src/main/java/com/zhongzheng/bo/ChannelEditBo.java
  4. 38 0
      src/main/java/com/zhongzheng/bo/ChannelQueryBo.java
  5. 21 0
      src/main/java/com/zhongzheng/bo/InvoiceUploadBo.java
  6. 19 0
      src/main/java/com/zhongzheng/bo/OrderAttachmentBo.java
  7. 38 0
      src/main/java/com/zhongzheng/bo/OrderCheckBo.java
  8. 18 0
      src/main/java/com/zhongzheng/bo/OrderEditBo.java
  9. 62 0
      src/main/java/com/zhongzheng/bo/OrderInvoiceCheckBo.java
  10. 31 0
      src/main/java/com/zhongzheng/bo/OrderPersonBo.java
  11. 55 0
      src/main/java/com/zhongzheng/bo/OrderQueryBo.java
  12. 14 0
      src/main/java/com/zhongzheng/bo/OrderRevocationBo.java
  13. 24 0
      src/main/java/com/zhongzheng/bo/OrderSettlementBo.java
  14. 45 0
      src/main/java/com/zhongzheng/bo/OrderSettlementDetailBo.java
  15. 84 0
      src/main/java/com/zhongzheng/bo/OrderSubmitBo.java
  16. 22 0
      src/main/java/com/zhongzheng/bo/OrderUploadAttachBo.java
  17. 38 0
      src/main/java/com/zhongzheng/bo/RefundApplyBo.java
  18. 52 0
      src/main/java/com/zhongzheng/bo/RelevanceGoodsBo.java
  19. 25 0
      src/main/java/com/zhongzheng/bo/RelevanceQueryBo.java
  20. 22 0
      src/main/java/com/zhongzheng/bo/UserChannelBo.java
  21. 17 0
      src/main/java/com/zhongzheng/bo/UserFiltrationBo.java
  22. 57 0
      src/main/java/com/zhongzheng/bo/UserImportAddBo.java
  23. 1 0
      src/main/java/com/zhongzheng/common/config/SecurityConfig.java
  24. 76 0
      src/main/java/com/zhongzheng/common/config/WxSmallConfig.java
  25. 13 0
      src/main/java/com/zhongzheng/common/util/TelPhoneUtils.java
  26. 42 0
      src/main/java/com/zhongzheng/common/util/wx/IWXPayDomain.java
  27. 690 0
      src/main/java/com/zhongzheng/common/util/wx/WXPay.java
  28. 103 0
      src/main/java/com/zhongzheng/common/util/wx/WXPayConfig.java
  29. 59 0
      src/main/java/com/zhongzheng/common/util/wx/WXPayConstants.java
  30. 265 0
      src/main/java/com/zhongzheng/common/util/wx/WXPayReport.java
  31. 259 0
      src/main/java/com/zhongzheng/common/util/wx/WXPayRequest.java
  32. 240 0
      src/main/java/com/zhongzheng/common/util/wx/WXPayUtil.java
  33. 30 0
      src/main/java/com/zhongzheng/common/util/wx/WXPayXmlUtil.java
  34. 90 0
      src/main/java/com/zhongzheng/controller/ChannelController.java
  35. 48 0
      src/main/java/com/zhongzheng/controller/CommonController.java
  36. 101 0
      src/main/java/com/zhongzheng/controller/FinanceOrderController.java
  37. 236 0
      src/main/java/com/zhongzheng/controller/OrderController.java
  38. 31 0
      src/main/java/com/zhongzheng/controller/wx/WxPayController.java
  39. 56 0
      src/main/java/com/zhongzheng/domian/Channel.java
  40. 75 0
      src/main/java/com/zhongzheng/domian/Order.java
  41. 34 0
      src/main/java/com/zhongzheng/domian/OrderAttachment.java
  42. 77 0
      src/main/java/com/zhongzheng/domian/OrderInvoice.java
  43. 60 0
      src/main/java/com/zhongzheng/domian/OrderPerson.java
  44. 66 0
      src/main/java/com/zhongzheng/domian/OrderRefund.java
  45. 46 0
      src/main/java/com/zhongzheng/domian/TenantRelevance.java
  46. 38 0
      src/main/java/com/zhongzheng/domian/UserChannel.java
  47. 21 0
      src/main/java/com/zhongzheng/mapper/ChannelMapper.java
  48. 21 0
      src/main/java/com/zhongzheng/mapper/OrderAttachmentMapper.java
  49. 18 0
      src/main/java/com/zhongzheng/mapper/OrderInvoiceMapper.java
  50. 35 0
      src/main/java/com/zhongzheng/mapper/OrderMapper.java
  51. 20 0
      src/main/java/com/zhongzheng/mapper/OrderPersonMapper.java
  52. 13 0
      src/main/java/com/zhongzheng/mapper/OrderRefundMapper.java
  53. 15 0
      src/main/java/com/zhongzheng/mapper/UserChannelMapper.java
  54. 30 0
      src/main/java/com/zhongzheng/service/IChannelService.java
  55. 11 0
      src/main/java/com/zhongzheng/service/IOrderAttachmentService.java
  56. 10 0
      src/main/java/com/zhongzheng/service/IOrderInvoiceService.java
  57. 11 0
      src/main/java/com/zhongzheng/service/IOrderPersonService.java
  58. 9 0
      src/main/java/com/zhongzheng/service/IOrderRefundService.java
  59. 49 0
      src/main/java/com/zhongzheng/service/IOrderService.java
  60. 7 0
      src/main/java/com/zhongzheng/service/ISysPersonService.java
  61. 12 0
      src/main/java/com/zhongzheng/service/IUserChannelService.java
  62. 13 0
      src/main/java/com/zhongzheng/service/IWxPayService.java
  63. 158 0
      src/main/java/com/zhongzheng/service/impl/ChannelServiceImpl.java
  64. 22 0
      src/main/java/com/zhongzheng/service/impl/OrderAttachmentServiceImpl.java
  65. 16 0
      src/main/java/com/zhongzheng/service/impl/OrderInvoiceServiceImpl.java
  66. 19 0
      src/main/java/com/zhongzheng/service/impl/OrderPersonServiceImpl.java
  67. 25 0
      src/main/java/com/zhongzheng/service/impl/OrderRefundServiceImpl.java
  68. 804 0
      src/main/java/com/zhongzheng/service/impl/OrderServiceImpl.java
  69. 137 0
      src/main/java/com/zhongzheng/service/impl/SysPersonServiceImpl.java
  70. 20 0
      src/main/java/com/zhongzheng/service/impl/UserChannelServiceImpl.java
  71. 176 0
      src/main/java/com/zhongzheng/service/impl/WxPayServiceImpl.java
  72. 59 0
      src/main/java/com/zhongzheng/vo/ChannelDetailVo.java
  73. 27 0
      src/main/java/com/zhongzheng/vo/OrderAttachmentVo.java
  74. 82 0
      src/main/java/com/zhongzheng/vo/OrderDetailVo.java
  75. 66 0
      src/main/java/com/zhongzheng/vo/OrderInvoiceVo.java
  76. 74 0
      src/main/java/com/zhongzheng/vo/OrderListVo.java
  77. 71 0
      src/main/java/com/zhongzheng/vo/OrderPersonVo.java
  78. 109 0
      src/main/java/com/zhongzheng/vo/OrderRefundDetailVo.java
  79. 38 0
      src/main/java/com/zhongzheng/vo/OrderSettlementVo.java
  80. 20 0
      src/main/java/com/zhongzheng/vo/UserChannelVo.java
  81. 0 0
      src/main/java/com/zhongzheng/vo/relevance/BusinessVo.java
  82. 0 0
      src/main/java/com/zhongzheng/vo/relevance/ConfigGoodsDetailVo.java
  83. 0 0
      src/main/java/com/zhongzheng/vo/relevance/ConfigGoodsVo.java
  84. 38 0
      src/main/java/com/zhongzheng/vo/relevance/EducationTypeVo.java
  85. 0 0
      src/main/java/com/zhongzheng/vo/relevance/GoodsBagVo.java
  86. 0 0
      src/main/java/com/zhongzheng/vo/relevance/GoodsBusinessVo.java
  87. 67 0
      src/main/java/com/zhongzheng/vo/relevance/GoodsVo.java
  88. 21 0
      src/main/java/com/zhongzheng/vo/relevance/RelevanceInfoVo.java
  89. 0 0
      src/main/java/com/zhongzheng/vo/relevance/SubjectMarketingVo.java
  90. 32 0
      src/main/java/com/zhongzheng/vo/relevance/SubjectVo.java
  91. 33 0
      src/main/resources/mapper/ChannelMapper.xml
  92. 11 0
      src/main/resources/mapper/OrderAttachmentMapper.xml
  93. 10 0
      src/main/resources/mapper/OrderInvoiceMapper.xml
  94. 200 0
      src/main/resources/mapper/OrderMapper.xml
  95. 10 0
      src/main/resources/mapper/OrderPersonMapper.xml
  96. 10 0
      src/main/resources/mapper/OrderRefundMapper.xml

+ 57 - 0
src/main/java/com/zhongzheng/bo/ChannelBo.java

@@ -0,0 +1,57 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("渠道新增参数")
+public class ChannelBo implements Serializable {
+
+    /** 渠道名称 */
+    @ApiModelProperty("渠道名称")
+    private String name;
+    /** 教育类型名称 */
+    @ApiModelProperty("教育类型名称")
+    private String eduName;
+    /** 业务层次名称 */
+    @ApiModelProperty("业务层次名称")
+    private String businessName;
+    /** 渠道内容 */
+    @ApiModelProperty("渠道内容")
+    private String content;
+    /** 渠道内容 */
+    @ApiModelProperty("封面地址")
+    private String coverUrl;
+    /** 单科价格 */
+    @ApiModelProperty("单科价格")
+    private BigDecimal price;
+    /** 活动价格 */
+    @ApiModelProperty("活动价格")
+    private BigDecimal activityPrice;
+    /** 渠道类型:1,默认类型 2满减 3特价活动 4商品礼包 */
+    @ApiModelProperty("渠道类型:1,默认类型 2满减 3特价活动 4商品礼包")
+    private Integer type;
+    /** 满额 */
+    @ApiModelProperty("满额")
+    private BigDecimal satisfyPrice;
+    /** 减额 */
+    @ApiModelProperty("减额")
+    private BigDecimal reducePrice;
+    /** 备注 */
+    @ApiModelProperty("备注")
+    private String remark;
+    /** 活动开始时间 */
+    @ApiModelProperty("活动开始时间")
+    private Long activityStart;
+    /** 活动结束时间 */
+    @ApiModelProperty("活动结束时间")
+    private Long activityEnd;
+    /** 1有效 0无效 */
+    @ApiModelProperty(" 1有效 0无效")
+    private Integer status;
+
+}

+ 19 - 0
src/main/java/com/zhongzheng/bo/ChannelDeleteBo.java

@@ -0,0 +1,19 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.stream.LongStream;
+
+@Data
+@ApiModel("渠道新增参数")
+public class ChannelDeleteBo implements Serializable {
+
+    @ApiModelProperty("渠道ID")
+    private List<Long> channelIds;
+
+}

+ 60 - 0
src/main/java/com/zhongzheng/bo/ChannelEditBo.java

@@ -0,0 +1,60 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("渠道新增参数")
+public class ChannelEditBo implements Serializable {
+
+    @ApiModelProperty("渠道ID")
+    private Long channelId;
+
+    /** 渠道名称 */
+    @ApiModelProperty("渠道名称")
+    private String name;
+    /** 教育类型名称 */
+    @ApiModelProperty("教育类型名称")
+    private String eduName;
+    /** 业务层次名称 */
+    @ApiModelProperty("业务层次名称")
+    private String businessName;
+    /** 渠道内容 */
+    @ApiModelProperty("渠道内容")
+    private String content;
+    /** 渠道内容 */
+    @ApiModelProperty("封面地址")
+    private String coverUrl;
+    /** 单科价格 */
+    @ApiModelProperty("单科价格")
+    private BigDecimal price;
+    /** 活动价格 */
+    @ApiModelProperty("活动价格")
+    private BigDecimal activityPrice;
+    /** 渠道类型:1,默认类型 2满减 3特价活动 4商品礼包 */
+    @ApiModelProperty("渠道类型:1,默认类型 2满减 3特价活动 4商品礼包")
+    private Integer type;
+    /** 满额 */
+    @ApiModelProperty("满额")
+    private BigDecimal satisfyPrice;
+    /** 减额 */
+    @ApiModelProperty("减额")
+    private BigDecimal reducePrice;
+    /** 备注 */
+    @ApiModelProperty("备注")
+    private String remark;
+    /** 活动开始时间 */
+    @ApiModelProperty("活动开始时间")
+    private Long activityStart;
+    /** 活动结束时间 */
+    @ApiModelProperty("活动结束时间")
+    private Long activityEnd;
+    /** 1有效 0无效 */
+    @ApiModelProperty(" 1有效 0无效")
+    private Integer status;
+
+}

+ 38 - 0
src/main/java/com/zhongzheng/bo/ChannelQueryBo.java

@@ -0,0 +1,38 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("渠道新增参数")
+public class ChannelQueryBo implements Serializable {
+
+    @ApiModelProperty("教育类型名称")
+    private String eduName;
+
+    @ApiModelProperty("业务层次名称")
+    private String businessName;
+
+    @ApiModelProperty("渠道名称")
+    private String name;
+
+    @ApiModelProperty("账号ID")
+    private Long userId;
+
+    @ApiModelProperty("当前页数")
+    private Integer pageNum;
+
+    @ApiModelProperty("分页大小")
+    private Integer pageSize;
+
+    @ApiModelProperty("渠道类型:1,默认类型 2满减 3特价活动")
+    private Integer type;
+
+    @ApiModelProperty("1有效 0无效")
+    private Integer status;
+
+}

+ 21 - 0
src/main/java/com/zhongzheng/bo/InvoiceUploadBo.java

@@ -0,0 +1,21 @@
+package com.zhongzheng.bo;
+
+import com.zhongzheng.vo.OrderPersonVo;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class InvoiceUploadBo implements Serializable {
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("id")
+    private Long invoiceId;
+
+    @ApiModelProperty("发票地址集合")
+    private List<String> invoiceUrl;
+}

+ 19 - 0
src/main/java/com/zhongzheng/bo/OrderAttachmentBo.java

@@ -0,0 +1,19 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel("订单审核")
+public class OrderAttachmentBo implements Serializable {
+
+    @ApiModelProperty("附件地址")
+    private String attachmentUrl;
+
+    @ApiModelProperty("类型:1转账凭证,2附件")
+    private Integer attachmentType;
+
+}

+ 38 - 0
src/main/java/com/zhongzheng/bo/OrderCheckBo.java

@@ -0,0 +1,38 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel("订单审核")
+public class OrderCheckBo implements Serializable {
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("订单审核状态:0待审核,1已通过,2未通过")
+    private Integer checkStatus;
+
+    @ApiModelProperty("发票ID")
+    private Long invoiceId;
+
+    @ApiModelProperty("发票审核状态:1待审核 2驳回 3通过")
+    private Integer invoiceStatus;
+
+    @ApiModelProperty("退款ID")
+    private Long refundId;
+
+    @ApiModelProperty("退款审核状态 0 待审核 1 通过 2未通过")
+    private Integer refundStatus;
+
+    @ApiModelProperty("退款审核原因")
+    private String periodReason;
+
+    @ApiModelProperty("用户ID")
+    private Long userId;
+
+    private Boolean isAdmin;
+}

+ 18 - 0
src/main/java/com/zhongzheng/bo/OrderEditBo.java

@@ -0,0 +1,18 @@
+package com.zhongzheng.bo;
+
+import com.zhongzheng.vo.OrderPersonVo;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class OrderEditBo implements Serializable {
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("订单学员集合")
+    private List<OrderPersonVo> orderPersonList;
+}

+ 62 - 0
src/main/java/com/zhongzheng/bo/OrderInvoiceCheckBo.java

@@ -0,0 +1,62 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("订单审核")
+public class OrderInvoiceCheckBo implements Serializable {
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("发票金额")
+    private BigDecimal amount;
+
+    @ApiModelProperty("银行账号")
+    private String bankAccount;
+
+    @ApiModelProperty("开户银行")
+    private String bankName;
+
+    @ApiModelProperty("公司地址")
+    private String companyAddress;
+
+    @ApiModelProperty("邮箱")
+    private String email;
+
+    @ApiModelProperty("发票抬头")
+    private String invoiceTitle;
+
+    @ApiModelProperty("开票备注")
+    private String openRemark;
+
+    @ApiModelProperty("审核状态 1待审核 2驳回 3通过")
+    private Integer periodStatus;
+
+    @ApiModelProperty("电话号码")
+    private String phone;
+
+    @ApiModelProperty("收件地址")
+    private String receivingAddress;
+
+    @ApiModelProperty("收件人姓名")
+    private String receivingName;
+
+    @ApiModelProperty("收件人手机号码")
+    private String receivingTel;
+
+    @ApiModelProperty("主体 1个人 2企业")
+    private Integer subject;
+
+    @ApiModelProperty("纳税登记号")
+    private String taxRegistryNumber;
+
+    @ApiModelProperty("类型 1普通发票 2增值税专用发票")
+    private Integer type;
+
+}

+ 31 - 0
src/main/java/com/zhongzheng/bo/OrderPersonBo.java

@@ -0,0 +1,31 @@
+package com.zhongzheng.bo;
+
+import com.zhongzheng.vo.OrderPersonVo;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class OrderPersonBo implements Serializable {
+
+    @ApiModelProperty("公司ID")
+    private Long companyId;
+
+    @ApiModelProperty("学历")
+    private String eduLevel;
+
+    @ApiModelProperty("身份证号")
+    private String idCard;
+
+    @ApiModelProperty("Ⓥ姓名")
+    private String realname;
+
+    @ApiModelProperty("性别")
+    private String sex;
+
+    @ApiModelProperty("手机号")
+    private String telphone;
+
+}

+ 55 - 0
src/main/java/com/zhongzheng/bo/OrderQueryBo.java

@@ -0,0 +1,55 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("渠道新增参数")
+public class OrderQueryBo implements Serializable {
+
+    @ApiModelProperty("教育类型名称")
+    private String eduName;
+
+    @ApiModelProperty("渠道名称")
+    private String channelName;
+
+    @ApiModelProperty("审核状态:0待审核,1已通过,2未通过")
+    private Integer checkStatus;
+
+    @ApiModelProperty("公司名称")
+    private String companyName;
+
+    @ApiModelProperty("业务层次名称")
+    private String businessName;
+
+    @ApiModelProperty("结束时间")
+    private Long endTime;
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("订单状态 0待支付,1已支付 4完全退款")
+    private Integer orderStatus;
+
+    @ApiModelProperty("订单类型 :1,对公 2,微信 3,线下")
+    private Integer orderType;
+
+    @ApiModelProperty("开始时间")
+    private Long startTime;
+
+    @ApiModelProperty("用户ID")
+    private Long userId;
+
+    @ApiModelProperty("用户名称")
+    private String userName;
+
+    @ApiModelProperty("当前页数")
+    private Integer pageNum;
+
+    @ApiModelProperty("分页大小")
+    private Integer pageSize;
+}

+ 14 - 0
src/main/java/com/zhongzheng/bo/OrderRevocationBo.java

@@ -0,0 +1,14 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class OrderRevocationBo implements Serializable {
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+}

+ 24 - 0
src/main/java/com/zhongzheng/bo/OrderSettlementBo.java

@@ -0,0 +1,24 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@ApiModel("渠道新增参数")
+public class OrderSettlementBo implements Serializable {
+
+    @ApiModelProperty("渠道ID")
+    private Long channelId;
+
+    @ApiModelProperty("公司ID")
+    private Long companyId;
+
+    @ApiModelProperty("订单员工集合")
+    private List<OrderSettlementDetailBo> settlementList;
+
+}

+ 45 - 0
src/main/java/com/zhongzheng/bo/OrderSettlementDetailBo.java

@@ -0,0 +1,45 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("渠道新增参数")
+public class OrderSettlementDetailBo implements Serializable {
+
+    @ApiModelProperty("所在公司")
+    private String companyName;
+
+    @ApiModelProperty("身份证号")
+    private String idCard;
+
+    @ApiModelProperty("学历")
+    private String eduLevel;
+
+    @ApiModelProperty("真实姓名")
+    private String realname;
+
+    @ApiModelProperty("性别")
+    private String sex;
+
+    @ApiModelProperty("科目")
+    private String subjectNames;
+
+    @ApiModelProperty("手机号码")
+    private String telphone;
+
+    @ApiModelProperty("年份")
+    private String year;
+
+    @ApiModelProperty("活动价格")
+    private BigDecimal activityPrice;
+
+    @ApiModelProperty("订单价格")
+    private BigDecimal orderPrice;
+
+
+}

+ 84 - 0
src/main/java/com/zhongzheng/bo/OrderSubmitBo.java

@@ -0,0 +1,84 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@ApiModel("渠道新增参数")
+public class OrderSubmitBo implements Serializable {
+
+    @ApiModelProperty("活动结束时间")
+    private Long activityEnd;
+
+    @ApiModelProperty("活动开始时间")
+    private Long activityStart;
+
+    @ApiModelProperty("活动价格")
+    private BigDecimal activityPrice;
+
+    @ApiModelProperty("业务层次名称")
+    private String businessName;
+
+    @ApiModelProperty("渠道ID")
+    private Long channelId;
+
+    @ApiModelProperty("公司ID")
+    private Long companyId;
+
+    @ApiModelProperty("渠道内容")
+    private String content;
+
+    @ApiModelProperty("教育类型名称")
+    private String eduName;
+
+    @ApiModelProperty("渠钜道名称")
+    private String name;
+
+    @ApiModelProperty("订单商品数量")
+    private Integer orderGoodsNum;
+
+    @ApiModelProperty("订单价格")
+    private BigDecimal orderPrice;
+
+    @ApiModelProperty("已付金额")
+    private BigDecimal orderReceived;
+
+    @ApiModelProperty("备注")
+    private String orderRemark;
+
+    @ApiModelProperty("订单类型:1,对公 2,微信 3,线下")
+    private Integer orderType;
+
+    @ApiModelProperty("订单人数")
+    private Integer orderUserNum;
+
+    @ApiModelProperty("订单原价")
+    private BigDecimal originalPrice;
+
+    @ApiModelProperty("单科价格")
+    private BigDecimal price;
+
+    @ApiModelProperty("减额")
+    private BigDecimal reducePrice;
+
+    @ApiModelProperty("满额")
+    private BigDecimal satisfyPrice;
+
+    @ApiModelProperty("渠道类型:1.默认类型 2满减 3特价活动 4商品礼包")
+    private Integer type;
+
+    @ApiModelProperty("账号ID")
+    private Long userId;
+
+    @ApiModelProperty("订单员工集合")
+    private List<OrderSettlementDetailBo> settlementList;
+
+    @ApiModelProperty("订单员工集合")
+    private List<OrderAttachmentBo> attachmentList;
+
+}

+ 22 - 0
src/main/java/com/zhongzheng/bo/OrderUploadAttachBo.java

@@ -0,0 +1,22 @@
+package com.zhongzheng.bo;
+
+import com.zhongzheng.domian.OrderAttachment;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@ApiModel("渠道新增参数")
+public class OrderUploadAttachBo implements Serializable {
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("附件集合")
+    private List<OrderAttachmentBo> attachmentList;
+
+}

+ 38 - 0
src/main/java/com/zhongzheng/bo/RefundApplyBo.java

@@ -0,0 +1,38 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class RefundApplyBo implements Serializable {
+
+    @ApiModelProperty("用户ID")
+    private Long userId;
+
+    @ApiModelProperty("公司ID")
+    private Long companyId;
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("申请退款原因")
+    private String applyReason;
+
+    @ApiModelProperty("审批状态 0 待审核 1 通过 2未通过")
+    private Integer periodStatus;
+
+    @ApiModelProperty("退款学员详情IDS,为空则是全部")
+    private String orderPersonIds;
+
+    @ApiModelProperty("收款人")
+    private String payee;
+
+    @ApiModelProperty("收款银行")
+    private String payeeBank;
+
+    @ApiModelProperty("收款账号")
+    private String payeeBankAccount;
+
+}

+ 52 - 0
src/main/java/com/zhongzheng/bo/RelevanceGoodsBo.java

@@ -0,0 +1,52 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel("商品列表请求参数")
+public class RelevanceGoodsBo implements Serializable {
+
+    @ApiModelProperty("关联机构ID")
+    private Long relevanceId;
+
+    @ApiModelProperty("教育类型ID")
+    private Long eduId;
+
+    @ApiModelProperty("项目类型ID")
+    private Long proId;
+
+    @ApiModelProperty("业务层ID")
+    private Long businessId;
+
+    @ApiModelProperty("专业ID")
+    private Long subjectId;
+
+    @ApiModelProperty("")
+    private Integer sortType = 1;
+
+    @ApiModelProperty("可售状态 : 1可售")
+    private Integer showStatus = 1;
+
+    @ApiModelProperty("上架状态 : 1上架")
+    private Integer goodsStatus = 1;
+
+    @ApiModelProperty("分页参数")
+    private Integer pageNum;
+
+    @ApiModelProperty("分页参数")
+    private Integer pageSize;
+
+    @ApiModelProperty("商品类型 : 1视频")
+    private Integer goodsType;
+
+    @ApiModelProperty("商品名称")
+    private String goodsName;
+
+    @ApiModelProperty("商品ID")
+    private Long goodsId;
+
+}

+ 25 - 0
src/main/java/com/zhongzheng/bo/RelevanceQueryBo.java

@@ -0,0 +1,25 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel("请求传参")
+public class RelevanceQueryBo implements Serializable {
+
+    @ApiModelProperty("关联机构ID")
+    private Long relevanceId;
+
+    @ApiModelProperty("教育类型ID")
+    private Long eduId;
+
+    @ApiModelProperty("项目类型ID")
+    private Long proId;
+
+    @ApiModelProperty("业务层ID")
+    private Long businessId;
+
+}

+ 22 - 0
src/main/java/com/zhongzheng/bo/UserChannelBo.java

@@ -0,0 +1,22 @@
+package com.zhongzheng.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.ListResourceBundle;
+
+@Data
+@ApiModel("关联渠道用户")
+public class UserChannelBo implements Serializable {
+
+    @ApiModelProperty("渠道ID")
+    private Long channelId;
+
+    @ApiModelProperty("渠道ID")
+    private List<Long> userIds;
+
+}

+ 17 - 0
src/main/java/com/zhongzheng/bo/UserFiltrationBo.java

@@ -0,0 +1,17 @@
+package com.zhongzheng.bo;
+
+
+import cn.afterturn.easypoi.excel.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class UserFiltrationBo implements Serializable {
+
+    @Excel(name = "手机号码")
+    private String userName;
+
+    @Excel(name = "公司名称")
+    private String company;
+}

+ 57 - 0
src/main/java/com/zhongzheng/bo/UserImportAddBo.java

@@ -0,0 +1,57 @@
+package com.zhongzheng.bo;
+
+
+import com.zhongzheng.common.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@ApiModel("关联渠道用户")
+public class UserImportAddBo implements Serializable {
+    /** 真实姓名 */
+    @Excel(name = "*学员姓名")
+    @ApiModelProperty("真实姓名")
+    private String realname;
+    /** 身份证号 */
+    @Excel(name = "学员身份证号码")
+    @ApiModelProperty("身份证号")
+    private String idCard;
+    /** 所在公司 */
+    @Excel(name = "所在公司")
+    @ApiModelProperty("所在公司")
+    private String companyName;
+    /** 手机号码 */
+    @Excel(name = "*手机号码")
+    @ApiModelProperty("手机号码")
+    private String telphone;
+
+    @Excel(name = "科目")
+    @ApiModelProperty("科目,多个,拼接")
+    private String subjectNames;
+
+    @Excel(name = "性别")
+    @ApiModelProperty("性别")
+    private String sex;
+
+    @Excel(name = "学历")
+    @ApiModelProperty("学历")
+    private String eduLevel;
+
+    @Excel(name = "年份")
+    @ApiModelProperty("年份")
+    private String year;
+
+    @Excel(name = "失败原因")
+    private String cause;
+
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    @ApiModelProperty("科目ID,多个,拼接")
+    private String subjectIds;
+
+}

+ 1 - 0
src/main/java/com/zhongzheng/common/config/SecurityConfig.java

@@ -119,6 +119,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/webjars/**").anonymous()
                 .antMatchers("/*/api-docs").anonymous()
                 .antMatchers("/druid/**").anonymous()
+                .antMatchers("/common/**").anonymous()
                 // Spring Boot Admin Server 的安全配置
                 .antMatchers(adminServerProperties.getContextPath()).anonymous()
                 .antMatchers(adminServerProperties.getContextPath() + "/**").anonymous()

+ 76 - 0
src/main/java/com/zhongzheng/common/config/WxSmallConfig.java

@@ -0,0 +1,76 @@
+package com.zhongzheng.common.config;
+
+import com.zhongzheng.common.util.wx.IWXPayDomain;
+import com.zhongzheng.common.util.wx.WXPayConfig;
+import com.zhongzheng.common.util.wx.WXPayConstants;
+
+import org.apache.commons.compress.utils.IOUtils;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+public class WxSmallConfig extends WXPayConfig {
+
+    private String appid;
+
+    private String mchid;
+
+    private String key;
+
+    private byte[] certData;
+
+    public WxSmallConfig(String appid,String mchid,String key)  {
+        this.appid = appid;
+        this.mchid = mchid;
+        this.key = key;
+    }
+    @Override
+    public String getAppID() {
+        return appid;
+    }
+
+    @Override
+    public String getMchID() {
+        return mchid;
+    }
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+
+
+    @Override
+    public IWXPayDomain getWXPayDomain() {
+
+        // 这个方法需要这样实现, 否则无法正常初始化WXPay
+        IWXPayDomain iwxPayDomain = new IWXPayDomain() {
+
+            public void report(String domain, long elapsedTimeMillis, Exception ex) {
+
+            }
+
+            public DomainInfo getDomain(WXPayConfig config) {
+                return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
+            }
+        };
+        return iwxPayDomain;
+
+    }
+
+    @Override
+    public InputStream getCertStream() {
+        try {
+            InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("config/"+getMchID()+"/apiclient_cert.p12");
+            this.certData = IOUtils.toByteArray(certStream);
+            certStream.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
+        return certBis;
+    }
+
+}

+ 13 - 0
src/main/java/com/zhongzheng/common/util/TelPhoneUtils.java

@@ -1,6 +1,7 @@
 package com.zhongzheng.common.util;
 
 import java.util.Random;
+import java.util.regex.Pattern;
 
 /**
  * 手机号码工具栏
@@ -70,4 +71,16 @@ public class TelPhoneUtils
         String phoneNumber = tel.replaceAll("(\\d{3})\\d{4}(\\d{4})","$1****$2");
         return phoneNumber;
     }
+
+    // 更精确的号段匹配(2024年最新)
+    private static final Pattern PHONE_PATTERN_EXACT =
+            Pattern.compile("^(13[0-9]|14[5-9]|15[0-3,5-9]|16[6]|17[0-8]|18[0-9]|19[1,3,5,8,9])\\d{8}$");
+
+    public static boolean isValidPhoneExact(String phone) {
+        if (phone == null || phone.isEmpty()) {
+            return false;
+        }
+        phone = phone.trim().replaceAll("\\s", "");
+        return PHONE_PATTERN_EXACT.matcher(phone).matches();
+    }
 }

+ 42 - 0
src/main/java/com/zhongzheng/common/util/wx/IWXPayDomain.java

@@ -0,0 +1,42 @@
+package com.zhongzheng.common.util.wx;
+
+/**
+ * 域名管理,实现主备域名自动切换
+ */
+public abstract interface IWXPayDomain {
+    /**
+     * 上报域名网络状况
+     * @param domain 域名。 比如:api.mch.weixin.qq.com
+     * @param elapsedTimeMillis 耗时
+     * @param ex 网络请求中出现的异常。
+     *           null表示没有异常
+     *           ConnectTimeoutException,表示建立网络连接异常
+     *           UnknownHostException, 表示dns解析异常
+     */
+    abstract void report(final String domain, long elapsedTimeMillis, final Exception ex);
+
+    /**
+     * 获取域名
+     * @param config 配置
+     * @return 域名
+     */
+    abstract DomainInfo getDomain(final WXPayConfig config);
+
+    static class DomainInfo{
+        public String domain;       //域名
+        public boolean primaryDomain;     //该域名是否为主域名。例如:api.mch.weixin.qq.com为主域名
+        public DomainInfo(String domain, boolean primaryDomain) {
+            this.domain = domain;
+            this.primaryDomain = primaryDomain;
+        }
+
+        @Override
+        public String toString() {
+            return "DomainInfo{" +
+                    "domain='" + domain + '\'' +
+                    ", primaryDomain=" + primaryDomain +
+                    '}';
+        }
+    }
+
+}

+ 690 - 0
src/main/java/com/zhongzheng/common/util/wx/WXPay.java

@@ -0,0 +1,690 @@
+package com.zhongzheng.common.util.wx;
+
+import com.zhongzheng.common.util.wx.WXPayConstants.SignType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class WXPay {
+
+    private WXPayConfig config;
+    private SignType signType;
+    private boolean autoReport;
+    private boolean useSandbox;
+    private String notifyUrl;
+    private WXPayRequest wxPayRequest;
+
+    public WXPay(final WXPayConfig config) throws Exception {
+        this(config, null, true, false);
+    }
+
+    public WXPay(final WXPayConfig config, final boolean autoReport) throws Exception {
+        this(config, null, autoReport, false);
+    }
+
+
+    public WXPay(final WXPayConfig config, final boolean autoReport, final boolean useSandbox) throws Exception{
+        this(config, null, autoReport, useSandbox);
+    }
+
+    public WXPay(final WXPayConfig config, final String notifyUrl) throws Exception {
+        this(config, notifyUrl, true, false);
+    }
+
+    public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport) throws Exception {
+        this(config, notifyUrl, autoReport, false);
+    }
+
+    public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception {
+        this.config = config;
+        this.notifyUrl = notifyUrl;
+        this.autoReport = autoReport;
+        this.useSandbox = useSandbox;
+        if (useSandbox) {
+            this.signType = SignType.MD5; // 沙箱环境
+        }
+        else {
+        //    this.signType = SignType.HMACSHA256;
+            this.signType = SignType.MD5;
+        }
+        this.wxPayRequest = new WXPayRequest(config);
+    }
+
+    private void checkWXPayConfig() throws Exception {
+        if (this.config == null) {
+            throw new Exception("config is null");
+        }
+        if (this.config.getAppID() == null || this.config.getAppID().trim().length() == 0) {
+            throw new Exception("appid in config is empty");
+        }
+        if (this.config.getMchID() == null || this.config.getMchID().trim().length() == 0) {
+            throw new Exception("appid in config is empty");
+        }
+        if (this.config.getCertStream() == null) {
+            throw new Exception("cert stream in config is empty");
+        }
+        if (this.config.getWXPayDomain() == null){
+            throw new Exception("config.getWXPayDomain() is null");
+        }
+
+        if (this.config.getHttpConnectTimeoutMs() < 10) {
+            throw new Exception("http connect timeout is too small");
+        }
+        if (this.config.getHttpReadTimeoutMs() < 10) {
+            throw new Exception("http read timeout is too small");
+        }
+
+    }
+
+    /**
+     * 向 Map 中添加 appid、mch_id、nonce_str、sign_type、sign <br>
+     * 该函数适用于商户适用于统一下单等接口,不适用于红包、代金券接口
+     *
+     * @param reqData
+     * @return
+     * @throws Exception
+     */
+    public Map<String, String> fillRequestData(Map<String, String> reqData) throws Exception {
+        reqData.put("appid", config.getAppID());
+        reqData.put("mch_id", config.getMchID());
+        reqData.put("nonce_str", WXPayUtil.generateNonceStr());
+        if (SignType.MD5.equals(this.signType)) {
+            reqData.put("sign_type", WXPayConstants.MD5);
+        }
+        else if (SignType.HMACSHA256.equals(this.signType)) {
+            reqData.put("sign_type", WXPayConstants.HMACSHA256);
+        }
+        reqData.put("sign", WXPayUtil.generateSignature(reqData, config.getKey(), this.signType));
+        return reqData;
+    }
+
+    /**
+     * 判断xml数据的sign是否有效,必须包含sign字段,否则返回false。
+     *
+     * @param reqData 向wxpay post的请求数据
+     * @return 签名是否有效
+     * @throws Exception
+     */
+    public boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {
+        // 返回数据的签名方式和请求中给定的签名方式是一致的
+        return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), this.signType);
+    }
+
+    /**
+     * 判断支付结果通知中的sign是否有效
+     *
+     * @param reqData 向wxpay post的请求数据
+     * @return 签名是否有效
+     * @throws Exception
+     */
+    public boolean isPayResultNotifySignatureValid(Map<String, String> reqData) throws Exception {
+        String signTypeInData = reqData.get(WXPayConstants.FIELD_SIGN_TYPE);
+        SignType signType;
+        if (signTypeInData == null) {
+            signType = SignType.MD5;
+        }
+        else {
+            signTypeInData = signTypeInData.trim();
+            if (signTypeInData.length() == 0) {
+                signType = SignType.MD5;
+            }
+            else if (WXPayConstants.MD5.equals(signTypeInData)) {
+                signType = SignType.MD5;
+            }
+            else if (WXPayConstants.HMACSHA256.equals(signTypeInData)) {
+                signType = SignType.HMACSHA256;
+            }
+            else {
+                throw new Exception(String.format("Unsupported sign_type: %s", signTypeInData));
+            }
+        }
+        return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), signType);
+    }
+
+
+    /**
+     * 不需要证书的请求
+     * @param urlSuffix String
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 超时时间,单位是毫秒
+     * @param readTimeoutMs 超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public String requestWithoutCert(String urlSuffix, Map<String, String> reqData,
+                                     int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String msgUUID = reqData.get("nonce_str");
+        String reqBody = WXPayUtil.mapToXml(reqData);
+
+        String resp = this.wxPayRequest.requestWithoutCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, autoReport);
+        return resp;
+    }
+
+
+    /**
+     * 需要证书的请求
+     * @param urlSuffix String
+     * @param reqData 向wxpay post的请求数据  Map
+     * @param connectTimeoutMs 超时时间,单位是毫秒
+     * @param readTimeoutMs 超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public String requestWithCert(String urlSuffix, Map<String, String> reqData,
+                                  int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String msgUUID= reqData.get("nonce_str");
+        String reqBody = WXPayUtil.mapToXml(reqData);
+
+        String resp = this.wxPayRequest.requestWithCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, this.autoReport);
+        return resp;
+    }
+
+    /**
+     * 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,验证签名。
+     * @param xmlStr API返回的XML格式数据
+     * @return Map类型数据
+     * @throws Exception
+     */
+    public Map<String, String> processResponseXml(String xmlStr) throws Exception {
+        String RETURN_CODE = "return_code";
+        String return_code;
+        Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
+        if (respData.containsKey(RETURN_CODE)) {
+            return_code = respData.get(RETURN_CODE);
+        }
+        else {
+            throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
+        }
+
+        if (return_code.equals(WXPayConstants.FAIL)) {
+            return respData;
+        }
+        else if (return_code.equals(WXPayConstants.SUCCESS)) {
+           if (this.isResponseSignatureValid(respData)) {
+               return respData;
+           }
+           else {
+               throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));
+           }
+        }
+        else {
+            throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
+        }
+    }
+
+    /**
+     * 作用:提交刷卡支付<br>
+     * 场景:刷卡支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> microPay(Map<String, String> reqData) throws Exception {
+        return this.microPay(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:提交刷卡支付<br>
+     * 场景:刷卡支付
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> microPay(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_MICROPAY_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.MICROPAY_URL_SUFFIX;
+        }
+        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return this.processResponseXml(respXml);
+    }
+
+    /**
+     * 提交刷卡支付,针对软POS,尽可能做成功
+     * 内置重试机制,最多60s
+     * @param reqData
+     * @return
+     * @throws Exception
+     */
+    public Map<String, String> microPayWithPos(Map<String, String> reqData) throws Exception {
+        return this.microPayWithPos(reqData, this.config.getHttpConnectTimeoutMs());
+    }
+
+    /**
+     * 提交刷卡支付,针对软POS,尽可能做成功
+     * 内置重试机制,最多60s
+     * @param reqData
+     * @param connectTimeoutMs
+     * @return
+     * @throws Exception
+     */
+    public Map<String, String> microPayWithPos(Map<String, String> reqData, int connectTimeoutMs) throws Exception {
+        int remainingTimeMs = 60*1000;
+        long startTimestampMs = 0;
+        Map<String, String> lastResult = null;
+        Exception lastException = null;
+
+        while (true) {
+            startTimestampMs = WXPayUtil.getCurrentTimestampMs();
+            int readTimeoutMs = remainingTimeMs - connectTimeoutMs;
+            if (readTimeoutMs > 1000) {
+                try {
+                    lastResult = this.microPay(reqData, connectTimeoutMs, readTimeoutMs);
+                    String returnCode = lastResult.get("return_code");
+                    if (returnCode.equals("SUCCESS")) {
+                        String resultCode = lastResult.get("result_code");
+                        String errCode = lastResult.get("err_code");
+                        if (resultCode.equals("SUCCESS")) {
+                            break;
+                        }
+                        else {
+                            // 看错误码,若支付结果未知,则重试提交刷卡支付
+                            if (errCode.equals("SYSTEMERROR") || errCode.equals("BANKERROR") || errCode.equals("USERPAYING")) {
+                                remainingTimeMs = remainingTimeMs - (int)(WXPayUtil.getCurrentTimestampMs() - startTimestampMs);
+                                if (remainingTimeMs <= 100) {
+                                    break;
+                                }
+                                else {
+                                    WXPayUtil.getLogger().info("microPayWithPos: try micropay again");
+                                    if (remainingTimeMs > 5*1000) {
+                                        Thread.sleep(5*1000);
+                                    }
+                                    else {
+                                        Thread.sleep(1*1000);
+                                    }
+                                    continue;
+                                }
+                            }
+                            else {
+                                break;
+                            }
+                        }
+                    }
+                    else {
+                        break;
+                    }
+                }
+                catch (Exception ex) {
+                    lastResult = null;
+                    lastException = ex;
+                }
+            }
+            else {
+                break;
+            }
+        }
+
+        if (lastResult == null) {
+            throw lastException;
+        }
+        else {
+            return lastResult;
+        }
+    }
+
+
+
+    /**
+     * 作用:统一下单<br>
+     * 场景:公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> unifiedOrder(Map<String, String> reqData) throws Exception {
+        return this.unifiedOrder(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:统一下单<br>
+     * 场景:公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> unifiedOrder(Map<String, String> reqData,  int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_UNIFIEDORDER_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.UNIFIEDORDER_URL_SUFFIX;
+        }
+        if(this.notifyUrl != null) {
+            reqData.put("notify_url", this.notifyUrl);
+        }
+        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return this.processResponseXml(respXml);
+    }
+
+
+    /**
+     * 作用:查询订单<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> orderQuery(Map<String, String> reqData) throws Exception {
+        return this.orderQuery(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:查询订单<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据 int
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> orderQuery(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_ORDERQUERY_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.ORDERQUERY_URL_SUFFIX;
+        }
+        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return this.processResponseXml(respXml);
+    }
+
+
+    /**
+     * 作用:撤销订单<br>
+     * 场景:刷卡支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> reverse(Map<String, String> reqData) throws Exception {
+        return this.reverse(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:撤销订单<br>
+     * 场景:刷卡支付<br>
+     * 其他:需要证书
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> reverse(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_REVERSE_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.REVERSE_URL_SUFFIX;
+        }
+        String respXml = this.requestWithCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return this.processResponseXml(respXml);
+    }
+
+
+    /**
+     * 作用:关闭订单<br>
+     * 场景:公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> closeOrder(Map<String, String> reqData) throws Exception {
+        return this.closeOrder(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:关闭订单<br>
+     * 场景:公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> closeOrder(Map<String, String> reqData,  int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_CLOSEORDER_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.CLOSEORDER_URL_SUFFIX;
+        }
+        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return this.processResponseXml(respXml);
+    }
+
+
+    /**
+     * 作用:申请退款<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> refund(Map<String, String> reqData) throws Exception {
+        return this.refund(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:申请退款<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付<br>
+     * 其他:需要证书
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> refund(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_REFUND_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.REFUND_URL_SUFFIX;
+        }
+        String respXml = this.requestWithCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return this.processResponseXml(respXml);
+    }
+
+
+    /**
+     * 作用:退款查询<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> refundQuery(Map<String, String> reqData) throws Exception {
+        return this.refundQuery(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:退款查询<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> refundQuery(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_REFUNDQUERY_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.REFUNDQUERY_URL_SUFFIX;
+        }
+        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return this.processResponseXml(respXml);
+    }
+
+
+    /**
+     * 作用:对账单下载(成功时返回对账单数据,失败时返回XML格式数据)<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> downloadBill(Map<String, String> reqData) throws Exception {
+        return this.downloadBill(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:对账单下载<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付<br>
+     * 其他:无论是否成功都返回Map。若成功,返回的Map中含有return_code、return_msg、data,
+     *      其中return_code为`SUCCESS`,data为对账单数据。
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return 经过封装的API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> downloadBill(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_DOWNLOADBILL_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.DOWNLOADBILL_URL_SUFFIX;
+        }
+        String respStr = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs).trim();
+        Map<String, String> ret;
+        // 出现错误,返回XML数据
+        if (respStr.indexOf("<") == 0) {
+            ret = WXPayUtil.xmlToMap(respStr);
+        }
+        else {
+            // 正常返回csv数据
+            ret = new HashMap<String, String>();
+            ret.put("return_code", WXPayConstants.SUCCESS);
+            ret.put("return_msg", "ok");
+            ret.put("data", respStr);
+        }
+        return ret;
+    }
+
+
+    /**
+     * 作用:交易保障<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> report(Map<String, String> reqData) throws Exception {
+        return this.report(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:交易保障<br>
+     * 场景:刷卡支付、公共号支付、扫码支付、APP支付
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> report(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_REPORT_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.REPORT_URL_SUFFIX;
+        }
+        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return WXPayUtil.xmlToMap(respXml);
+    }
+
+
+    /**
+     * 作用:转换短链接<br>
+     * 场景:刷卡支付、扫码支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> shortUrl(Map<String, String> reqData) throws Exception {
+        return this.shortUrl(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:转换短链接<br>
+     * 场景:刷卡支付、扫码支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> shortUrl(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_SHORTURL_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.SHORTURL_URL_SUFFIX;
+        }
+        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return this.processResponseXml(respXml);
+    }
+
+
+    /**
+     * 作用:授权码查询OPENID接口<br>
+     * 场景:刷卡支付
+     * @param reqData 向wxpay post的请求数据
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> authCodeToOpenid(Map<String, String> reqData) throws Exception {
+        return this.authCodeToOpenid(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
+    }
+
+
+    /**
+     * 作用:授权码查询OPENID接口<br>
+     * 场景:刷卡支付
+     * @param reqData 向wxpay post的请求数据
+     * @param connectTimeoutMs 连接超时时间,单位是毫秒
+     * @param readTimeoutMs 读超时时间,单位是毫秒
+     * @return API返回数据
+     * @throws Exception
+     */
+    public Map<String, String> authCodeToOpenid(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
+        String url;
+        if (this.useSandbox) {
+            url = WXPayConstants.SANDBOX_AUTHCODETOOPENID_URL_SUFFIX;
+        }
+        else {
+            url = WXPayConstants.AUTHCODETOOPENID_URL_SUFFIX;
+        }
+        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
+        return this.processResponseXml(respXml);
+    }
+
+
+} // end class

+ 103 - 0
src/main/java/com/zhongzheng/common/util/wx/WXPayConfig.java

@@ -0,0 +1,103 @@
+package com.zhongzheng.common.util.wx;
+
+import java.io.InputStream;
+
+public abstract class WXPayConfig {
+
+
+
+    /**
+     * 获取 App ID
+     *
+     * @return App ID
+     */
+    protected abstract String getAppID();
+
+
+    /**
+     * 获取 Mch ID
+     *
+     * @return Mch ID
+     */
+    protected abstract String getMchID();
+
+
+    /**
+     * 获取 API 密钥
+     *
+     * @return API密钥
+     */
+    protected abstract String getKey();
+
+
+    /**
+     * 获取商户证书内容
+     *
+     * @return 商户证书内容
+     */
+    protected abstract InputStream getCertStream();
+
+    /**
+     * HTTP(S) 连接超时时间,单位毫秒
+     *
+     * @return
+     */
+    public int getHttpConnectTimeoutMs() {
+        return 6*1000;
+    }
+
+    /**
+     * HTTP(S) 读数据超时时间,单位毫秒
+     *
+     * @return
+     */
+    public int getHttpReadTimeoutMs() {
+        return 8*1000;
+    }
+
+    /**
+     * 获取WXPayDomain, 用于多域名容灾自动切换
+     * @return
+     */
+    protected abstract IWXPayDomain getWXPayDomain();
+
+    /**
+     * 是否自动上报。
+     * 若要关闭自动上报,子类中实现该函数返回 false 即可。
+     *
+     * @return
+     */
+    public boolean shouldAutoReport() {
+        return true;
+    }
+
+    /**
+     * 进行健康上报的线程的数量
+     *
+     * @return
+     */
+    public int getReportWorkerNum() {
+        return 6;
+    }
+
+
+    /**
+     * 健康上报缓存消息的最大数量。会有线程去独立上报
+     * 粗略计算:加入一条消息200B,10000消息占用空间 2000 KB,约为2MB,可以接受
+     *
+     * @return
+     */
+    public int getReportQueueMaxSize() {
+        return 10000;
+    }
+
+    /**
+     * 批量上报,一次最多上报多个数据
+     *
+     * @return
+     */
+    public int getReportBatchSize() {
+        return 10;
+    }
+
+}

+ 59 - 0
src/main/java/com/zhongzheng/common/util/wx/WXPayConstants.java

@@ -0,0 +1,59 @@
+package com.zhongzheng.common.util.wx;
+
+import org.apache.http.client.HttpClient;
+
+/**
+ * 常量
+ */
+public class WXPayConstants {
+
+    public enum SignType {
+        MD5, HMACSHA256
+    }
+
+    public static final String DOMAIN_API = "api.mch.weixin.qq.com";
+    public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
+    public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
+    public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";
+
+
+    public static final String FAIL     = "FAIL";
+    public static final String SUCCESS  = "SUCCESS";
+    public static final String HMACSHA256 = "HMAC-SHA256";
+    public static final String MD5 = "MD5";
+
+    public static final String FIELD_SIGN = "sign";
+    public static final String FIELD_SIGN_TYPE = "sign_type";
+
+    public static final String WXPAYSDK_VERSION = "WXPaySDK/3.0.9";
+    public static final String USER_AGENT = WXPAYSDK_VERSION +
+            " (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +
+            ") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion();
+
+    public static final String MICROPAY_URL_SUFFIX     = "/pay/micropay";
+    public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
+    public static final String ORDERQUERY_URL_SUFFIX   = "/pay/orderquery";
+    public static final String REVERSE_URL_SUFFIX      = "/secapi/pay/reverse";
+    public static final String CLOSEORDER_URL_SUFFIX   = "/pay/closeorder";
+    public static final String REFUND_URL_SUFFIX       = "/secapi/pay/refund";
+    public static final String REFUNDQUERY_URL_SUFFIX  = "/pay/refundquery";
+    public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
+    public static final String REPORT_URL_SUFFIX       = "/payitil/report";
+    public static final String SHORTURL_URL_SUFFIX     = "/tools/shorturl";
+    public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid";
+
+    // sandbox
+    public static final String SANDBOX_MICROPAY_URL_SUFFIX     = "/sandboxnew/pay/micropay";
+    public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
+    public static final String SANDBOX_ORDERQUERY_URL_SUFFIX   = "/sandboxnew/pay/orderquery";
+    public static final String SANDBOX_REVERSE_URL_SUFFIX      = "/sandboxnew/secapi/pay/reverse";
+    public static final String SANDBOX_CLOSEORDER_URL_SUFFIX   = "/sandboxnew/pay/closeorder";
+    public static final String SANDBOX_REFUND_URL_SUFFIX       = "/sandboxnew/secapi/pay/refund";
+    public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX  = "/sandboxnew/pay/refundquery";
+    public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
+    public static final String SANDBOX_REPORT_URL_SUFFIX       = "/sandboxnew/payitil/report";
+    public static final String SANDBOX_SHORTURL_URL_SUFFIX     = "/sandboxnew/tools/shorturl";
+    public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
+
+}
+

+ 265 - 0
src/main/java/com/zhongzheng/common/util/wx/WXPayReport.java

@@ -0,0 +1,265 @@
+package com.zhongzheng.common.util.wx;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
+import org.apache.http.util.EntityUtils;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * 交易保障
+ */
+public class WXPayReport {
+
+    public static class ReportInfo {
+
+        /**
+         * 布尔变量使用int。0为false, 1为true。
+         */
+
+        // 基本信息
+        private String version = "v1";
+        private String sdk = WXPayConstants.WXPAYSDK_VERSION;
+        private String uuid;  // 交易的标识
+        private long timestamp;   // 上报时的时间戳,单位秒
+        private long elapsedTimeMillis; // 耗时,单位 毫秒
+
+        // 针对主域名
+        private String firstDomain;  // 第1次请求的域名
+        private boolean primaryDomain; //是否主域名
+        private int firstConnectTimeoutMillis;  // 第1次请求设置的连接超时时间,单位 毫秒
+        private int firstReadTimeoutMillis;  // 第1次请求设置的读写超时时间,单位 毫秒
+        private int firstHasDnsError;  // 第1次请求是否出现dns问题
+        private int firstHasConnectTimeout; // 第1次请求是否出现连接超时
+        private int firstHasReadTimeout; // 第1次请求是否出现连接超时
+
+        public ReportInfo(String uuid, long timestamp, long elapsedTimeMillis, String firstDomain, boolean primaryDomain, int firstConnectTimeoutMillis, int firstReadTimeoutMillis, boolean firstHasDnsError, boolean firstHasConnectTimeout, boolean firstHasReadTimeout) {
+            this.uuid = uuid;
+            this.timestamp = timestamp;
+            this.elapsedTimeMillis = elapsedTimeMillis;
+            this.firstDomain = firstDomain;
+            this.primaryDomain = primaryDomain;
+            this.firstConnectTimeoutMillis = firstConnectTimeoutMillis;
+            this.firstReadTimeoutMillis = firstReadTimeoutMillis;
+            this.firstHasDnsError = firstHasDnsError?1:0;
+            this.firstHasConnectTimeout = firstHasConnectTimeout?1:0;
+            this.firstHasReadTimeout = firstHasReadTimeout?1:0;
+         }
+
+        @Override
+        public String toString() {
+            return "ReportInfo{" +
+                    "version='" + version + '\'' +
+                    ", sdk='" + sdk + '\'' +
+                    ", uuid='" + uuid + '\'' +
+                    ", timestamp=" + timestamp +
+                    ", elapsedTimeMillis=" + elapsedTimeMillis +
+                    ", firstDomain='" + firstDomain + '\'' +
+                    ", primaryDomain=" + primaryDomain +
+                    ", firstConnectTimeoutMillis=" + firstConnectTimeoutMillis +
+                    ", firstReadTimeoutMillis=" + firstReadTimeoutMillis +
+                    ", firstHasDnsError=" + firstHasDnsError +
+                    ", firstHasConnectTimeout=" + firstHasConnectTimeout +
+                    ", firstHasReadTimeout=" + firstHasReadTimeout +
+                    '}';
+        }
+
+        /**
+         * 转换成 csv 格式
+         *
+         * @return
+         */
+        public String toLineString(String key) {
+            String separator = ",";
+            Object[] objects = new Object[] {
+                version, sdk, uuid, timestamp, elapsedTimeMillis,
+                    firstDomain, primaryDomain, firstConnectTimeoutMillis, firstReadTimeoutMillis,
+                    firstHasDnsError, firstHasConnectTimeout, firstHasReadTimeout
+            };
+            StringBuffer sb = new StringBuffer();
+            for(Object obj: objects) {
+                sb.append(obj).append(separator);
+            }
+            try {
+                String sign = WXPayUtil.HMACSHA256(sb.toString(), key);
+                sb.append(sign);
+                return sb.toString();
+            }
+            catch (Exception ex) {
+                return null;
+            }
+
+        }
+
+    }
+
+    private static final String REPORT_URL = "http://report.mch.weixin.qq.com/wxpay/report/default";
+    // private static final String REPORT_URL = "http://127.0.0.1:5000/test";
+
+
+    private static final int DEFAULT_CONNECT_TIMEOUT_MS = 6*1000;
+    private static final int DEFAULT_READ_TIMEOUT_MS = 8*1000;
+
+    private LinkedBlockingQueue<String> reportMsgQueue = null;
+    private WXPayConfig config;
+    private ExecutorService executorService;
+
+    private volatile static WXPayReport INSTANCE;
+
+    private WXPayReport(final WXPayConfig config) {
+        this.config = config;
+        reportMsgQueue = new LinkedBlockingQueue<String>(config.getReportQueueMaxSize());
+
+        // 添加处理线程
+        executorService = Executors.newFixedThreadPool(config.getReportWorkerNum(), new ThreadFactory() {
+            public Thread newThread(Runnable r) {
+                Thread t = Executors.defaultThreadFactory().newThread(r);
+                t.setDaemon(true);
+                return t;
+            }
+        });
+
+        if (config.shouldAutoReport()) {
+            WXPayUtil.getLogger().info("report worker num: {}", config.getReportWorkerNum());
+            for (int i = 0; i < config.getReportWorkerNum(); ++i) {
+                executorService.execute(new Runnable() {
+                    public void run() {
+                        while (true) {
+                            // 先用 take 获取数据
+                            try {
+                                StringBuffer sb = new StringBuffer();
+                                String firstMsg = reportMsgQueue.take();
+                                WXPayUtil.getLogger().info("get first report msg: {}", firstMsg);
+                                String msg = null;
+                                sb.append(firstMsg); //会阻塞至有消息
+                                int remainNum = config.getReportBatchSize() - 1;
+                                for (int j=0; j<remainNum; ++j) {
+                                    WXPayUtil.getLogger().info("try get remain report msg");
+                                    // msg = reportMsgQueue.poll();  // 不阻塞了
+                                    msg = reportMsgQueue.take();
+                                    WXPayUtil.getLogger().info("get remain report msg: {}", msg);
+                                    if (msg == null) {
+                                        break;
+                                    }
+                                    else {
+                                        sb.append("\n");
+                                        sb.append(msg);
+                                    }
+                                }
+                                // 上报
+                                WXPayReport.httpRequest(sb.toString(), DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_READ_TIMEOUT_MS);
+                            }
+                            catch (Exception ex) {
+                                WXPayUtil.getLogger().warn("report fail. reason: {}", ex.getMessage());
+                            }
+                        }
+                    }
+                });
+            }
+        }
+
+    }
+
+    /**
+     * 单例,双重校验,请在 JDK 1.5及更高版本中使用
+     *
+     * @param config
+     * @return
+     */
+    public static WXPayReport getInstance(WXPayConfig config) {
+        if (INSTANCE == null) {
+            synchronized (WXPayReport.class) {
+                if (INSTANCE == null) {
+                    INSTANCE = new WXPayReport(config);
+                }
+            }
+        }
+        return INSTANCE;
+    }
+
+    public void report(String uuid, long elapsedTimeMillis,
+                       String firstDomain, boolean primaryDomain, int firstConnectTimeoutMillis, int firstReadTimeoutMillis,
+                       boolean firstHasDnsError, boolean firstHasConnectTimeout, boolean firstHasReadTimeout) {
+        long currentTimestamp = WXPayUtil.getCurrentTimestamp();
+        ReportInfo reportInfo = new ReportInfo(uuid, currentTimestamp, elapsedTimeMillis,
+                firstDomain, primaryDomain, firstConnectTimeoutMillis, firstReadTimeoutMillis,
+                firstHasDnsError, firstHasConnectTimeout, firstHasReadTimeout);
+        String data = reportInfo.toLineString(config.getKey());
+        WXPayUtil.getLogger().info("report {}", data);
+        if (data != null) {
+            reportMsgQueue.offer(data);
+        }
+    }
+
+
+    @Deprecated
+    private void reportSync(final String data) throws Exception {
+        httpRequest(data, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_READ_TIMEOUT_MS);
+    }
+
+    @Deprecated
+    private void reportAsync(final String data) throws Exception {
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    httpRequest(data, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_READ_TIMEOUT_MS);
+                }
+                catch (Exception ex) {
+                    WXPayUtil.getLogger().warn("report fail. reason: {}", ex.getMessage());
+                }
+            }
+        }).start();
+    }
+
+    /**
+     * http 请求
+     * @param data
+     * @param connectTimeoutMs
+     * @param readTimeoutMs
+     * @return
+     * @throws Exception
+     */
+    private static String httpRequest(String data, int connectTimeoutMs, int readTimeoutMs) throws Exception{
+        BasicHttpClientConnectionManager connManager;
+        connManager = new BasicHttpClientConnectionManager(
+                RegistryBuilder.<ConnectionSocketFactory>create()
+                        .register("http", PlainConnectionSocketFactory.getSocketFactory())
+                        .register("https", SSLConnectionSocketFactory.getSocketFactory())
+                        .build(),
+                null,
+                null,
+                null
+        );
+        HttpClient httpClient = HttpClientBuilder.create()
+                .setConnectionManager(connManager)
+                .build();
+
+        HttpPost httpPost = new HttpPost(REPORT_URL);
+
+        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
+        httpPost.setConfig(requestConfig);
+
+        StringEntity postEntity = new StringEntity(data, "UTF-8");
+        httpPost.addHeader("Content-Type", "text/xml");
+        httpPost.addHeader("User-Agent", WXPayConstants.USER_AGENT);
+        httpPost.setEntity(postEntity);
+
+        HttpResponse httpResponse = httpClient.execute(httpPost);
+        HttpEntity httpEntity = httpResponse.getEntity();
+        return EntityUtils.toString(httpEntity, "UTF-8");
+    }
+
+}

+ 259 - 0
src/main/java/com/zhongzheng/common/util/wx/WXPayRequest.java

@@ -0,0 +1,259 @@
+package com.zhongzheng.common.util.wx;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
+import org.apache.http.util.EntityUtils;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import java.io.InputStream;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+
+import static com.zhongzheng.common.util.wx.WXPayConstants.USER_AGENT;
+
+
+public class WXPayRequest {
+    private WXPayConfig config;
+    public WXPayRequest(WXPayConfig config) throws Exception{
+
+        this.config = config;
+    }
+
+    /**
+     * 请求,只请求一次,不做重试
+     * @param domain
+     * @param urlSuffix
+     * @param uuid
+     * @param data
+     * @param connectTimeoutMs
+     * @param readTimeoutMs
+     * @param useCert 是否使用证书,针对退款、撤销等操作
+     * @return
+     * @throws Exception
+     */
+    private String requestOnce(final String domain, String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert) throws Exception {
+        BasicHttpClientConnectionManager connManager;
+
+        if (useCert) {
+            // 证书
+            char[] password = config.getMchID().toCharArray();
+            InputStream certStream = config.getCertStream();
+            KeyStore ks = KeyStore.getInstance("PKCS12");
+            ks.load(certStream, password);
+            // 实例化密钥库 & 初始化密钥工厂
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            kmf.init(ks, password);
+
+            // 创建 SSLContext
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
+
+            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
+                    sslContext,
+                    null,
+                    null,
+                    new DefaultHostnameVerifier());
+
+            connManager = new BasicHttpClientConnectionManager(
+                    RegistryBuilder.<ConnectionSocketFactory>create()
+                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
+                            .register("https", sslConnectionSocketFactory)
+                            .build(),
+                    null,
+                    null,
+                    null
+            );
+        }
+        else {
+            connManager = new BasicHttpClientConnectionManager(
+                    RegistryBuilder.<ConnectionSocketFactory>create()
+                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
+                            .register("https", SSLConnectionSocketFactory.getSocketFactory())
+                            .build(),
+                    null,
+                    null,
+                    null
+            );
+        }
+
+        HttpClient httpClient = HttpClientBuilder.create()
+                .setConnectionManager(connManager)
+                .build();
+
+        String url = "https://" + domain + urlSuffix;
+        HttpPost httpPost = new HttpPost(url);
+
+        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
+        httpPost.setConfig(requestConfig);
+
+        StringEntity postEntity = new StringEntity(data, "UTF-8");
+        httpPost.addHeader("Content-Type", "text/xml");
+        httpPost.addHeader("User-Agent", USER_AGENT + " " + config.getMchID());
+        httpPost.setEntity(postEntity);
+
+        HttpResponse httpResponse = httpClient.execute(httpPost);
+        HttpEntity httpEntity = httpResponse.getEntity();
+        return EntityUtils.toString(httpEntity, "UTF-8");
+
+    }
+
+
+    private String request(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert, boolean autoReport) throws Exception {
+        Exception exception = null;
+        long elapsedTimeMillis = 0;
+        long startTimestampMs = WXPayUtil.getCurrentTimestampMs();
+        boolean firstHasDnsErr = false;
+        boolean firstHasConnectTimeout = false;
+        boolean firstHasReadTimeout = false;
+        IWXPayDomain.DomainInfo domainInfo = config.getWXPayDomain().getDomain(config);
+        if(domainInfo == null){
+            throw new Exception("WXPayConfig.getWXPayDomain().getDomain() is empty or null");
+        }
+        try {
+            String result = requestOnce(domainInfo.domain, urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, useCert);
+            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
+            config.getWXPayDomain().report(domainInfo.domain, elapsedTimeMillis, null);
+            WXPayReport.getInstance(config).report(
+                    uuid,
+                    elapsedTimeMillis,
+                    domainInfo.domain,
+                    domainInfo.primaryDomain,
+                    connectTimeoutMs,
+                    readTimeoutMs,
+                    firstHasDnsErr,
+                    firstHasConnectTimeout,
+                    firstHasReadTimeout);
+            return result;
+        }
+        catch (UnknownHostException ex) {  // dns 解析错误,或域名不存在
+            exception = ex;
+            firstHasDnsErr = true;
+            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
+            WXPayUtil.getLogger().warn("UnknownHostException for domainInfo {}", domainInfo);
+            WXPayReport.getInstance(config).report(
+                    uuid,
+                    elapsedTimeMillis,
+                    domainInfo.domain,
+                    domainInfo.primaryDomain,
+                    connectTimeoutMs,
+                    readTimeoutMs,
+                    firstHasDnsErr,
+                    firstHasConnectTimeout,
+                    firstHasReadTimeout
+            );
+        }
+        catch (ConnectTimeoutException ex) {
+            exception = ex;
+            firstHasConnectTimeout = true;
+            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
+            WXPayUtil.getLogger().warn("connect timeout happened for domainInfo {}", domainInfo);
+            WXPayReport.getInstance(config).report(
+                    uuid,
+                    elapsedTimeMillis,
+                    domainInfo.domain,
+                    domainInfo.primaryDomain,
+                    connectTimeoutMs,
+                    readTimeoutMs,
+                    firstHasDnsErr,
+                    firstHasConnectTimeout,
+                    firstHasReadTimeout
+            );
+        }
+        catch (SocketTimeoutException ex) {
+            exception = ex;
+            firstHasReadTimeout = true;
+            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
+            WXPayUtil.getLogger().warn("timeout happened for domainInfo {}", domainInfo);
+            WXPayReport.getInstance(config).report(
+                    uuid,
+                    elapsedTimeMillis,
+                    domainInfo.domain,
+                    domainInfo.primaryDomain,
+                    connectTimeoutMs,
+                    readTimeoutMs,
+                    firstHasDnsErr,
+                    firstHasConnectTimeout,
+                    firstHasReadTimeout);
+        }
+        catch (Exception ex) {
+            exception = ex;
+            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
+            WXPayReport.getInstance(config).report(
+                    uuid,
+                    elapsedTimeMillis,
+                    domainInfo.domain,
+                    domainInfo.primaryDomain,
+                    connectTimeoutMs,
+                    readTimeoutMs,
+                    firstHasDnsErr,
+                    firstHasConnectTimeout,
+                    firstHasReadTimeout);
+        }
+        config.getWXPayDomain().report(domainInfo.domain, elapsedTimeMillis, exception);
+        throw exception;
+    }
+
+
+    /**
+     * 可重试的,非双向认证的请求
+     * @param urlSuffix
+     * @param uuid
+     * @param data
+     * @return
+     */
+    public String requestWithoutCert(String urlSuffix, String uuid, String data, boolean autoReport) throws Exception {
+        return this.request(urlSuffix, uuid, data, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs(), false, autoReport);
+    }
+
+    /**
+     * 可重试的,非双向认证的请求
+     * @param urlSuffix
+     * @param uuid
+     * @param data
+     * @param connectTimeoutMs
+     * @param readTimeoutMs
+     * @return
+     */
+    public String requestWithoutCert(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs,  boolean autoReport) throws Exception {
+        return this.request(urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, false, autoReport);
+    }
+
+    /**
+     * 可重试的,双向认证的请求
+     * @param urlSuffix
+     * @param uuid
+     * @param data
+     * @return
+     */
+    public String requestWithCert(String urlSuffix, String uuid, String data, boolean autoReport) throws Exception {
+        return this.request(urlSuffix, uuid, data, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs(), true, autoReport);
+    }
+
+    /**
+     * 可重试的,双向认证的请求
+     * @param urlSuffix
+     * @param uuid
+     * @param data
+     * @param connectTimeoutMs
+     * @param readTimeoutMs
+     * @return
+     */
+    public String requestWithCert(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean autoReport) throws Exception {
+        return this.request(urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, true, autoReport);
+    }
+}

+ 240 - 0
src/main/java/com/zhongzheng/common/util/wx/WXPayUtil.java

@@ -0,0 +1,240 @@
+package com.zhongzheng.common.util.wx;
+
+import com.zhongzheng.common.util.wx.WXPayConstants.SignType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.*;
+
+
+public class WXPayUtil {
+
+    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+    private static final Random RANDOM = new SecureRandom();
+
+    /**
+     * XML格式字符串转换为Map
+     *
+     * @param strXML XML字符串
+     * @return XML数据转换后的Map
+     * @throws Exception
+     */
+    public static Map<String, String> xmlToMap(String strXML) throws Exception {
+        try {
+            Map<String, String> data = new HashMap<String, String>();
+            DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
+            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
+            org.w3c.dom.Document doc = documentBuilder.parse(stream);
+            doc.getDocumentElement().normalize();
+            NodeList nodeList = doc.getDocumentElement().getChildNodes();
+            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
+                Node node = nodeList.item(idx);
+                if (node.getNodeType() == Node.ELEMENT_NODE) {
+                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
+                    data.put(element.getNodeName(), element.getTextContent());
+                }
+            }
+            try {
+                stream.close();
+            } catch (Exception ex) {
+                // do nothing
+            }
+            return data;
+        } catch (Exception ex) {
+            WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
+            throw ex;
+        }
+
+    }
+
+    /**
+     * 将Map转换为XML格式的字符串
+     *
+     * @param data Map类型数据
+     * @return XML格式的字符串
+     * @throws Exception
+     */
+    public static String mapToXml(Map<String, String> data) throws Exception {
+        org.w3c.dom.Document document = WXPayXmlUtil.newDocument();
+        org.w3c.dom.Element root = document.createElement("xml");
+        document.appendChild(root);
+        for (String key: data.keySet()) {
+            String value = data.get(key);
+            if (value == null) {
+                value = "";
+            }
+            value = value.trim();
+            org.w3c.dom.Element filed = document.createElement(key);
+            filed.appendChild(document.createTextNode(value));
+            root.appendChild(filed);
+        }
+        TransformerFactory tf = TransformerFactory.newInstance();
+        Transformer transformer = tf.newTransformer();
+        DOMSource source = new DOMSource(document);
+        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+        StringWriter writer = new StringWriter();
+        StreamResult result = new StreamResult(writer);
+        transformer.transform(source, result);
+        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
+        try {
+            writer.close();
+        }
+        catch (Exception ex) {
+        }
+        return output;
+    }
+
+    /**
+     * 判断签名是否正确,必须包含sign字段,否则返回false。
+     *
+     * @param data Map类型数据
+     * @param key API密钥
+     * @param signType 签名方式
+     * @return 签名是否正确
+     * @throws Exception
+     */
+    public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {
+        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
+            return false;
+        }
+        String sign = data.get(WXPayConstants.FIELD_SIGN);
+        return generateSignature(data, key, signType).equals(sign);
+    }
+
+    /**
+     * 生成签名
+     *
+     * @param data 待签名数据
+     * @param key API密钥
+     * @return 签名
+     */
+    public static String generateSignature(final Map<String, String> data, String key) throws Exception {
+        return generateSignature(data, key, SignType.MD5);
+    }
+
+    /**
+     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
+     *
+     * @param data 待签名数据
+     * @param key API密钥
+     * @param signType 签名方式
+     * @return 签名
+     */
+    public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
+        Set<String> keySet = data.keySet();
+        String[] keyArray = keySet.toArray(new String[keySet.size()]);
+        Arrays.sort(keyArray);
+        StringBuilder sb = new StringBuilder();
+
+        for (String k : keyArray) {
+            if (k.equals(WXPayConstants.FIELD_SIGN)) {
+                continue;
+            }
+            if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
+                sb.append(k).append("=").append(data.get(k).trim()).append("&");
+        }
+        sb.append("key=").append(key);
+        if (SignType.MD5.equals(signType)) {
+            return MD5(sb.toString()).toUpperCase();
+        }
+        else if (SignType.HMACSHA256.equals(signType)) {
+            return HMACSHA256(sb.toString(), key);
+        }
+        else {
+            throw new Exception(String.format("Invalid sign_type: %s", signType));
+        }
+    }
+
+
+    /**
+     * 获取随机字符串 Nonce Str
+     *
+     * @return String 随机字符串
+     */
+    public static 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);
+    }
+
+
+    /**
+     * 生成 MD5
+     *
+     * @param data 待处理数据
+     * @return MD5结果
+     */
+    public static String MD5(String data) throws Exception {
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        byte[] array = md.digest(data.getBytes("UTF-8"));
+        StringBuilder sb = new StringBuilder();
+        for (byte item : array) {
+            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
+        }
+        return sb.toString().toUpperCase();
+    }
+
+    /**
+     * 生成 HMACSHA256
+     * @param data 待处理数据
+     * @param key 密钥
+     * @return 加密结果
+     * @throws Exception
+     */
+    public static String HMACSHA256(String data, String key) throws Exception {
+        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
+        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
+        sha256_HMAC.init(secret_key);
+        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
+        StringBuilder sb = new StringBuilder();
+        for (byte item : array) {
+            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
+        }
+        return sb.toString().toUpperCase();
+    }
+
+    /**
+     * 日志
+     * @return
+     */
+    public static Logger getLogger() {
+        Logger logger = LoggerFactory.getLogger("wxpay java sdk");
+        return logger;
+    }
+
+    /**
+     * 获取当前时间戳,单位秒
+     * @return
+     */
+    public static long getCurrentTimestamp() {
+        return System.currentTimeMillis()/1000;
+    }
+
+    /**
+     * 获取当前时间戳,单位毫秒
+     * @return
+     */
+    public static long getCurrentTimestampMs() {
+        return System.currentTimeMillis();
+    }
+
+}

+ 30 - 0
src/main/java/com/zhongzheng/common/util/wx/WXPayXmlUtil.java

@@ -0,0 +1,30 @@
+package com.zhongzheng.common.util.wx;
+
+import org.w3c.dom.Document;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+/**
+ * 2018/7/3
+ */
+public final class WXPayXmlUtil {
+    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
+        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        documentBuilderFactory.setXIncludeAware(false);
+        documentBuilderFactory.setExpandEntityReferences(false);
+
+        return documentBuilderFactory.newDocumentBuilder();
+    }
+
+    public static Document newDocument() throws ParserConfigurationException {
+        return newDocumentBuilder().newDocument();
+    }
+}

+ 90 - 0
src/main/java/com/zhongzheng/controller/ChannelController.java

@@ -0,0 +1,90 @@
+package com.zhongzheng.controller;
+
+import com.zhongzheng.bo.*;
+import com.zhongzheng.common.croe.BaseController;
+import com.zhongzheng.common.croe.LoginUser;
+import com.zhongzheng.common.model.AjaxResult;
+import com.zhongzheng.common.model.TableDataInfo;
+import com.zhongzheng.common.model.TokenService;
+import com.zhongzheng.common.util.ServletUtils;
+import com.zhongzheng.service.IChannelService;
+import com.zhongzheng.vo.ChannelDetailVo;
+import com.zhongzheng.vo.UserChannelVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 渠道管理
+ *
+ * @author zhongzheng
+ */
+@Api(tags ="渠道管理")
+@RestController
+@RequestMapping("/channel")
+public class ChannelController extends BaseController {
+
+    @Autowired
+    private IChannelService iChannelService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    @ApiOperation("渠道列表")
+    @GetMapping("/list")
+    public TableDataInfo<ChannelDetailVo> listByQuery(ChannelQueryBo queryBo) {
+        return iChannelService.listByQuery(queryBo);
+    }
+
+    @ApiOperation("新增渠道")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody ChannelBo bo) {
+        return toAjax(iChannelService.insertChannel(bo));
+    }
+
+    @ApiOperation("修改渠道")
+    @PostMapping("/edit")
+    public AjaxResult edit(@RequestBody ChannelEditBo editBo) {
+        return toAjax(iChannelService.editChannel(editBo));
+    }
+
+    @ApiOperation("渠道详情")
+    @GetMapping("/detail/{channelId}")
+    public AjaxResult<ChannelDetailVo> detail(@PathVariable("channelId") Long channelId) {
+        ChannelDetailVo detailVo = iChannelService.detail(channelId);
+        return AjaxResult.success(detailVo);
+    }
+
+    @ApiOperation("删除渠道")
+    @PostMapping("/delete")
+    public AjaxResult delete(@RequestBody ChannelDeleteBo deleteBo) {
+        return toAjax(iChannelService.delete(deleteBo));
+    }
+
+    @ApiOperation("关联渠道用户")
+    @PostMapping("/add/user/relevance")
+    public AjaxResult addUserChannel(@RequestBody UserChannelBo bo) {
+        return toAjax(iChannelService.addUserChannel(bo));
+    }
+
+    @ApiOperation("获取渠道关联账号")
+    @GetMapping("/user/list/{channelId}")
+    public AjaxResult<List<UserChannelVo>> getChannelUser(@PathVariable("channelId") Long channelId) {
+        List<UserChannelVo> channelVos = iChannelService.getUserList(channelId);
+        return AjaxResult.success(channelVos);
+    }
+
+    @ApiOperation("获取账号渠道列表")
+    @GetMapping("/userChannel")
+    public TableDataInfo<ChannelDetailVo> getUserChannelList(ChannelQueryBo queryBo) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        queryBo.setUserId(userId);
+        return iChannelService.getUserChannelList(queryBo);
+    }
+
+
+}

+ 48 - 0
src/main/java/com/zhongzheng/controller/CommonController.java

@@ -0,0 +1,48 @@
+package com.zhongzheng.controller;
+
+import cn.hutool.core.util.StrUtil;
+import com.zhongzheng.common.config.RuoYiConfig;
+import com.zhongzheng.common.util.FileUtils;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 通用请求处理
+ *
+ * @author zhongzheng
+ */
+@RestController
+public class CommonController {
+
+    /**
+     * 通用下载请求
+     *
+     * @param fileName 文件名称
+     * @param delete   是否删除
+     */
+    @GetMapping("/common/download")
+    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) {
+        try {
+            if (!FileUtils.checkAllowDownload(fileName)) {
+                throw new Exception(StrUtil.format("文件名称({})非法,不允许下载。 ", fileName));
+            }
+            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
+            String filePath = RuoYiConfig.getDownloadPath() + fileName;
+
+            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+            FileUtils.setAttachmentResponseHeader(response, realFileName);
+            FileUtils.writeBytes(filePath, response.getOutputStream());
+            if (delete)
+            {
+                FileUtils.deleteFile(filePath);
+            }
+        } catch (Exception e) {
+           e.printStackTrace();
+        }
+    }
+
+}

+ 101 - 0
src/main/java/com/zhongzheng/controller/FinanceOrderController.java

@@ -0,0 +1,101 @@
+package com.zhongzheng.controller;
+
+import com.zhongzheng.bo.*;
+import com.zhongzheng.common.croe.BaseController;
+import com.zhongzheng.common.croe.LoginUser;
+import com.zhongzheng.common.model.AjaxResult;
+import com.zhongzheng.common.model.TableDataInfo;
+import com.zhongzheng.common.model.TokenService;
+import com.zhongzheng.common.util.ServletUtils;
+import com.zhongzheng.service.IOrderService;
+import com.zhongzheng.service.ISysPersonService;
+import com.zhongzheng.vo.OrderDetailVo;
+import com.zhongzheng.vo.OrderInvoiceVo;
+import com.zhongzheng.vo.OrderListVo;
+import com.zhongzheng.vo.OrderRefundDetailVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 财务订单管理
+ *
+ * @author zhongzheng
+ */
+@Api(tags ="财务订单管理")
+@RestController
+@RequestMapping("/finance/order")
+public class FinanceOrderController extends BaseController {
+
+    @Autowired
+    private ISysPersonService iSysPersonService;
+
+    @Autowired
+    private IOrderService iOrderService;
+
+    @Autowired
+    private TokenService tokenService;
+
+
+    @ApiOperation("订单列表")
+    @GetMapping("/list")
+    public TableDataInfo<OrderListVo> getFinanceOrderList(OrderQueryBo bo) {
+        return iOrderService.getFinanceOrderList(bo);
+    }
+
+    @ApiOperation("订单详情")
+    @GetMapping("/detail/{orderSn}")
+    public AjaxResult<OrderDetailVo> getFinanceOrderDetail(@PathVariable("orderSn" ) String orderSn) {
+        OrderDetailVo detailVo = iOrderService.getFinanceOrderDetail(orderSn);
+        return AjaxResult.success(detailVo);
+    }
+
+    @ApiOperation("订单审核")
+    @PostMapping("/check")
+    public AjaxResult checkOrder(@RequestBody OrderCheckBo bo) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        bo.setUserId(loginUser.getUser().getUserId());
+        bo.setIsAdmin(loginUser.getUser().getUserName().equals("admin"));
+        return toAjax(iOrderService.checkOrder(bo));
+    }
+
+    @ApiOperation("订单发票申请信息")
+    @GetMapping("/invoice/apply/{orderSn}")
+    public AjaxResult<OrderInvoiceVo> getOrderInvoiceApply(@PathVariable("orderSn" ) String orderSn) {
+        OrderInvoiceVo invoiceVo = iOrderService.getOrderInvoiceApply(orderSn);
+        return AjaxResult.success(invoiceVo);
+    }
+
+    @ApiOperation("订单发票审核")
+    @PostMapping("/invoice/check")
+    public AjaxResult invoiceCheckOrder(@RequestBody OrderCheckBo bo) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        bo.setUserId(loginUser.getUser().getUserId());
+        bo.setIsAdmin(loginUser.getUser().getUserName().equals("admin"));
+        return toAjax(iOrderService.invoiceCheckOrder(bo));
+    }
+
+    @ApiOperation("发票上传")
+    @PostMapping("/invoice/upload")
+    public AjaxResult invoiceUpload(@RequestBody InvoiceUploadBo bo) {
+        return toAjax(iOrderService.invoiceUpload(bo));
+    }
+
+    @ApiOperation("订单退款详情")
+    @GetMapping("/refund/detail/{orderSn}")
+    public AjaxResult<OrderRefundDetailVo> getOrderRefundDetail(@PathVariable("orderSn" ) String orderSn) {
+        OrderRefundDetailVo refundDetail = iOrderService.getOrderRefundDetail(orderSn);
+        return AjaxResult.success(refundDetail);
+    }
+
+    @ApiOperation("订单退款审核")
+    @PostMapping("/refund/check")
+    public AjaxResult refundCheckOrder(@RequestBody OrderCheckBo bo) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        bo.setUserId(loginUser.getUser().getUserId());
+        bo.setIsAdmin(loginUser.getUser().getUserName().equals("admin"));
+        return toAjax(iOrderService.refundCheckOrder(bo));
+    }
+
+}

+ 236 - 0
src/main/java/com/zhongzheng/controller/OrderController.java

@@ -0,0 +1,236 @@
+package com.zhongzheng.controller;
+
+import cn.afterturn.easypoi.excel.entity.ExportParams;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.zhongzheng.bo.*;
+import com.zhongzheng.common.croe.BaseController;
+import com.zhongzheng.common.croe.LoginUser;
+import com.zhongzheng.common.model.AjaxResult;
+import com.zhongzheng.common.model.TableDataInfo;
+import com.zhongzheng.common.model.TokenService;
+import com.zhongzheng.common.util.EasyPoiUtil;
+import com.zhongzheng.common.util.ExcelUtil;
+import com.zhongzheng.common.util.ServletUtils;
+import com.zhongzheng.common.util.TelPhoneUtils;
+import com.zhongzheng.service.IOrderService;
+import com.zhongzheng.service.ISysPersonService;
+import com.zhongzheng.vo.OrderDetailVo;
+import com.zhongzheng.vo.OrderListVo;
+import com.zhongzheng.vo.OrderPersonVo;
+import com.zhongzheng.vo.OrderSettlementVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 咨询订单管理
+ *
+ * @author zhongzheng
+ */
+@Api(tags ="咨询订单管理")
+@RestController
+@RequestMapping("/order")
+public class OrderController extends BaseController {
+
+    @Autowired
+    private ISysPersonService iSysPersonService;
+
+    @Autowired
+    private IOrderService iOrderService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    @ApiOperation("导入用户")
+    @PostMapping("/importData")
+    public AjaxResult<Map<String,Object>> importData(MultipartFile file, Long companyId) throws Exception
+    {
+        Map<String, Object> rs = new HashMap<>();
+        ExcelUtil<UserImportAddBo> util = new ExcelUtil<UserImportAddBo>(UserImportAddBo.class);
+        List<UserImportAddBo> userList = null;
+        try{
+            userList = util.importExcel(file.getInputStream());
+        }catch (Exception e){
+            throw new IllegalArgumentException("模板格式不正确,请重新下载模板");
+        }
+        Collections.reverse(userList);
+        if (userList.stream().allMatch(item -> ObjectUtils.isNull(item))){
+            throw new IllegalArgumentException("模板格式不正确,请重新下载模板");
+        }
+        rs = iSysPersonService.importUser(userList,companyId);
+        return AjaxResult.success(rs);
+    }
+
+    @ApiOperation("新增员工信息")
+    @PostMapping("/add/person")
+    public AjaxResult<OrderPersonVo> addOrderPerson(@RequestBody OrderPersonBo bo) {
+        OrderPersonVo vo = iSysPersonService.addOrderPerson(bo);
+        return AjaxResult.success(vo);
+    }
+
+    @ApiOperation("订单结算")
+    @PostMapping("/settlement")
+    public AjaxResult<OrderSettlementVo> orderSettlement(@RequestBody OrderSettlementBo bo) {
+        OrderSettlementVo vo = iOrderService.orderSettlement(bo);
+        return AjaxResult.success(vo);
+    }
+
+    @ApiOperation("订单提交")
+    @PostMapping("/submit")
+    public AjaxResult orderSubmit(@RequestBody OrderSubmitBo bo) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        bo.setUserId(loginUser.getUser().getUserId());
+        Map<String, String> vo = iOrderService.orderSubmit(bo);
+        return AjaxResult.success(vo);
+    }
+
+    @ApiOperation("查看支付状态")
+    @GetMapping("/{orderSn}")
+    public AjaxResult<Integer> getOrderStatus(@PathVariable("orderSn" ) String orderSn) {
+        return AjaxResult.success(iOrderService.queryByOrderSn(orderSn));
+    }
+
+    @ApiOperation("订单列表")
+    @GetMapping("/list")
+    public TableDataInfo<OrderListVo> getOrderList(OrderQueryBo bo) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        bo.setUserId(loginUser.getUser().getUserId());
+        return iOrderService.getOrderList(bo);
+    }
+
+    @ApiOperation("订单详情")
+    @GetMapping("/detail/{orderSn}")
+    public AjaxResult<OrderDetailVo> getOrderDetail(@PathVariable("orderSn" ) String orderSn) {
+        OrderDetailVo detailVo = iOrderService.getOrderDetail(orderSn);
+        return AjaxResult.success(detailVo);
+    }
+
+    @ApiOperation("上传支付凭证")
+    @PostMapping("/attachment")
+    public AjaxResult uploadAttachment(@RequestBody OrderUploadAttachBo bo) {
+        return toAjax(iOrderService.uploadAttachment(bo));
+    }
+
+    @ApiOperation("发票申请")
+    @PostMapping("/invoice")
+    public AjaxResult invoiceApply(@RequestBody OrderInvoiceCheckBo bo) {
+        return toAjax(iOrderService.invoiceApply(bo));
+    }
+
+    @ApiOperation("退款申请")
+    @PostMapping("/refund/apply")
+    public AjaxResult refundApply(@RequestBody RefundApplyBo bo) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        bo.setUserId(loginUser.getUser().getUserId());
+        return toAjax(iOrderService.refundApply(bo));
+    }
+
+    @ApiOperation("订单修改")
+    @PostMapping("/edit")
+    public AjaxResult edit(@RequestBody OrderEditBo bo) {
+        return toAjax(iOrderService.edit(bo));
+    }
+
+    @ApiOperation("订单关闭")
+    @PostMapping("/revocation")
+    public AjaxResult revocation(@RequestBody OrderRevocationBo bo) {
+        return toAjax(iOrderService.revocation(bo));
+    }
+
+    @ApiOperation("用户过滤")
+    @PostMapping("/filtration")
+    public AjaxResult filtration(MultipartFile file) {
+        List<UserFiltrationBo> filtrationBos = EasyPoiUtil.importExcel(file,0,1,UserFiltrationBo.class);
+
+        List<String> company = new ArrayList<>();
+        company.add("中交广州航道局有限公司");
+        company.add("广州市第三建筑工程有限公司");
+        company.add("广东省工业设备安装有限公司");
+        company.add("珠海市建安集团有限公司");
+        company.add("广东省基础工程集团有限公司");
+        company.add("上海建工四建集团有限公司广州分公司");
+        company.add("广东省建筑工程机械施工有限公司");
+        company.add("广州市恒盛建设工程有限公司");
+        company.add("广东省建筑装饰集团公司");
+        company.add("中交四航局第二工程有限公司");
+        company.add("广州市市政工程机械施工有限公司");
+        company.add("中交四航局江门航通船业有限公司");
+        company.add("中铁二十五局集团有限公司");
+        company.add("广州天力建筑工程有限公司");
+        company.add("中交四航局第六工程有限公司");
+        company.add("中交四航局第三工程有限公司");
+        company.add("广州市第二建筑工程有限公司");
+        company.add("广州电力建设有限公司");
+        company.add("汕尾市广投建设工程有限公司");
+        company.add("中铁二十五局集团第一工程有限公司");
+        company.add("江门市电力工程输变电有限公司");
+        company.add("中交四航局第一工程有限公司");
+        company.add("中铁广州局集团第三工程有限公司");
+        company.add("中交广州建设有限公司");
+        company.add("恩平市第一建筑工程有限公司");
+        company.add("中冶城市投资控股有限公司");
+        company.add("梅州市敏捷建筑工程有限公司");
+        company.add("中铁广州工程局第二工程有限公司");
+        company.add("中铁广州工程局集团第二工程有限公司");
+        company.add("广东华隧建设集团股份有限公司");
+        company.add("广州增电电力建设投资集团有限公司");
+        company.add("广东广晟南方建设有限公司");
+        company.add("中建不二幕墙装饰有限公司");
+        company.add("上海建工五建集团有限公司");
+        company.add("广州普邦园林股份有限公司");
+        company.add("博罗县园洲建筑工程有限公司");
+        company.add("博罗县园洲建筑工程公司");
+        company.add("中交第四航务工程局有限公司");
+        company.add("中交四航局第七工程有限公司");
+        company.add("中建三局第二建设工程有限责任公司");
+        company.add("广东省第二建筑工程有限公司");
+        company.add("中铁十八局集团南方工程有限公司");
+        company.add("中铁建设集团南方工程有限公司");
+        company.add("广州市第五建筑工程有限公司");
+        company.add("广东省湛江农垦建设集团有限公司");
+        company.add("茂名建筑集团第三有限公司");
+        company.add("中建五局深圳分公司");
+        company.add("中建五局华南建设有限公司");
+        company.add("中建五局南方建设发展有限公司");
+        company.add("中建五局华南公司广州分公司");
+        company.add("中建五局华南建设有限公司(深圳)");
+        company.add("中建五局华南公司深圳分公司");
+        company.add("中建五局华南公司广西分公司");
+        company.add("中建五局华南公司东莞分公司");
+        company.add("中建五局华南公司福建经理部");
+        company.add("中建五局华南公司基础设施分公司");
+        company.add("中建五局华南公司机电钢构事业部");
+        company.add("中建五局装饰幕墙有限公司");
+        company.add("广东省第二建筑有限公司第四分公司");
+        company.add("广东省第二建筑工程有限公司粤西分公司");
+        company.add("广东省第二建筑有限公司第三分公司");
+        company.add("广州市恒盛建设集团有限公司");
+
+        List<UserFiltrationBo> collect = filtrationBos.stream().filter(x -> !company.contains(x.getCompany()) && TelPhoneUtils.isValidPhoneExact(x.getUserName())).collect(Collectors.toList());
+        //去重
+        List<UserFiltrationBo> collect1 = collect.stream().collect(Collectors.collectingAndThen(
+                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(UserFiltrationBo::getUserName))),
+                ArrayList::new
+        ));
+
+        ExcelUtil<UserFiltrationBo> util = new ExcelUtil<UserFiltrationBo>(UserFiltrationBo.class);
+        ExportParams deptExportParams = new ExportParams();
+        // 设置sheet得名称
+        deptExportParams.setSheetName("表1");
+        Map<String, Object> deptExportMap = new HashMap<>();
+        deptExportMap.put("title", deptExportParams);
+        deptExportMap.put("entity", UserFiltrationBo.class);
+        // sheet中要填充得数据
+        deptExportMap.put("data", collect1);
+        List<Map<String, Object>> sheetsList = new ArrayList<>();
+        sheetsList.add(deptExportMap);
+        return util.exportEasyExcel(sheetsList, "客户端用户");
+    }
+
+}

+ 31 - 0
src/main/java/com/zhongzheng/controller/wx/WxPayController.java

@@ -0,0 +1,31 @@
+package com.zhongzheng.controller.wx;
+
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import com.zhongzheng.service.IWxPayService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Api(tags ="微信支付操作管理")
+@ApiSupport(order = 2)
+@RestController
+@RequestMapping("/wx/pay")
+public class WxPayController {
+
+    @Autowired
+    private IWxPayService iWxPayService;
+
+    @ApiOperation("获取支付回调凭证")
+    @PostMapping("/callback")
+    public String callback(@RequestBody String notifyData)
+    {
+        System.out.println("支付回调");
+        return iWxPayService.paymentCallBack(notifyData);
+    }
+
+}
+

+ 56 - 0
src/main/java/com/zhongzheng/domian/Channel.java

@@ -0,0 +1,56 @@
+package com.zhongzheng.domian;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName(value ="Channel",autoResultMap = true)
+public class Channel implements Serializable {
+
+    @TableId(value = "channel_id")
+    private Long channelId;
+    /** 渠道名称 */
+    private String name;
+    /** 教育类型名称 */
+    private String eduName;
+    /** 业务层次名称 */
+    private String businessName;
+    /** 渠道内容 */
+    private String content;
+    /** 渠道内容 */
+    private String coverUrl;
+    /** 单科价格 */
+    private BigDecimal price;
+    /** 活动价格 */
+    private BigDecimal activityPrice;
+    /** 渠道类型:1,默认类型 2满减 3特价活动 4商品礼包 */
+    private Integer type;
+    /** 满额 */
+    private BigDecimal satisfyPrice;
+    /** 减额 */
+    private BigDecimal reducePrice;
+    /** 备注 */
+    private String remark;
+    /** 活动开始时间 */
+    private Long activityStart;
+    /** 活动结束时间 */
+    private Long activityEnd;
+    /** 创建时间 */
+    private Long createTime;
+    /** 更新时间 */
+    private Long updateTime;
+    /** 1有效 0无效 */
+    private Integer status;
+    /** 租户ID  */
+    private Long tenantId;
+
+}

+ 75 - 0
src/main/java/com/zhongzheng/domian/Order.java

@@ -0,0 +1,75 @@
+package com.zhongzheng.domian;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 咨询机构关联 order
+ *
+ * @author zhongzheng
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName(value ="`order`",autoResultMap = true)
+public class Order implements Serializable {
+
+    @TableId(value = "order_id")
+    private Long orderId;
+    /** 订单编号 */
+    private String orderSn;
+    /** 用户ID */
+    private Long userId;
+    /** 公司ID */
+    private Long companyId;
+    /** 订单标准价格 */
+    private BigDecimal orderPrice;
+    /** 支付订单号 */
+    private String orderGeneral;
+    /** 订单状态 -2 超时关闭,-1手动关闭,0待支付,1已支付 3部分已退款 4完全退款 */
+    private Integer orderStatus;
+    /** 创建时间 */
+    private Long createTime;
+    /** 更新时间 */
+    private Long updateTime;
+    /** 支付时间 */
+    private Long payTime;
+    /** 微信流水号 */
+    private String transid;
+    /** 渠道ID */
+    private Long channelId;
+    /** 订单类型 :1,对公  2,微信 3,线下 */
+    private Integer orderType;
+    /** 支付价格(成交价) */
+    private BigDecimal payPrice;
+    /** 订单来源 1营销系统 */
+    private Integer orderFrom;
+    /** 1有效 0无效 */
+    private Integer status;
+    /** 支付状态 0未收费,1部分付款  2完全付款 3免费 */
+    private Integer payStatus;
+    /** 审核状态:0待审核,1已通过,2未通过,3已撤销 */
+    private Integer checkStatus;
+    /** 退款状态:0待审核,1正常,2未退款,3已完成,4未通过 */
+    private Integer refundStatus;
+    /** 发票状态:0未开票,1已开票,2待审核,,3已审核,4审核未通过*/
+    private Integer invoiceStatus;
+    /** 退款(元) */
+    private BigDecimal orderRefund;
+    /** 已退(元) */
+    private BigDecimal orderRefunded;
+    /** 未收账款(元) */
+    private BigDecimal orderUncollected;
+    /** 实收账款(元) */
+    private BigDecimal orderReceived;
+    /** 备注 */
+    private String remark;
+    /** 租户ID */
+    private Long tenantId;
+}

+ 34 - 0
src/main/java/com/zhongzheng/domian/OrderAttachment.java

@@ -0,0 +1,34 @@
+package com.zhongzheng.domian;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName(value ="order_attachment",autoResultMap = true)
+public class OrderAttachment implements Serializable {
+
+    /** 主键 */
+    @TableId(value = "id")
+    private Long id;
+    /** 订单编号 */
+    private String orderSn;
+    /** 附件地址 */
+    private String attachmentUrl;
+    /** 类型:1转账凭证,2附件 */
+    private Integer attachmentType;
+    /** 1有效 0无效 */
+    private Integer status;
+    /** 创建时间 */
+    private Long createTime;
+    /** 更新时间 */
+    private Long updateTime;
+
+    private Long tenantId;
+}

+ 77 - 0
src/main/java/com/zhongzheng/domian/OrderInvoice.java

@@ -0,0 +1,77 @@
+package com.zhongzheng.domian;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName("order_invoice")
+public class OrderInvoice implements Serializable {
+
+    /** 发票ID */
+    @TableId(value = "invoice_id")
+    private Long invoiceId;
+    /** 类型 1普通发票 2 增值税专用发票 */
+    private Integer type;
+    /** 订单编号 */
+    private String orderSn;
+    /** 发票抬头 */
+    private String invoiceTitle;
+    /** 发票地址(多个逗号隔开) */
+    private String invoiceUrl;
+    /** 邮箱 */
+    private String email;
+    /** 审核状态 1待审核 2驳回 3通过 */
+    private Integer periodStatus;
+    /** 主体 1个人 2企业 */
+    private Integer subject;
+    /** 纳税登记号 */
+    private String taxRegistryNumber;
+    /** 发票状态 1未开票 2已开票 -1 已退票 */
+    private Integer invoiceStatus;
+    /** 发票金额 */
+    private BigDecimal amount;
+    /** 开票时间 */
+    private Long makeOutTime;
+    /** 申请时间 */
+    private Long applyTime;
+    /** 审核时间 */
+    private Long periodTime;
+    /** 审核原因 */
+    private String periodReason;
+    /** 公司地址 */
+    private String companyAddress;
+    /** 电话号码 */
+    private String phone;
+    /** 开户银行 */
+    private String bankName;
+    /** 银行账号 */
+    private String bankAccount;
+    /** 收件地址 */
+    private String receivingAddress;
+    /** 收件人姓名 */
+    private String receivingName;
+    /** 收件人手机号码 */
+    private String receivingTel;
+    /** 开票备注 */
+    private String openRemark;
+    /** 处理备注 */
+    private String handleRemark;
+    /** 添加时间 */
+    private Long createTime;
+    /** 修改时间 */
+    private Long updateTime;
+    /** 状态 1有效 0无效 */
+    private Integer status;
+    /** 租户ID */
+    private Long tenantId;
+}

+ 60 - 0
src/main/java/com/zhongzheng/domian/OrderPerson.java

@@ -0,0 +1,60 @@
+package com.zhongzheng.domian;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 咨询机构关联 order_goods
+ *
+ * @author zhongzheng
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName(value ="order_person",autoResultMap = true)
+public class OrderPerson implements Serializable {
+
+    @TableId(value = "order_person_id")
+    private Long orderPersonId;
+    /** 订单编号 */
+    private String orderSn;
+    /** 标准价格 */
+    private BigDecimal price;
+    /** 成交价格 */
+    private BigDecimal realPrice;
+    /** 退款状态 0(无和学员端拒绝退款)  1退款中 2已退款 3拒绝退款 */
+    private Integer refundStatus;
+    /** 支付状态 1未收费 2部分收费 3完全收费 4免费 (退款此状态保持不变) */
+    private Integer payStatus;
+    /** 教育类型 */
+    private String eduName;
+    /** 业务名称 */
+    private String businessName;
+    /** 科目名称 */
+    private String subjectName;
+    /** 年份 */
+    private String year;
+    /** 员工ID*/
+    private Long personId;
+    /**员工名称 */
+    private String personName;
+    /** 员工身份证 */
+    private String personCard;
+    /** 员工手机号码 */
+    private String personPhone;
+    /** 状态 1正常 0关闭 */
+    private Integer status;
+    /** 添加时间 */
+    private Long createTime;
+    /** 修改时间 */
+    private Long updateTime;
+    /** 租户ID */
+    private Long tenantId;
+
+}

+ 66 - 0
src/main/java/com/zhongzheng/domian/OrderRefund.java

@@ -0,0 +1,66 @@
+package com.zhongzheng.domian;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 咨询机构关联 order_goods
+ *
+ * @author zhongzheng
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName(value ="order_refund",autoResultMap = true)
+public class OrderRefund implements Serializable {
+
+    @TableId(value = "refund_id")
+    private Long refundId;
+    /** 退款单号 */
+    private String refundSn;
+    /** 用户ID */
+    private Long userId;
+    /** 公司ID */
+    private Long companyId;
+    /** 订单编号 */
+    private String orderSn;
+    /** 退款金额 */
+    private BigDecimal refundFee;
+    /** 实际退款金额 */
+    private BigDecimal realRefundFee;
+    /** 微信退款单号 */
+    private String wxpayRefundId;
+    /** 申请退款原因 */
+    private String applyReason;
+    /** 审批状态 0 待审核 1 通过 2未通过 */
+    private Integer periodStatus;
+    /** 审核时间 */
+    private Long periodTime;
+    /** 审核原因 */
+    private String periodReason;
+    /** 退款时间 */
+    private Long refundTime;
+    /** 退款学员详情IDS,为空则是全部 */
+    private String orderPersonIds;
+    /** 收款人 */
+    private String payee;
+    /** 收款银行 */
+    private String payeeBank;
+    /** 收款账号 */
+    private String payeeBankAccount;
+    /** 状态 1正常 0关闭 */
+    private Integer status;
+    /** 添加时间 */
+    private Long createTime;
+    /** 修改时间 */
+    private Long updateTime;
+    /** 租户ID */
+    private Long tenantId;
+
+}

+ 46 - 0
src/main/java/com/zhongzheng/domian/TenantRelevance.java

@@ -0,0 +1,46 @@
+package com.zhongzheng.domian;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 咨询机构关联 tenant_relevance
+ *
+ * @author zhongzheng
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName(value ="tenant_relevance",autoResultMap = true)
+public class TenantRelevance implements Serializable {
+
+    /** 用户ID */
+    @TableId(value = "id",type = IdType.AUTO)
+    private Long id;
+
+    /** 租户ID */
+    private Long tenantId;
+
+    /** 关联的机构ID */
+    private Long relevanceId;
+
+    /** 机构名称 */
+    private String relevanceName;
+
+    /** 菜单状态(0显示 1隐藏) */
+    private Integer status;
+
+    /** 创建时间 */
+    private Long createTime;
+
+    /** 更新时间 */
+    private Long updateTime;
+
+}

+ 38 - 0
src/main/java/com/zhongzheng/domian/UserChannel.java

@@ -0,0 +1,38 @@
+package com.zhongzheng.domian;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 咨询机构关联 order_goods
+ *
+ * @author zhongzheng
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName(value ="user_channel",autoResultMap = true)
+public class UserChannel implements Serializable {
+
+    @TableId(value = "id")
+    private Long id;
+    /** 用户ID */
+    private Long userId;
+    /** 渠道ID */
+    private Long channelId;
+    /** 状态 1正常 0关闭 */
+    private Integer status;
+    /** 添加时间 */
+    private Long createTime;
+    /** 修改时间 */
+    private Long updateTime;
+    /** 租户ID */
+    private Long tenantId;
+
+}

+ 21 - 0
src/main/java/com/zhongzheng/mapper/ChannelMapper.java

@@ -0,0 +1,21 @@
+package com.zhongzheng.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zhongzheng.bo.ChannelQueryBo;
+import com.zhongzheng.domian.Channel;
+import com.zhongzheng.domian.Order;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 订单 数据层
+ *
+ * @author zhongzheng
+ */
+@Mapper
+public interface ChannelMapper extends BaseMapper<Channel> {
+
+    IPage<Channel> getUserChannelList(Page page,@Param("queryBo") ChannelQueryBo queryBo);
+}

+ 21 - 0
src/main/java/com/zhongzheng/mapper/OrderAttachmentMapper.java

@@ -0,0 +1,21 @@
+package com.zhongzheng.mapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhongzheng.domian.Order;
+import com.zhongzheng.domian.OrderAttachment;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 订单 数据层
+ *
+ * @author zhongzheng
+ */
+@Mapper
+public interface OrderAttachmentMapper extends BaseMapper<OrderAttachment> {
+
+    @InterceptorIgnore(tenantLine = "true")
+    List<OrderAttachment> listByOrderSn(String orderSn);
+}

+ 18 - 0
src/main/java/com/zhongzheng/mapper/OrderInvoiceMapper.java

@@ -0,0 +1,18 @@
+package com.zhongzheng.mapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhongzheng.domian.OrderInvoice;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 订单 数据层
+ *
+ * @author zhongzheng
+ */
+@Mapper
+public interface OrderInvoiceMapper extends BaseMapper<OrderInvoice> {
+
+    @InterceptorIgnore(tenantLine = "true")
+    OrderInvoice getOrderInvoiceById(Long invoiceId);
+}

+ 35 - 0
src/main/java/com/zhongzheng/mapper/OrderMapper.java

@@ -0,0 +1,35 @@
+package com.zhongzheng.mapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zhongzheng.bo.OrderQueryBo;
+import com.zhongzheng.domian.Order;
+import com.zhongzheng.vo.OrderDetailVo;
+import com.zhongzheng.vo.OrderInvoiceVo;
+import com.zhongzheng.vo.OrderListVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 订单 数据层
+ *
+ * @author zhongzheng
+ */
+@Mapper
+public interface OrderMapper extends BaseMapper<Order> {
+
+    IPage<OrderListVo> getOrderList(Page page,@Param("bo") OrderQueryBo bo);
+
+    OrderDetailVo getOrderDetail(String orderSn);
+
+    @InterceptorIgnore(tenantLine = "true")
+    IPage<OrderListVo> getFinanceOrderList(Page page,@Param("bo") OrderQueryBo bo);
+
+    @InterceptorIgnore(tenantLine = "true")
+    OrderDetailVo getFinanceOrderDetail(String orderSn);
+
+    @InterceptorIgnore(tenantLine = "true")
+    OrderInvoiceVo getOrderInvoiceApply(String orderSn);
+}

+ 20 - 0
src/main/java/com/zhongzheng/mapper/OrderPersonMapper.java

@@ -0,0 +1,20 @@
+package com.zhongzheng.mapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhongzheng.domian.OrderPerson;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 订单 数据层
+ *
+ * @author zhongzheng
+ */
+@Mapper
+public interface OrderPersonMapper extends BaseMapper<OrderPerson> {
+
+    @InterceptorIgnore(tenantLine = "true")
+    List<OrderPerson> listPersonBySn(String orderSn);
+}

+ 13 - 0
src/main/java/com/zhongzheng/mapper/OrderRefundMapper.java

@@ -0,0 +1,13 @@
+package com.zhongzheng.mapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhongzheng.domian.OrderRefund;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface OrderRefundMapper extends BaseMapper<OrderRefund> {
+
+    @InterceptorIgnore(tenantLine = "true")
+    OrderRefund getRefundBySn(String orderSn);
+}

+ 15 - 0
src/main/java/com/zhongzheng/mapper/UserChannelMapper.java

@@ -0,0 +1,15 @@
+package com.zhongzheng.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhongzheng.domian.UserChannel;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 字典表 数据层
+ *
+ * @author zhongzheng
+ */
+@Mapper
+public interface UserChannelMapper extends BaseMapper<UserChannel> {
+}
+

+ 30 - 0
src/main/java/com/zhongzheng/service/IChannelService.java

@@ -0,0 +1,30 @@
+package com.zhongzheng.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhongzheng.bo.*;
+import com.zhongzheng.common.model.TableDataInfo;
+import com.zhongzheng.domian.Channel;
+import com.zhongzheng.domian.Order;
+import com.zhongzheng.vo.ChannelDetailVo;
+import com.zhongzheng.vo.UserChannelVo;
+
+import java.util.List;
+
+public interface IChannelService extends IService<Channel> {
+
+    Boolean insertChannel(ChannelBo bo);
+
+    Boolean editChannel(ChannelEditBo editBo);
+
+    ChannelDetailVo detail(Long channelId);
+
+    Boolean delete(ChannelDeleteBo deleteBo);
+
+    TableDataInfo<ChannelDetailVo> listByQuery(ChannelQueryBo queryBo);
+
+    Boolean addUserChannel(UserChannelBo bo);
+
+    List<UserChannelVo> getUserList(Long channelId);
+
+    TableDataInfo<ChannelDetailVo> getUserChannelList(ChannelQueryBo queryBo);
+}

+ 11 - 0
src/main/java/com/zhongzheng/service/IOrderAttachmentService.java

@@ -0,0 +1,11 @@
+package com.zhongzheng.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhongzheng.domian.OrderAttachment;
+
+import java.util.List;
+
+public interface IOrderAttachmentService extends IService<OrderAttachment> {
+
+    List<OrderAttachment> listByOrderSn(String orderSn);
+}

+ 10 - 0
src/main/java/com/zhongzheng/service/IOrderInvoiceService.java

@@ -0,0 +1,10 @@
+package com.zhongzheng.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhongzheng.domian.OrderInvoice;
+
+public interface IOrderInvoiceService extends IService<OrderInvoice> {
+
+    OrderInvoice getOrderInvoiceById(Long invoiceId);
+
+}

+ 11 - 0
src/main/java/com/zhongzheng/service/IOrderPersonService.java

@@ -0,0 +1,11 @@
+package com.zhongzheng.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhongzheng.domian.OrderPerson;
+
+import java.util.List;
+
+public interface IOrderPersonService extends IService<OrderPerson> {
+
+    List<OrderPerson> listPersonBySn(String orderSn);
+}

+ 9 - 0
src/main/java/com/zhongzheng/service/IOrderRefundService.java

@@ -0,0 +1,9 @@
+package com.zhongzheng.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhongzheng.domian.OrderRefund;
+
+public interface IOrderRefundService extends IService<OrderRefund> {
+
+    OrderRefund getRefundBySn(String orderSn);
+}

+ 49 - 0
src/main/java/com/zhongzheng/service/IOrderService.java

@@ -0,0 +1,49 @@
+package com.zhongzheng.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhongzheng.bo.*;
+import com.zhongzheng.common.model.TableDataInfo;
+import com.zhongzheng.domian.Order;
+import com.zhongzheng.vo.*;
+
+import java.util.Map;
+
+public interface IOrderService extends IService<Order> {
+
+    OrderSettlementVo orderSettlement(OrderSettlementBo bo);
+
+    Map<String, String> orderSubmit(OrderSubmitBo bo);
+
+    TableDataInfo<OrderListVo> getOrderList(OrderQueryBo bo);
+
+    Integer queryByOrderSn(String orderSn);
+
+    OrderDetailVo getOrderDetail(String orderSn);
+
+    Boolean uploadAttachment(OrderUploadAttachBo bo);
+
+    Boolean invoiceApply(OrderInvoiceCheckBo bo);
+
+    Boolean refundApply(RefundApplyBo bo);
+
+    Boolean edit(OrderEditBo bo);
+
+    Boolean revocation(OrderRevocationBo bo);
+
+    TableDataInfo<OrderListVo> getFinanceOrderList(OrderQueryBo bo);
+
+    OrderDetailVo getFinanceOrderDetail(String orderSn);
+
+    Boolean checkOrder(OrderCheckBo bo);
+
+    OrderInvoiceVo getOrderInvoiceApply(String orderSn);
+
+    Boolean invoiceUpload(InvoiceUploadBo bo);
+
+    Boolean invoiceCheckOrder(OrderCheckBo bo);
+
+    OrderRefundDetailVo getOrderRefundDetail(String orderSn);
+
+    Boolean refundCheckOrder(OrderCheckBo bo);
+
+}

+ 7 - 0
src/main/java/com/zhongzheng/service/ISysPersonService.java

@@ -2,9 +2,12 @@ package com.zhongzheng.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.zhongzheng.bo.ImportPersonBo;
+import com.zhongzheng.bo.OrderPersonBo;
+import com.zhongzheng.bo.UserImportAddBo;
 import com.zhongzheng.common.model.TableDataInfo;
 import com.zhongzheng.domian.SysCompany;
 import com.zhongzheng.domian.SysPerson;
+import com.zhongzheng.vo.OrderPersonVo;
 
 import java.util.List;
 import java.util.Map;
@@ -46,4 +49,8 @@ public interface ISysPersonService extends IService<SysPerson> {
     public  SysPerson getPersonByIdCardAndNoInert(SysPerson entity);
 
     public Map<String, Object> importPerson(List<ImportPersonBo> personList,long companyId,long userId);
+
+    Map<String, Object> importUser(List<UserImportAddBo> userList, Long companyId);
+
+    OrderPersonVo addOrderPerson(OrderPersonBo bo);
 }

+ 12 - 0
src/main/java/com/zhongzheng/service/IUserChannelService.java

@@ -0,0 +1,12 @@
+package com.zhongzheng.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhongzheng.domian.UserChannel;
+
+/**
+ * 菜单 业务层
+ *
+ * @author zhongzheng
+ */
+public interface IUserChannelService extends IService<UserChannel> {
+}

+ 13 - 0
src/main/java/com/zhongzheng/service/IWxPayService.java

@@ -0,0 +1,13 @@
+package com.zhongzheng.service;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+public interface IWxPayService {
+
+    String paymentCallBack(String notifyData);
+
+    boolean dealOrder(String out_trade_no, String transaction_id);
+
+    Map<String, String> paymentPc(String out_trade_no, String body, BigDecimal price);
+}

+ 158 - 0
src/main/java/com/zhongzheng/service/impl/ChannelServiceImpl.java

@@ -0,0 +1,158 @@
+package com.zhongzheng.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhongzheng.bo.*;
+import com.zhongzheng.common.filter.CustomException;
+import com.zhongzheng.common.model.TableDataInfo;
+import com.zhongzheng.common.util.DateUtils;
+import com.zhongzheng.domian.Channel;
+import com.zhongzheng.domian.Order;
+import com.zhongzheng.domian.SysUser;
+import com.zhongzheng.domian.UserChannel;
+import com.zhongzheng.mapper.ChannelMapper;
+import com.zhongzheng.service.IChannelService;
+import com.zhongzheng.service.IOrderService;
+import com.zhongzheng.service.ISysUserService;
+import com.zhongzheng.service.IUserChannelService;
+import com.zhongzheng.vo.ChannelDetailVo;
+import com.zhongzheng.vo.UserChannelVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class ChannelServiceImpl extends ServiceImpl<ChannelMapper, Channel> implements IChannelService {
+
+    @Autowired
+    private IOrderService iOrderService;
+
+    @Autowired
+    private IUserChannelService iUserChannelService;
+
+    @Autowired
+    private ISysUserService iSysUserService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean insertChannel(ChannelBo bo) {
+        Channel bean = BeanUtil.toBean(bo, Channel.class);
+        bean.setUpdateTime(DateUtils.getNowTime());
+        bean.setCreateTime(DateUtils.getNowTime());
+        return save(bean);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean editChannel(ChannelEditBo editBo) {
+        //校验是否已有订单参加活动
+        int count = iOrderService.count(new LambdaQueryWrapper<Order>()
+                .eq(Order::getChannelId, editBo.getChannelId())
+                .eq(Order::getStatus, 1));
+        if (count > 0) {
+            throw new CustomException("已有订单参加该活动,请勿修改");
+        }
+        Channel bean = BeanUtil.toBean(editBo, Channel.class);
+        bean.setUpdateTime(DateUtils.getNowTime());
+        return updateById(bean);
+    }
+
+    @Override
+    public ChannelDetailVo detail(Long channelId) {
+        Channel channel = getById(channelId);
+        return BeanUtil.toBean(channel, ChannelDetailVo.class);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean delete(ChannelDeleteBo deleteBo) {
+        int count = iOrderService.count(new LambdaQueryWrapper<Order>()
+                .in(Order::getChannelId, deleteBo.getChannelIds())
+                .eq(Order::getStatus, 1));
+        if (count > 0) {
+            throw new CustomException("已有订单参加该活动,请勿修改");
+        }
+        removeByIds(deleteBo.getChannelIds());
+        //删除关联关系
+        iUserChannelService.remove(new LambdaQueryWrapper<UserChannel>().in(UserChannel::getChannelId, deleteBo.getChannelIds()));
+
+        return true;
+    }
+
+
+    @Override
+    public TableDataInfo<ChannelDetailVo> listByQuery(ChannelQueryBo queryBo) {
+        Page<Channel> page = page(new Page<>(queryBo.getPageNum(), queryBo.getPageSize()), new LambdaQueryWrapper<Channel>()
+                .eq(ObjectUtils.isNotNull(queryBo.getEduName()), Channel::getEduName, queryBo.getEduName())
+                .eq(ObjectUtils.isNotNull(queryBo.getBusinessName()), Channel::getBusinessName, queryBo.getBusinessName())
+                .eq(ObjectUtils.isNotNull(queryBo.getType()), Channel::getType, queryBo.getType())
+                .like(StringUtils.isNotBlank(queryBo.getName()), Channel::getName, queryBo.getName())
+                .eq(ObjectUtils.isNotNull(queryBo.getStatus()),Channel::getStatus, queryBo.getStatus()));
+        if (CollectionUtils.isEmpty(page.getRecords())){
+            return new TableDataInfo<ChannelDetailVo>().setRows(new ArrayList<>());
+        }
+        TableDataInfo<ChannelDetailVo> info = new TableDataInfo<>();
+        info.setTotal(page.getTotal());
+        info.setRows(page.getRecords().stream().map(x -> BeanUtil.toBean(x,ChannelDetailVo.class)).collect(Collectors.toList()));
+        return info;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean addUserChannel(UserChannelBo bo) {
+        //清除之前的关联关系
+        iUserChannelService.remove(new LambdaQueryWrapper<UserChannel>().eq(UserChannel::getChannelId,bo.getChannelId()));
+        //添加新的关联关系
+        if (CollectionUtils.isNotEmpty(bo.getUserIds())){
+            List<UserChannel> collect = bo.getUserIds().stream().map(x -> {
+                UserChannel channel = new UserChannel();
+                channel.setChannelId(bo.getChannelId());
+                channel.setUserId(x);
+                channel.setCreateTime(DateUtils.getNowTime());
+                channel.setUpdateTime(DateUtils.getNowTime());
+                return channel;
+            }).collect(Collectors.toList());
+            iUserChannelService.saveBatch(collect);
+        }
+        return true;
+    }
+
+    @Override
+    public List<UserChannelVo> getUserList(Long channelId) {
+        List<UserChannel> list = iUserChannelService.list(new LambdaQueryWrapper<UserChannel>().eq(UserChannel::getChannelId, channelId));
+        if (CollectionUtils.isEmpty(list)){
+            new ArrayList<>();
+        }
+        return list.stream().map(x -> {
+            SysUser sysUser = iSysUserService.getById(x.getUserId());
+            UserChannelVo vo = new UserChannelVo();
+            vo.setUserId(sysUser.getUserId());
+            vo.setUserName(sysUser.getNickName());
+            return vo;
+        }).collect(Collectors.toList());
+    }
+
+    @Override
+    public TableDataInfo<ChannelDetailVo> getUserChannelList(ChannelQueryBo queryBo) {
+        IPage<Channel> page =  baseMapper.getUserChannelList(new Page<Channel>(queryBo.getPageNum(),queryBo.getPageSize()),queryBo);
+        if (CollectionUtils.isEmpty(page.getRecords())){
+            return new TableDataInfo<ChannelDetailVo>().setRows(new ArrayList<>());
+        }
+        TableDataInfo<ChannelDetailVo> info = new TableDataInfo<>();
+        info.setTotal(page.getTotal());
+        info.setRows(page.getRecords().stream().map(x -> BeanUtil.toBean(x,ChannelDetailVo.class)).collect(Collectors.toList()));
+        return info;
+    }
+
+}

+ 22 - 0
src/main/java/com/zhongzheng/service/impl/OrderAttachmentServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zhongzheng.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhongzheng.domian.Order;
+import com.zhongzheng.domian.OrderAttachment;
+import com.zhongzheng.mapper.OrderAttachmentMapper;
+import com.zhongzheng.mapper.OrderMapper;
+import com.zhongzheng.service.IOrderAttachmentService;
+import com.zhongzheng.service.IOrderService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public class OrderAttachmentServiceImpl extends ServiceImpl<OrderAttachmentMapper, OrderAttachment> implements IOrderAttachmentService {
+
+    @Override
+    public List<OrderAttachment> listByOrderSn(String orderSn) {
+        return baseMapper.listByOrderSn(orderSn);
+    }
+}

+ 16 - 0
src/main/java/com/zhongzheng/service/impl/OrderInvoiceServiceImpl.java

@@ -0,0 +1,16 @@
+package com.zhongzheng.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhongzheng.domian.OrderInvoice;
+import com.zhongzheng.mapper.OrderInvoiceMapper;
+import com.zhongzheng.service.IOrderInvoiceService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class OrderInvoiceServiceImpl extends ServiceImpl<OrderInvoiceMapper, OrderInvoice> implements IOrderInvoiceService {
+
+    @Override
+    public OrderInvoice getOrderInvoiceById(Long invoiceId) {
+        return baseMapper.getOrderInvoiceById(invoiceId);
+    }
+}

+ 19 - 0
src/main/java/com/zhongzheng/service/impl/OrderPersonServiceImpl.java

@@ -0,0 +1,19 @@
+package com.zhongzheng.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhongzheng.domian.OrderPerson;
+import com.zhongzheng.mapper.OrderPersonMapper;
+import com.zhongzheng.service.IOrderPersonService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public class OrderPersonServiceImpl extends ServiceImpl<OrderPersonMapper, OrderPerson> implements IOrderPersonService {
+
+    @Override
+    public List<OrderPerson> listPersonBySn(String orderSn) {
+        return baseMapper.listPersonBySn(orderSn);
+    }
+}

+ 25 - 0
src/main/java/com/zhongzheng/service/impl/OrderRefundServiceImpl.java

@@ -0,0 +1,25 @@
+package com.zhongzheng.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhongzheng.common.filter.CustomException;
+import com.zhongzheng.domian.Memo;
+import com.zhongzheng.domian.MemoCertificate;
+import com.zhongzheng.domian.OrderRefund;
+import com.zhongzheng.mapper.MemoCertificateMapper;
+import com.zhongzheng.mapper.MemoMapper;
+import com.zhongzheng.mapper.OrderRefundMapper;
+import com.zhongzheng.service.IMemoCertificateService;
+import com.zhongzheng.service.IMemoService;
+import com.zhongzheng.service.IOrderRefundService;
+import com.zhongzheng.service.ISysCertificateService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class OrderRefundServiceImpl extends ServiceImpl<OrderRefundMapper, OrderRefund> implements IOrderRefundService {
+
+    @Override
+    public OrderRefund getRefundBySn(String orderSn) {
+        return baseMapper.getRefundBySn(orderSn);
+    }
+}

+ 804 - 0
src/main/java/com/zhongzheng/service/impl/OrderServiceImpl.java

@@ -0,0 +1,804 @@
+package com.zhongzheng.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.lang.Validator;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhongzheng.bo.*;
+import com.zhongzheng.common.croe.RedisCache;
+import com.zhongzheng.common.filter.CustomException;
+import com.zhongzheng.common.model.TableDataInfo;
+import com.zhongzheng.common.util.DateUtils;
+import com.zhongzheng.common.util.EncryptHandler;
+import com.zhongzheng.common.util.ServletUtils;
+import com.zhongzheng.domian.*;
+import com.zhongzheng.mapper.OrderMapper;
+import com.zhongzheng.mapper.SysUserRoleMapper;
+import com.zhongzheng.service.*;
+import com.zhongzheng.vo.*;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Service
+public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
+
+    @Autowired
+    private IChannelService iChannelService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    private ISysPersonService iSysPersonService;
+
+    @Autowired
+    private IOrderPersonService iOrderPersonService;
+
+    @Autowired
+    private IOrderRefundService iOrderRefundService;
+
+    @Autowired
+    private IOrderAttachmentService iOrderAttachmentService;
+
+    @Autowired
+    private IOrderInvoiceService iOrderInvoiceService;
+
+    @Autowired
+    private IWxPayService iWxPayService;
+
+    @Autowired
+    private ISysRoleService iSysRoleService;
+
+    @Autowired
+    private SysUserRoleMapper sysUserRoleMapper;
+
+    @Override
+    public OrderSettlementVo orderSettlement(OrderSettlementBo bo) {
+        if (CollectionUtils.isEmpty(bo.getSettlementList())){
+            throw new CustomException("清先添加商品信息");
+        }
+        //渠道信息
+        Channel channel = iChannelService.getById(bo.getChannelId());
+        OrderSettlementVo orderSettlementVo = BeanUtil.toBean(channel, OrderSettlementVo.class);
+        orderSettlementVo.setChannelId(bo.getChannelId());
+        orderSettlementVo.setCompanyId(bo.getCompanyId());
+        List<OrderSettlementDetailBo> detailBos = bo.getSettlementList();
+        //订单人数
+        Set<String> userNum = detailBos.stream().map(OrderSettlementDetailBo::getIdCard).collect(Collectors.toSet());
+        orderSettlementVo.setOrderUserNum(userNum.size());
+        //订单数量
+        orderSettlementVo.setOrderGoodsNum(detailBos.size());
+        BigDecimal priceTotal = detailBos.stream().map(OrderSettlementDetailBo::getOrderPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
+        orderSettlementVo.setOriginalPrice(priceTotal);
+        switch (channel.getType()){
+            case 1://普通渠道
+                //订单价格
+                orderSettlementVo.setOrderPrice(priceTotal);
+                break;
+            case 2: //满减
+                if (priceTotal.compareTo(channel.getSatisfyPrice()) >= 0 ){
+                    //减额
+                    BigDecimal reducePrice = channel.getReducePrice();
+                    BigDecimal subtract = priceTotal.subtract(reducePrice);
+                    orderSettlementVo.setOrderPrice(subtract);
+                }else {
+                    orderSettlementVo.setOrderPrice(priceTotal);
+                }
+                break;
+            case 3: //活动特价
+                BigDecimal activityPrice = detailBos.stream().map(OrderSettlementDetailBo::getActivityPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
+                orderSettlementVo.setOrderPrice(activityPrice);
+                break;
+            default: throw new CustomException("活动渠道类型有误!请检查");
+        }
+        orderSettlementVo.setSettlementList(bo.getSettlementList());
+        return orderSettlementVo;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String,String> orderSubmit(OrderSubmitBo bo) {
+        String key = "ORDER-" + "-" + bo.getUserId();
+        Long value = redisCache.getCacheObject(key);
+        if (Validator.isNotEmpty(value)) {
+            throw new CustomException("请勿频繁提交订单");
+        }
+        redisCache.setCacheObject(key, 1L, 5, TimeUnit.SECONDS);//5秒
+        if (CollectionUtils.isEmpty(bo.getSettlementList())) {
+            throw new CustomException("订单商品为空,请检查!");
+        }
+        //渠道信息
+        Channel channel = iChannelService.getById(bo.getChannelId());
+        if (ObjectUtils.isNull(channel)) {
+            throw new CustomException("渠道获取有误,请检查!");
+        }
+
+        //创建订单
+        Order order = new Order();
+        //生成订单号
+        String out_trade_no = DateUtils.getDateOrderSn();
+        order.setCreateTime(DateUtils.getNowTime());
+        order.setUpdateTime(DateUtils.getNowTime());
+        order.setChannelId(bo.getChannelId());
+        order.setUserId(bo.getUserId());
+        order.setCompanyId(bo.getCompanyId());
+        order.setOrderFrom(1);
+        order.setOrderSn(out_trade_no);
+        order.setStatus(1);
+        order.setRemark(bo.getOrderRemark());
+
+        //订单员工
+        for (OrderSettlementDetailBo detailBo : bo.getSettlementList()) {
+            OrderPerson orderPerson = new OrderPerson();
+            orderPerson.setOrderSn(out_trade_no);
+            orderPerson.setRefundStatus(0);
+            orderPerson.setPayStatus(1);
+            orderPerson.setEduName(bo.getEduName());
+            orderPerson.setBusinessName(bo.getBusinessName());
+            orderPerson.setSubjectName(detailBo.getSubjectNames());
+            orderPerson.setYear(detailBo.getYear());
+            orderPerson.setCreateTime(DateUtils.getNowTime());
+            orderPerson.setUpdateTime(DateUtils.getNowTime());
+            //获取学员
+            SysPerson person = iSysPersonService.getOne(new LambdaQueryWrapper<SysPerson>()
+                    .eq(SysPerson::getIdCard, EncryptHandler.encrypt(detailBo.getIdCard()))
+                    .eq(SysPerson::getStatus, 1)
+                    .eq(SysPerson::getCompanyId, bo.getCompanyId())
+                    .last("limit 1"));
+            if (ObjectUtils.isNull(person)){
+                throw new CustomException(String.format("【%】学员信息获取有误,请检查", detailBo.getIdCard()));
+            }
+            orderPerson.setPersonId(person.getPersonId());
+            orderPerson.setPersonName(detailBo.getRealname());
+            orderPerson.setPersonPhone(detailBo.getTelphone());
+            orderPerson.setPersonCard(detailBo.getIdCard());
+            orderPerson.setStatus(1);
+            orderPerson.setPrice(detailBo.getOrderPrice());
+            //价格
+            if (channel.getType() == 3){
+                //特价活动
+                orderPerson.setRealPrice(detailBo.getActivityPrice());
+            }else {
+                orderPerson.setRealPrice(detailBo.getOrderPrice());
+            }
+
+            iOrderPersonService.save(orderPerson);
+        }
+
+        //订单原价
+        BigDecimal reduce = bo.getSettlementList().stream().map(OrderSettlementDetailBo::getOrderPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
+        order.setOrderPrice(reduce);
+
+        //订单支付价格
+        BigDecimal payPrice = BigDecimal.ZERO;
+        if (channel.getType() == 2){
+            //满减
+            if (reduce.compareTo(channel.getSatisfyPrice()) >= 0){
+                payPrice = reduce.subtract(channel.getReducePrice());
+            }
+        }else if (channel.getType() == 3){
+            //活动特价
+            payPrice = bo.getSettlementList().stream().map(OrderSettlementDetailBo::getActivityPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
+        }else {
+            //普通
+            payPrice = reduce;
+        }
+        order.setPayPrice(payPrice);
+        //待支付
+        order.setOrderStatus(0);
+        order.setPayStatus(0);
+        order.setCheckStatus(0);
+        order.setInvoiceStatus(0);
+
+        //订单附件
+        if (CollectionUtils.isNotEmpty(bo.getAttachmentList())){
+            List<OrderAttachment> attachmentList = bo.getAttachmentList().stream().map(x -> {
+                OrderAttachment attachment = new OrderAttachment();
+                attachment.setOrderSn(out_trade_no);
+                attachment.setAttachmentUrl(x.getAttachmentUrl());
+                attachment.setAttachmentType(x.getAttachmentType());
+                attachment.setStatus(1);
+                attachment.setCreateTime(DateUtils.getNowTime());
+                attachment.setUpdateTime(DateUtils.getNowTime());
+                return attachment;
+            }).collect(Collectors.toList());
+            iOrderAttachmentService.saveBatch(attachmentList);
+        }
+
+        String body = "在线支付";
+        Map<String,String> result = new HashMap<>();
+        //支付方式
+        if (bo.getOrderType() == 1 ){
+            //对公
+            order.setOrderUncollected(payPrice);
+        }else if (bo.getOrderType() == 2){
+            order.setOrderUncollected(payPrice);
+            //微信支付
+            if (payPrice.compareTo(BigDecimal.ZERO) > 0){
+                result = iWxPayService.paymentPc(out_trade_no, body, payPrice);
+            }
+        }else if (bo.getOrderType() == 3){
+            //线下
+            //未付
+            order.setOrderReceived(bo.getOrderReceived());
+            BigDecimal subtract = payPrice.subtract(bo.getOrderReceived());
+            order.setOrderUncollected(subtract);
+        }
+        order.setOrderType(bo.getOrderType());
+        save(order);
+
+        result.put("orderSn", out_trade_no);
+        return result;
+    }
+
+
+    @Override
+    public TableDataInfo<OrderListVo> getOrderList(OrderQueryBo bo) {
+        IPage<OrderListVo>  page = baseMapper.getOrderList(new Page<>(bo.getPageNum(), bo.getPageSize()), bo);
+        if (CollectionUtils.isEmpty(page.getRecords())){
+            return new TableDataInfo<OrderListVo>().setRows(new ArrayList<>());
+        }
+
+        page.getRecords().forEach(x -> {
+            List<OrderPerson> personList = iOrderPersonService.list(new LambdaQueryWrapper<OrderPerson>().eq(OrderPerson::getOrderSn, x.getOrderSn()).eq(OrderPerson::getStatus, 1));
+            //人数和订单数
+            Set<Long> userNum = personList.stream().map(OrderPerson::getPersonId).collect(Collectors.toSet());
+            x.setOrderPersonNum(userNum.size());
+            x.setOrderNum(personList.size());
+        });
+        TableDataInfo<OrderListVo> info = new TableDataInfo<>();
+        info.setTotal(page.getTotal());
+        info.setRows(page.getRecords());
+        return info;
+    }
+
+
+    @Override
+    public Integer queryByOrderSn(String orderSn) {
+        Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, orderSn));
+        if (Objects.isNull(order)){
+            return 0;
+        }
+        return order.getOrderStatus();
+    }
+
+    @Override
+    public OrderDetailVo getOrderDetail(String orderSn) {
+        OrderDetailVo detailVo = baseMapper.getOrderDetail(orderSn);
+        if (Objects.isNull(detailVo)){
+            throw new CustomException("订单获取有误!");
+        }
+        //订单学员
+        List<OrderPerson> orderPeople = iOrderPersonService
+                .list(new LambdaQueryWrapper<OrderPerson>().eq(OrderPerson::getOrderSn, orderSn));
+        if (CollectionUtils.isNotEmpty(orderPeople)){
+            List<OrderPersonVo> personVos = orderPeople.stream().map(x -> {
+                        OrderPersonVo bean = BeanUtil.toBean(x, OrderPersonVo.class);
+                        bean.setIdCard(x.getPersonCard());
+                        bean.setPhone(x.getPersonPhone());
+                        bean.setGender(getUserSex(x.getPersonCard()));
+                        return bean;
+                    }
+                    ).collect(Collectors.toList());
+            detailVo.setOrderPersonList(personVos);
+            //人数和订单数
+            Set<Long> userNum = orderPeople.stream().map(OrderPerson::getPersonId).collect(Collectors.toSet());
+            detailVo.setOrderPersonNum(userNum.size());
+            detailVo.setOrderNum(orderPeople.size());
+        }
+        //订单附件
+        List<OrderAttachment> attachmentList = iOrderAttachmentService
+                .list(new LambdaQueryWrapper<OrderAttachment>().eq(OrderAttachment::getOrderSn, orderSn));
+        if (CollectionUtils.isNotEmpty(attachmentList)){
+            List<OrderAttachmentVo> attachmentVos = attachmentList.stream().map(x -> BeanUtil.toBean(x, OrderAttachmentVo.class)).collect(Collectors.toList());
+            detailVo.setOrderAttachmentVos(attachmentVos);
+        }
+        return detailVo;
+    }
+
+    private String getUserSex(String idNumber){
+        if (idNumber == null || (idNumber.length() != 18 && idNumber.length() != 15)) {
+            return "男";
+        }
+        int index = idNumber.length() == 18 ? 16 : 14;
+        char genderCode = idNumber.charAt(index);
+        return  (genderCode % 2 == 0) ? "女" : "男";
+    }
+
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean uploadAttachment(OrderUploadAttachBo bo) {
+        Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, bo.getOrderSn()));
+        if (ObjectUtils.isNull(order)){
+            throw new CustomException("订单号有误!");
+        }
+        //上传附件
+        if (CollectionUtils.isEmpty(bo.getAttachmentList())){
+            throw new CustomException("附件为空");
+        }
+        List<OrderAttachment> attachments = bo.getAttachmentList().stream().map(x -> {
+            OrderAttachment attachment = BeanUtil.toBean(x, OrderAttachment.class);
+            attachment.setOrderSn(bo.getOrderSn());
+            attachment.setStatus(1);
+            attachment.setCreateTime(DateUtils.getNowTime());
+            attachment.setUpdateTime(DateUtils.getNowTime());
+            return attachment;
+        }).collect(Collectors.toList());
+
+        return iOrderAttachmentService.saveBatch(attachments);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean invoiceApply(OrderInvoiceCheckBo bo) {
+        Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, bo.getOrderSn()));
+        if (ObjectUtils.isNull(order)){
+            throw new CustomException("订单号有误!");
+        }
+        if (order.getCheckStatus() != 1){
+            throw new CustomException("订单未审核通过,请检查!");
+        }
+        if (order.getInvoiceStatus() == 2){
+            throw new CustomException("当前订单已申请发票,请等待后台审核");
+        }
+
+        //新增发票信息
+        OrderInvoice invoice = BeanUtil.toBean(bo, OrderInvoice.class);
+        //待审核
+        invoice.setAmount(order.getPayPrice());
+        invoice.setPeriodStatus(1);
+        invoice.setInvoiceStatus(1); //未开票
+        invoice.setApplyTime(DateUtils.getNowTime());
+        invoice.setStatus(1);
+        invoice.setCreateTime(DateUtils.getNowTime());
+        invoice.setUpdateTime(DateUtils.getNowTime());
+        iOrderInvoiceService.save(invoice);
+
+        order.setInvoiceStatus(2);
+        return updateById(order);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean refundApply(RefundApplyBo bo) {
+        Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, bo.getOrderSn()));
+        if (ObjectUtils.isNull(order)){
+            throw new CustomException("订单号有误!");
+        }
+        if (order.getCheckStatus() != 1){
+            throw new CustomException("订单未审核通过,请检查!");
+        }
+        if (ObjectUtils.isNotNull(order.getRefundStatus()) && order.getRefundStatus() == 0){
+            throw new CustomException("该订单已有申请退款,请等待审核!");
+        }
+        OrderRefund orderRefund = new OrderRefund();
+        //退款单号
+        String refundSn = DateUtils.getDateOrderSn();
+
+        orderRefund.setRefundSn(refundSn);
+        orderRefund.setOrderSn(order.getOrderSn());
+        orderRefund.setUserId(bo.getUserId());
+        orderRefund.setCompanyId(bo.getCompanyId());
+        orderRefund.setApplyReason(bo.getApplyReason());
+        //待审核
+        orderRefund.setPeriodStatus(0);
+        orderRefund.setPayee(bo.getPayee());
+        orderRefund.setPayeeBank(bo.getPayeeBank());
+        orderRefund.setPayeeBankAccount(bo.getPayeeBankAccount());
+        orderRefund.setStatus(1);
+        orderRefund.setCreateTime(DateUtils.getNowTime());
+        orderRefund.setUpdateTime(DateUtils.getNowTime());
+
+        //退款金额
+        if (StringUtils.isNotEmpty(bo.getOrderPersonIds())){
+            List<Long> personIds = Arrays.stream(bo.getOrderPersonIds().split(",")).map(x -> Long.valueOf(x)).collect(Collectors.toList());
+            List<OrderPerson> orderPeople = iOrderPersonService.list(new LambdaQueryWrapper<OrderPerson>().eq(OrderPerson::getStatus,1).in(OrderPerson::getOrderPersonId,personIds));
+            BigDecimal refundPrice = orderPeople.stream().map(OrderPerson::getRealPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
+            orderRefund.setRefundFee(refundPrice);
+            orderRefund.setOrderPersonIds(bo.getOrderPersonIds());
+            orderPeople.forEach(x -> x.setRefundStatus(1));
+            iOrderPersonService.updateBatchById(orderPeople);
+            //补充订单退款金额
+            if (ObjectUtils.isNull(order.getOrderRefund())){
+                order.setOrderRefund(refundPrice);
+            }else {
+                order.setOrderRefund(order.getOrderPrice().add(refundPrice));
+            }
+        }else {
+            //全部
+            List<OrderPerson> list = iOrderPersonService.list(new LambdaQueryWrapper<OrderPerson>()
+                    .eq(OrderPerson::getOrderSn, bo.getOrderSn())
+                    .notIn(OrderPerson::getRefundStatus, Arrays.asList(1, 2))
+                    .eq(OrderPerson::getStatus, 1));
+            if (CollectionUtils.isEmpty(list)){
+                throw new CustomException("该订单已全部退款,请检查");
+            }
+            BigDecimal refundPrice2 = list.stream().map(OrderPerson::getRealPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
+            orderRefund.setRefundFee(refundPrice2);
+            list.forEach(x -> x.setRefundStatus(1));
+            iOrderPersonService.updateBatchById(list);
+            //补充订单退款金额
+            if (ObjectUtils.isNull(order.getOrderRefund())){
+                order.setOrderRefund(refundPrice2);
+            }else {
+                order.setOrderRefund(order.getOrderPrice().add(refundPrice2));
+            }
+        }
+
+        //修改订单状态
+        order.setRefundStatus(0);
+        updateById(order);
+        return iOrderRefundService.save(orderRefund);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean edit(OrderEditBo bo) {
+        Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, bo.getOrderSn()));
+        if (ObjectUtils.isNull(order)){
+            throw new CustomException("订单号有误!");
+        }
+        if (order.getCheckStatus() == 1){
+            throw new CustomException("该订单已审核,请勿修改!");
+        }
+        if (CollectionUtils.isEmpty(bo.getOrderPersonList())){
+            throw new CustomException("订单员工信息未空,请检查!");
+        }
+
+        for (OrderPersonVo personVo : bo.getOrderPersonList()) {
+            OrderPerson orderPerson= iOrderPersonService.getById(personVo.getOrderPersonId());
+            //校验身份证
+            SysPerson person = iSysPersonService.getOne(new LambdaQueryWrapper<SysPerson>()
+                    .eq(SysPerson::getIdCard,EncryptHandler.encrypt(personVo.getIdCard()))
+                    .eq(SysPerson::getCompanyId,order.getCompanyId()).last("limit 1"));
+
+            if(Validator.isNull(person)){
+                SysPerson sysPerson = new SysPerson();
+                sysPerson.setPersonCode(ServletUtils.getEncoded("YG"));
+                sysPerson.setCompanyId(order.getCompanyId());
+                sysPerson.setPersonName(personVo.getPersonName());
+                sysPerson.setGender(getUserSex(personVo.getIdCard()));
+                sysPerson.setIdCard(personVo.getIdCard());
+                sysPerson.setPhone(personVo.getPhone());
+                sysPerson.setStatus(1);
+                sysPerson.setCreateTime(DateUtils.getNowTime());
+                sysPerson.setUpdateTime(DateUtils.getNowTime());
+                iSysPersonService.save(sysPerson);
+                person = iSysPersonService.getById(sysPerson.getPersonId());
+
+            }
+            orderPerson.setPersonId(person.getPersonId());
+            orderPerson.setPersonName(person.getPersonName());
+            orderPerson.setPersonCard(EncryptHandler.decrypt(person.getIdCard()));
+            orderPerson.setPersonPhone(EncryptHandler.decrypt(person.getPhone()));
+            orderPerson.setYear(personVo.getYear());
+            orderPerson.setSubjectName(personVo.getSubjectName());
+            iOrderPersonService.updateById(orderPerson);
+        }
+
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean revocation(OrderRevocationBo bo) {
+        Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, bo.getOrderSn()));
+        if (ObjectUtils.isNull(order)){
+            throw new CustomException("订单号有误!");
+        }
+        if (order.getCheckStatus() == 1){
+            throw new CustomException("该订单已审核,请勿修改!");
+        }
+        if (order.getStatus() != 1){
+            throw new CustomException("当前订单已关闭,请勿重复关闭");
+        }
+
+        //订单员工
+        iOrderPersonService.update(new LambdaUpdateWrapper<OrderPerson>()
+                .set(OrderPerson::getStatus,0)
+                .eq(OrderPerson::getOrderSn,bo.getOrderSn()));
+
+        order.setStatus(0);
+        return updateById(order);
+    }
+
+    @Override
+    public TableDataInfo<OrderListVo> getFinanceOrderList(OrderQueryBo bo) {
+        IPage<OrderListVo>  page = baseMapper.getFinanceOrderList(new Page<>(bo.getPageNum(), bo.getPageSize()), bo);
+        if (CollectionUtils.isEmpty(page.getRecords())){
+            return new TableDataInfo<OrderListVo>().setRows(new ArrayList<>());
+        }
+
+        page.getRecords().forEach(x -> {
+            List<OrderPerson> personList = iOrderPersonService.listPersonBySn(x.getOrderSn());
+            //人数和订单数
+            Set<Long> userNum = personList.stream().map(OrderPerson::getPersonId).collect(Collectors.toSet());
+            x.setOrderPersonNum(userNum.size());
+            x.setOrderNum(personList.size());
+        });
+        TableDataInfo<OrderListVo> info = new TableDataInfo<>();
+        info.setTotal(page.getTotal());
+        info.setRows(page.getRecords());
+        return info;
+    }
+
+    @Override
+    public OrderDetailVo getFinanceOrderDetail(String orderSn) {
+        OrderDetailVo detailVo = baseMapper.getFinanceOrderDetail(orderSn);
+        if (Objects.isNull(detailVo)){
+            throw new CustomException("订单获取有误!");
+        }
+        //订单学员
+        List<OrderPerson> orderPeople = iOrderPersonService.listPersonBySn(orderSn);
+        if (CollectionUtils.isNotEmpty(orderPeople)){
+            List<OrderPersonVo> personVos = orderPeople.stream().map(x -> {
+                        OrderPersonVo bean = BeanUtil.toBean(x, OrderPersonVo.class);
+                        bean.setIdCard(x.getPersonCard());
+                        bean.setPhone(x.getPersonPhone());
+                        bean.setGender(getUserSex(x.getPersonCard()));
+                        return bean;
+                    }
+            ).collect(Collectors.toList());
+            detailVo.setOrderPersonList(personVos);
+            //人数和订单数
+            Set<Long> userNum = orderPeople.stream().map(OrderPerson::getPersonId).collect(Collectors.toSet());
+            detailVo.setOrderPersonNum(userNum.size());
+            detailVo.setOrderNum(orderPeople.size());
+        }
+        //订单附件
+        List<OrderAttachment> attachmentList = iOrderAttachmentService.listByOrderSn(orderSn);
+        if (CollectionUtils.isNotEmpty(attachmentList)){
+            List<OrderAttachmentVo> attachmentVos = attachmentList.stream().map(x -> BeanUtil.toBean(x, OrderAttachmentVo.class)).collect(Collectors.toList());
+            detailVo.setOrderAttachmentVos(attachmentVos);
+        }
+        return detailVo;
+    }
+
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean checkOrder(OrderCheckBo bo) {
+        if (!bo.getIsAdmin()){
+            //校验审核角色
+            List<SysUserRole> sysUserRoles = sysUserRoleMapper
+                    .selectList(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, bo.getUserId()));
+            if (CollectionUtils.isEmpty(sysUserRoles)){
+                throw new CustomException("当前账号没有审核权限");
+            }
+            List<SysRole> sysRoles = iSysRoleService.listByIds(sysUserRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList()));
+            if (!sysRoles.stream().anyMatch(x -> x.getRoleName().contains("审核员"))){
+                throw new CustomException("当前账号没有审核权限");
+            }
+        }
+        OrderDetailVo detailVo = baseMapper.getFinanceOrderDetail(bo.getOrderSn());
+        if (Objects.isNull(detailVo)){
+            throw new CustomException("订单获取有误!");
+        }
+
+        if (detailVo.getCheckStatus() != 0){
+            throw new CustomException("当前订单状态不在待审核,请检查!");
+        }
+
+        if (detailVo.getStatus() == 0){
+            throw new CustomException("当前订单已关闭,请检查!");
+        }
+
+        //机构
+        ServletUtils.getResponse().setHeader("tenantId",detailVo.getTenantId().toString());
+
+        //修改审核状态
+        update(new LambdaUpdateWrapper<Order>().set(Order::getCheckStatus,bo.getCheckStatus()).eq(Order::getOrderSn,bo.getOrderSn()));
+        iOrderPersonService.update(new LambdaUpdateWrapper<OrderPerson>().set(OrderPerson::getPayStatus,3).eq(OrderPerson::getOrderSn,bo.getOrderSn()).eq(OrderPerson::getStatus,1));
+
+        return true;
+    }
+
+
+    @Override
+    public OrderInvoiceVo getOrderInvoiceApply(String orderSn) {
+        OrderDetailVo detailVo = baseMapper.getFinanceOrderDetail(orderSn);
+        if (Objects.isNull(detailVo)){
+            throw new CustomException("订单获取有误!");
+        }
+        //发票申请信息
+        return baseMapper.getOrderInvoiceApply(orderSn);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean invoiceUpload(InvoiceUploadBo bo) {
+        if (CollectionUtils.isEmpty(bo.getInvoiceUrl())){
+            throw new CustomException("发票地址为空,请检查");
+        }
+        //订单发票信息
+        OrderInvoice invoice = iOrderInvoiceService.getOrderInvoiceById(bo.getInvoiceId());
+        if (Objects.isNull(invoice)){
+            throw new CustomException("发票信息有误");
+        }
+        if (invoice.getPeriodStatus() != 3){
+            throw new CustomException("当前发票审核未通过");
+        }
+        //机构
+        ServletUtils.getResponse().setHeader("tenantId",invoice.getTenantId().toString());
+
+        String url = bo.getInvoiceUrl().stream().collect(Collectors.joining(","));
+        invoice.setInvoiceUrl(url);
+        invoice.setInvoiceStatus(2);
+        iOrderInvoiceService.updateById(invoice);
+
+        //补充发票附件
+        List<OrderAttachment> attachments = bo.getInvoiceUrl().stream().map(x -> {
+            OrderAttachment attachment = new OrderAttachment();
+            attachment.setAttachmentType(3);
+            attachment.setAttachmentUrl(x);
+            attachment.setStatus(1);
+            attachment.setOrderSn(invoice.getOrderSn());
+            attachment.setCreateTime(DateUtils.getNowTime());
+            attachment.setUpdateTime(DateUtils.getNowTime());
+            return attachment;
+        }).collect(Collectors.toList());
+
+        iOrderAttachmentService.saveBatch(attachments);
+
+        return update(new LambdaUpdateWrapper<Order>()
+                .set(Order::getInvoiceStatus,1)
+                .eq(Order::getOrderSn,invoice.getOrderSn()));
+    }
+
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean invoiceCheckOrder(OrderCheckBo bo) {
+        if (!bo.getIsAdmin()){
+            //校验审核角色
+            List<SysUserRole> sysUserRoles = sysUserRoleMapper
+                    .selectList(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, bo.getUserId()));
+            if (CollectionUtils.isEmpty(sysUserRoles)){
+                throw new CustomException("当前账号没有审核权限");
+            }
+            List<SysRole> sysRoles = iSysRoleService.listByIds(sysUserRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList()));
+            if (!sysRoles.stream().anyMatch(x -> x.getRoleName().contains("审核员"))){
+                throw new CustomException("当前账号没有审核权限");
+            }
+        }
+
+        //订单发票信息
+        OrderInvoice invoice = iOrderInvoiceService.getOrderInvoiceById(bo.getInvoiceId());
+        if (Objects.isNull(invoice)){
+            throw new CustomException("发票信息有误");
+        }
+        if (invoice.getPeriodStatus() != 1){
+            throw new CustomException("当前发票审核状态未在待审核,请检查!");
+        }
+
+        OrderDetailVo detailVo = baseMapper.getFinanceOrderDetail(invoice.getOrderSn());
+        if (Objects.isNull(detailVo)){
+            throw new CustomException("订单获取有误!");
+        }
+
+        //机构
+        ServletUtils.getResponse().setHeader("tenantId",invoice.getTenantId().toString());
+        switch (bo.getInvoiceStatus()){
+            case 2: //驳回
+                invoice.setPeriodStatus(2);
+                iOrderInvoiceService.updateById(invoice);
+                update(new LambdaUpdateWrapper<Order>().set(Order::getInvoiceStatus,4).eq(Order::getOrderSn,invoice.getOrderSn()));
+                break;
+            case 3: //通过
+                invoice.setPeriodStatus(3);
+                iOrderInvoiceService.updateById(invoice);
+                update(new LambdaUpdateWrapper<Order>().set(Order::getInvoiceStatus,3).eq(Order::getOrderSn,invoice.getOrderSn()));
+                break;
+            default: throw new CustomException("审核状态不匹配");
+        }
+
+        return true;
+    }
+
+    @Override
+    public OrderRefundDetailVo getOrderRefundDetail(String orderSn) {
+        OrderDetailVo detailVo = baseMapper.getFinanceOrderDetail(orderSn);
+        if (Objects.isNull(detailVo)){
+            throw new CustomException("订单获取有误!");
+        }
+        //退款详情
+        OrderRefund refund = iOrderRefundService.getRefundBySn(orderSn);
+        if (ObjectUtils.isNull(refund)){
+            throw new CustomException("退款信息有误");
+        }
+
+        OrderRefundDetailVo refundDetailVo = BeanUtil.toBean(detailVo, OrderRefundDetailVo.class);
+        refundDetailVo.setRefundId(refund.getRefundId());
+        refundDetailVo.setRefundSn(refund.getRefundSn());
+        refundDetailVo.setRefundFee(refund.getRefundFee());
+        refundDetailVo.setApplyReason(refund.getApplyReason());
+        refundDetailVo.setPayee(refund.getPayee());
+        refundDetailVo.setPayeeBank(refund.getPayeeBank());
+        refundDetailVo.setPayeeBankAccount(refund.getPayeeBankAccount());
+        refundDetailVo.setPeriodStatus(refund.getPeriodStatus());
+
+        //退款员工信息
+        if (StringUtils.isNotEmpty(refund.getOrderPersonIds())){
+            //机构
+            ServletUtils.getResponse().setHeader("tenantId",refund.getTenantId().toString());
+
+            List<OrderPerson> orderPeople = iOrderPersonService.listByIds(Arrays.asList(refund.getOrderPersonIds()));
+            if (CollectionUtils.isNotEmpty(orderPeople)){
+                List<OrderPersonVo> collect = orderPeople.stream().map(x -> {
+                    OrderPersonVo personVo = BeanUtil.toBean(x, OrderPersonVo.class);
+                    personVo.setGender(getUserSex(x.getPersonCard()));
+                    return personVo;
+                }).collect(Collectors.toList());
+                refundDetailVo.setOrderPersonList(collect);
+            }
+        }
+        return refundDetailVo;
+    }
+
+    @Override
+    public Boolean refundCheckOrder(OrderCheckBo bo) {
+        if (!bo.getIsAdmin()){
+            //校验审核角色
+            List<SysUserRole> sysUserRoles = sysUserRoleMapper
+                    .selectList(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, bo.getUserId()));
+            if (CollectionUtils.isEmpty(sysUserRoles)){
+                throw new CustomException("当前账号没有审核权限");
+            }
+            List<SysRole> sysRoles = iSysRoleService.listByIds(sysUserRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList()));
+            if (!sysRoles.stream().anyMatch(x -> x.getRoleName().contains("审核员"))){
+                throw new CustomException("当前账号没有审核权限");
+            }
+        }
+
+        OrderDetailVo detailVo = baseMapper.getFinanceOrderDetail(bo.getOrderSn());
+        if (Objects.isNull(detailVo)){
+            throw new CustomException("订单获取有误!");
+        }
+
+        if (detailVo.getRefundStatus() != 0){
+            throw new CustomException("当前订单退款状态未在待审核!");
+        }
+
+        //机构
+        ServletUtils.getResponse().setHeader("tenantId",detailVo.getTenantId().toString());
+        if (bo.getRefundStatus() == 2){
+            //审核不通过
+            OrderRefund orderRefund = iOrderRefundService.getById(bo.getRefundId());
+            orderRefund.setPeriodStatus(2);//未通过
+            orderRefund.setPeriodTime(DateUtils.getNowTime());
+
+            if (StringUtils.isNotEmpty(orderRefund.getOrderPersonIds())){
+                List<OrderPerson> orderPeople = iOrderPersonService.listByIds(Arrays.asList(orderRefund.getOrderPersonIds()));
+                orderPeople.forEach(x -> x.setRefundStatus(3));//拒绝退款
+                iOrderPersonService.updateBatchById(orderPeople);
+            }
+            orderRefund.setPeriodReason(bo.getPeriodReason());
+            iOrderRefundService.updateById(orderRefund);
+
+            Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, detailVo.getOrderSn()));
+            order.setRefundStatus(1);//未通过
+            //退款金额
+            order.setOrderRefund(order.getOrderRefund().subtract(orderRefund.getRefundFee()));
+            updateById(order);
+        }else if (detailVo.getRefundStatus() == 1){
+            //通过
+
+        }
+        return true;
+    }
+
+
+}

+ 137 - 0
src/main/java/com/zhongzheng/service/impl/SysPersonServiceImpl.java

@@ -11,6 +11,8 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zhongzheng.bo.ImportPersonBo;
+import com.zhongzheng.bo.OrderPersonBo;
+import com.zhongzheng.bo.UserImportAddBo;
 import com.zhongzheng.common.filter.CustomException;
 import com.zhongzheng.common.model.TableDataInfo;
 import com.zhongzheng.common.util.DateUtils;
@@ -23,6 +25,7 @@ import com.zhongzheng.mapper.SysPersonMapper;
 import com.zhongzheng.service.ISysCompanyService;
 import com.zhongzheng.service.ISysCustomerSeaService;
 import com.zhongzheng.service.ISysPersonService;
+import com.zhongzheng.vo.OrderPersonVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -293,6 +296,140 @@ public class SysPersonServiceImpl extends ServiceImpl<SysPersonMapper, SysPerson
 
     }
 
+    @Override
+    public Map<String, Object> importUser(List<UserImportAddBo> userList, Long companyId) {
+        if (CollectionUtils.isEmpty(userList)) {
+            throw new IllegalArgumentException("请勿导入空表格");
+        }
+        List<UserImportAddBo> errorList = new ArrayList<>();
+        List<UserImportAddBo> successList = new ArrayList<>();
+        for (UserImportAddBo itemImport : userList) {
+            if(Validator.isEmpty(itemImport.getTelphone())||itemImport.getTelphone().length()!=11){
+                itemImport.setCause("手机号不能为空或者格式不对");
+                errorList.add(itemImport);
+                continue;
+            }
+            String REGEX = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])\\d{3}([0-9Xx])$";
+            String REGEX2 = "^(H|K|A)?\\d{6}(\\([A-Z0-9]\\)|\\d{2})(\\d|[A-Z])?$|^M\\d{6}(\\([A-Z]\\)|\\d)?$";
+            if(Validator.isEmpty(itemImport.getIdCard()) || (!itemImport.getIdCard().matches(REGEX) && !itemImport.getIdCard().matches(REGEX2))){
+                itemImport.setCause("身份证不能为空或者格式不对");
+                errorList.add(itemImport);
+                continue;
+            }
+            if(Validator.isEmpty(itemImport.getRealname())){
+                itemImport.setCause("真实姓名不能为空");
+                errorList.add(itemImport);
+                continue;
+            }
+            //科目
+            if(Validator.isEmpty(itemImport.getSubjectNames())){
+                itemImport.setCause("科目不能为空");
+                errorList.add(itemImport);
+                continue;
+            }
+
+            SysPerson user = getOne(new LambdaQueryWrapper<SysPerson>()
+                    .eq(SysPerson::getIdCard,EncryptHandler.encrypt(itemImport.getIdCard()))
+                    .eq(SysPerson::getCompanyId,companyId).last("limit 1"));
+            if(Validator.isNotNull(user)){
+                //身份证已存在
+                itemImport.setUserId(user.getPersonId());
+                itemImport.setTelphone(EncryptHandler.decrypt(user.getPhone()));
+                itemImport.setRealname(user.getPersonName());
+                itemImport.setIdCard(EncryptHandler.decrypt(user.getIdCard()));
+                successList.add(itemImport);
+                continue;
+            } else{
+                SysPerson user1 = getOne(new LambdaQueryWrapper<SysPerson>()
+                        .eq(SysPerson::getPhone,EncryptHandler.encrypt(itemImport.getTelphone()))
+                        .eq(SysPerson::getCompanyId,companyId).last("limit 1"));
+                if(Validator.isNotNull(user1)) {
+                    if(Validator.isNotEmpty(user1.getIdCard())&&Validator.isNotEmpty(itemImport.getIdCard())&&!itemImport.getIdCard().equals(user1.getIdCard())){
+                        itemImport.setCause("手机号码已被占用请检查");
+                        errorList.add(itemImport);
+                        continue;
+                    }
+                }
+            }
+
+            SysPerson inertData = new SysPerson();
+            inertData.setPersonCode(ServletUtils.getEncoded("YG"));
+            inertData.setCompanyId(companyId);
+            inertData.setIdCard(itemImport.getIdCard());
+            inertData.setPersonName(itemImport.getRealname());
+            inertData.setPhone(itemImport.getTelphone());
+            inertData.setGender(getUserSex(itemImport.getIdCard()));
+            inertData.setStatus(1);
+            inertData.setEducation(itemImport.getEduLevel());
+            inertData.setCreateTime(DateUtils.getNowTime());
+            inertData.setUpdateTime(DateUtils.getNowTime());
+
+            if(!save(inertData)){
+                itemImport.setCause("注册失败");
+                errorList.add(itemImport);
+                continue;
+            }
+            successList.add(itemImport);
+        }
+        Map<String,Object> resultMap = new HashMap<>();
+        resultMap.put("errorList",errorList);
+        resultMap.put("successList",successList);
+        return resultMap;
+    }
+
+    private String getUserSex(String idNumber){
+        if (idNumber == null || (idNumber.length() != 18 && idNumber.length() != 15)) {
+            return "男";
+        }
+        int index = idNumber.length() == 18 ? 16 : 14;
+        char genderCode = idNumber.charAt(index);
+        return  (genderCode % 2 == 0) ? "女" : "男";
+    }
+
+    @Override
+    public OrderPersonVo addOrderPerson(OrderPersonBo bo) {
+        SysPerson user = getOne(new LambdaQueryWrapper<SysPerson>()
+                .eq(SysPerson::getIdCard,EncryptHandler.encrypt(bo.getIdCard()))
+                .eq(SysPerson::getCompanyId,bo.getCompanyId()).last("limit 1"));
+        OrderPersonVo personVo = new OrderPersonVo();
+        if(Validator.isNotNull(user)){
+            //身份证已存在
+            personVo.setPersonId(user.getPersonId());
+            personVo.setPhone(EncryptHandler.decrypt(user.getPhone()));
+            personVo.setPersonName(user.getPersonName());
+            personVo.setIdCard(EncryptHandler.decrypt(user.getIdCard()));
+            personVo.setGender(getUserSex(bo.getIdCard()));
+            return personVo;
+        } else{
+            SysPerson user1 = getOne(new LambdaQueryWrapper<SysPerson>()
+                    .eq(SysPerson::getPhone,EncryptHandler.encrypt(bo.getTelphone()))
+                    .eq(SysPerson::getCompanyId,bo.getCompanyId()).last("limit 1"));
+            if(Validator.isNotNull(user1)) {
+                throw new CustomException("手机号码已被占用请检查");
+            }
+        }
+
+        SysPerson inertData = new SysPerson();
+        inertData.setPersonCode(ServletUtils.getEncoded("YG"));
+        inertData.setCompanyId(bo.getCompanyId());
+        inertData.setIdCard(bo.getIdCard());
+        inertData.setPersonName(bo.getRealname());
+        inertData.setPhone(bo.getTelphone());
+        inertData.setGender(getUserSex(bo.getIdCard()));
+        inertData.setStatus(1);
+        inertData.setEducation(bo.getEduLevel());
+        inertData.setCreateTime(DateUtils.getNowTime());
+        inertData.setUpdateTime(DateUtils.getNowTime());
+        save(inertData);
+
+        personVo.setPersonId(inertData.getPersonId());
+        personVo.setPhone(bo.getTelphone());
+        personVo.setPersonName(bo.getRealname());
+        personVo.setIdCard(bo.getIdCard());
+        personVo.setGender(getUserSex(bo.getIdCard()));
+        return personVo;
+    }
+
 
     public  SysPerson getPersonByIdName(SysPerson model) {
 

+ 20 - 0
src/main/java/com/zhongzheng/service/impl/UserChannelServiceImpl.java

@@ -0,0 +1,20 @@
+package com.zhongzheng.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhongzheng.domian.Order;
+import com.zhongzheng.domian.OrderAttachment;
+import com.zhongzheng.domian.UserChannel;
+import com.zhongzheng.mapper.OrderAttachmentMapper;
+import com.zhongzheng.mapper.OrderMapper;
+import com.zhongzheng.mapper.UserChannelMapper;
+import com.zhongzheng.service.IOrderAttachmentService;
+import com.zhongzheng.service.IOrderService;
+import com.zhongzheng.service.IUserChannelService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public class UserChannelServiceImpl extends ServiceImpl<UserChannelMapper, UserChannel> implements IUserChannelService {
+}

+ 176 - 0
src/main/java/com/zhongzheng/service/impl/WxPayServiceImpl.java

@@ -0,0 +1,176 @@
+package com.zhongzheng.service.impl;
+
+import cn.hutool.core.lang.Validator;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+import com.zhongzheng.common.config.WxSmallConfig;
+import com.zhongzheng.common.croe.RedisCache;
+import com.zhongzheng.common.filter.CustomException;
+import com.zhongzheng.common.util.DateUtils;
+import com.zhongzheng.common.util.IpUtils;
+import com.zhongzheng.common.util.ServletUtils;
+import com.zhongzheng.common.util.wx.WXPay;
+import com.zhongzheng.common.util.wx.WXPayUtil;
+import com.zhongzheng.domian.Order;
+import com.zhongzheng.domian.OrderPerson;
+import com.zhongzheng.service.IOrderAttachmentService;
+import com.zhongzheng.service.IOrderPersonService;
+import com.zhongzheng.service.IOrderService;
+import com.zhongzheng.service.IWxPayService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.ByteArrayOutputStream;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Service
+public class WxPayServiceImpl implements IWxPayService {
+
+//    @Value("${wx.gzh.appid}")
+    private String appid;
+//    @Value("${wx.wepay.mchid}")
+    private String mchid;
+//    @Value("${wx.wepay.key}")
+    private String key;
+//    @Value("${wx.wepay.notifyUrl}")
+    private String notifyUrl;
+
+    @Autowired
+    private IOrderService iOrderService;
+
+    @Autowired
+    private IOrderPersonService iOrderPersonService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Override
+    public String paymentCallBack(String notifyData) {
+        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
+        try {
+            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);  // 转换成map
+            String TenantId = notifyMap.get("attach");
+            ServletUtils.getRequestAttributes().getResponse().setHeader("TenantId", TenantId);
+            WxSmallConfig config = new WxSmallConfig(appid, mchid, key);
+            WXPay wxpay = new WXPay(config);
+            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
+                String result_code = notifyMap.get("result_code");
+                if ("SUCCESS".equals(result_code)) {
+                    String out_trade_no = notifyMap.get("out_trade_no");
+                    String transaction_id = notifyMap.get("transaction_id");
+                    Order order = iOrderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, out_trade_no));
+                    if (ObjectUtils.isNotNull(order)) {
+                        if (dealOrder(out_trade_no, transaction_id)) {
+                            xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
+                            return xmlBack;
+                        }
+                    }
+                }
+            } else {
+                // 签名错误,如果数据里没有sign字段,也认为是签名错误
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return xmlBack;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean dealOrder(String out_trade_no, String transaction_id) {
+        String key = out_trade_no;
+        String ticket = redisCache.getCacheObject(key);
+        if (Validator.isNotEmpty(ticket)) {
+            return false;
+        }
+        redisCache.setCacheObject(key, "dealing", 30, TimeUnit.SECONDS);//30秒
+        Order order = iOrderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, out_trade_no));
+        if (ObjectUtils.isNotNull(order) && order.getOrderStatus() == 0) {
+            //已支付
+            order.setOrderStatus(1);
+
+            order.setPayTime(DateUtils.getNowTime());
+            order.setTransid(transaction_id);
+            order.setPayStatus(2);
+            order.setCheckStatus(1);
+            order.setOrderReceived(order.getPayPrice());
+            //未付
+            BigDecimal orderUncollected = order.getOrderUncollected();
+            if (orderUncollected.compareTo(BigDecimal.ZERO) > 0) {
+                BigDecimal subtract = order.getPayPrice().subtract(orderUncollected);
+                order.setOrderUncollected(subtract);
+            }
+            iOrderService.updateById(order);
+
+            //修改订单员工支付状态
+            iOrderPersonService.update(new LambdaUpdateWrapper<OrderPerson>()
+                            .set(OrderPerson::getPayStatus,3)//已收费
+                            .eq(OrderPerson::getOrderSn, out_trade_no)
+                            .eq(OrderPerson::getStatus,1));
+        }
+        return true;
+    }
+
+    @Override
+    public Map<String, String> paymentPc(String out_trade_no, String body, BigDecimal price) {
+
+        WxSmallConfig config = new WxSmallConfig(appid, mchid, key);
+        BigDecimal unit = new BigDecimal(100);
+        price = price.multiply(unit);
+        try {
+            WXPay wxpay = new WXPay(config);
+            Map<String, String> data = new HashMap<String, String>();
+            data.put("body", body);
+            data.put("out_trade_no", out_trade_no);
+            data.put("device_info", "");
+            data.put("fee_type", "CNY");
+            data.put("total_fee", price.longValue() + "");
+
+            data.put("spbill_create_ip", IpUtils.getIpAddr(ServletUtils.getRequest()));
+            data.put("notify_url", notifyUrl);
+            data.put("trade_type", "NATIVE");
+            Date date = new Date();
+            Date newDate = new Date(date.getTime() + 60000*30);
+
+            String TenantId = ServletUtils.getRequest().getHeader("TenantId");
+            data.put("attach", TenantId);
+            Map<String, String> resp = wxpay.unifiedOrder(data);
+
+            Map<String, String> result = new HashMap<>();
+            String code_url = resp.get("code_url");
+            if (Validator.isEmpty(code_url)) {
+                throw new CustomException("支付错误");
+            }
+            QRCodeWriter qrCodeWriter = new QRCodeWriter();
+            BitMatrix bitMatrix = qrCodeWriter.encode(code_url, BarcodeFormat.QR_CODE, 120, 120);
+            // 写到输出流
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            MatrixToImageWriter.writeToStream(bitMatrix, "jpg", outputStream);
+            //转换为base64
+            Base64.Encoder encoder1 = Base64.getEncoder();
+            String urlBase64 = "data:image/jpeg;base64,"
+                    + encoder1.encodeToString(outputStream.toByteArray());
+            result.put("urlBase64", urlBase64);
+            Long overTime = (long)(newDate.getTime()/1000);
+            result.put("overTime", overTime.toString());
+            return result;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+}

+ 59 - 0
src/main/java/com/zhongzheng/vo/ChannelDetailVo.java

@@ -0,0 +1,59 @@
+package com.zhongzheng.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("渠道新增参数")
+public class ChannelDetailVo implements Serializable {
+
+    @ApiModelProperty("id")
+    private Long channelId;
+    /** 渠道名称 */
+    @ApiModelProperty("渠道名称")
+    private String name;
+    /** 教育类型名称 */
+    @ApiModelProperty("教育类型名称")
+    private String eduName;
+    /** 业务层次名称 */
+    @ApiModelProperty("业务层次名称")
+    private String businessName;
+    /** 渠道内容 */
+    @ApiModelProperty("渠道内容")
+    private String content;
+    /** 渠道内容 */
+    @ApiModelProperty("封面地址")
+    private String coverUrl;
+    /** 单科价格 */
+    @ApiModelProperty("单科价格")
+    private BigDecimal price;
+    /** 活动价格 */
+    @ApiModelProperty("活动价格")
+    private BigDecimal activityPrice;
+    /** 渠道类型:1,默认类型 2满减 3特价活动 4商品礼包 */
+    @ApiModelProperty("渠道类型:1,默认类型 2满减 3特价活动 4商品礼包")
+    private Integer type;
+    /** 满额 */
+    @ApiModelProperty("满额")
+    private BigDecimal satisfyPrice;
+    /** 减额 */
+    @ApiModelProperty("减额")
+    private BigDecimal reducePrice;
+    /** 备注 */
+    @ApiModelProperty("备注")
+    private String remark;
+    /** 活动开始时间 */
+    @ApiModelProperty("活动开始时间")
+    private Long activityStart;
+    /** 活动结束时间 */
+    @ApiModelProperty("活动结束时间")
+    private Long activityEnd;
+    /** 1有效 0无效 */
+    @ApiModelProperty(" 1有效 0无效")
+    private Integer status;
+
+}

+ 27 - 0
src/main/java/com/zhongzheng/vo/OrderAttachmentVo.java

@@ -0,0 +1,27 @@
+package com.zhongzheng.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel("订单附件")
+public class OrderAttachmentVo implements Serializable {
+
+    private Long id;
+
+    @ApiModelProperty("订单编号")
+    private String orderSn;
+
+    @ApiModelProperty("附件地址")
+    private String attachmentUrl;
+
+    @ApiModelProperty(" 类型:1转账凭证,2附件 3发票 ")
+    private Integer attachmentType;
+
+    @ApiModelProperty("1有效 0无效")
+    private Integer status;
+
+}

+ 82 - 0
src/main/java/com/zhongzheng/vo/OrderDetailVo.java

@@ -0,0 +1,82 @@
+package com.zhongzheng.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@ApiModel("订单列表")
+public class OrderDetailVo implements Serializable {
+
+    @ApiModelProperty("业务名称")
+    private String businessName;
+
+    @ApiModelProperty("渠道名称")
+    private String channelName;
+
+    @ApiModelProperty("审核状态:0待审核,1已通过,2未通过,3已撤销")
+    private Integer checkStatus;
+
+    @ApiModelProperty("公司名称")
+    private String companyName;
+
+    @ApiModelProperty("创建时间")
+    private Long createTime;
+
+    @ApiModelProperty("教育类型")
+    private String eduName;
+
+    @ApiModelProperty("发票状态:0未开票,1已开票,2待审核,,3已审核,4审核未通过")
+    private Integer invoiceStatus;
+
+    @ApiModelProperty("订单数量")
+    private Integer orderNum;
+
+    @ApiModelProperty("订单人员数量")
+    private Integer orderPersonNum;
+
+    @ApiModelProperty("订单标准价格")
+    private BigDecimal orderPrice;
+
+    @ApiModelProperty("实收账款(元)")
+    private BigDecimal orderReceived;
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("订单状态 0待支付,1已支付 4完全退款")
+    private Integer orderStatus;
+
+    @ApiModelProperty("订单类型:1,对公 2,微信 3,线下")
+    private Integer orderType;
+
+    @ApiModelProperty("未收账款(元)")
+    private BigDecimal orderUncollected;
+
+    @ApiModelProperty("支付价格")
+    private BigDecimal payPrice;
+
+    @ApiModelProperty("支付时间/审核时间")
+    private Long payTime;
+
+    @ApiModelProperty("退款状态:0待审核,1未通过,2未退款,3已完成")
+    private Integer refundStatus;
+
+    @ApiModelProperty("1有效 0无效")
+    private Integer status;
+
+    @ApiModelProperty("用户名称")
+    private String userName;
+
+    @ApiModelProperty("订单学员集合")
+    private List<OrderPersonVo> orderPersonList;
+
+    @ApiModelProperty("订单附件集合")
+    private List<OrderAttachmentVo> orderAttachmentVos;
+
+    private Long tenantId;
+}

+ 66 - 0
src/main/java/com/zhongzheng/vo/OrderInvoiceVo.java

@@ -0,0 +1,66 @@
+package com.zhongzheng.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+public class OrderInvoiceVo implements Serializable {
+
+    @ApiModelProperty("发票ID")
+    private Long invoiceId;
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("类型 1普通发票 2 增值税专用发票")
+    private Integer type;
+
+    @ApiModelProperty("发票抬头")
+    private String invoiceTitle;
+
+    @ApiModelProperty("邮箱")
+    private String email;
+
+    @ApiModelProperty("审核状态 1待审核 2驳回 3通过 ")
+    private Integer periodStatus;
+
+    @ApiModelProperty("主体 1个人 2企业 ")
+    private Integer subject;
+
+    @ApiModelProperty("纳税登记号")
+    private String taxRegistryNumber;
+
+    @ApiModelProperty("发票金额")
+    private BigDecimal amount;
+
+    @ApiModelProperty("申请时间")
+    private Long applyTime;
+
+    @ApiModelProperty("公司地址")
+    private String companyAddress;
+
+    @ApiModelProperty("电话号码")
+    private String phone;
+
+    @ApiModelProperty("开户银行")
+    private String bankName;
+
+    @ApiModelProperty("银行账号")
+    private String bankAccount;
+
+    @ApiModelProperty("收件地址")
+    private String receivingAddress;
+
+    @ApiModelProperty("收件人姓名")
+    private String receivingName;
+
+    @ApiModelProperty("收件人手机号码")
+    private String receivingTel;
+
+    @ApiModelProperty("开票备注")
+    private String openRemark;
+
+}

+ 74 - 0
src/main/java/com/zhongzheng/vo/OrderListVo.java

@@ -0,0 +1,74 @@
+package com.zhongzheng.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("订单列表")
+public class OrderListVo implements Serializable {
+
+    @ApiModelProperty("业务名称")
+    private String businessName;
+
+    @ApiModelProperty("渠道名称")
+    private String channelName;
+
+    @ApiModelProperty("审核状态:0待审核,1已通过,2未通过,3已撤销")
+    private Integer checkStatus;
+
+    @ApiModelProperty("公司名称")
+    private String companyName;
+
+    @ApiModelProperty("创建时间")
+    private Long createTime;
+
+    @ApiModelProperty("教育类型")
+    private String eduName;
+
+    @ApiModelProperty("发票状态:0未开票,1已开票,2待审核,,3已审核,4审核未通过")
+    private Integer invoiceStatus;
+
+    @ApiModelProperty("订单数量")
+    private Integer orderNum;
+
+    @ApiModelProperty("订单人员数量")
+    private Integer orderPersonNum;
+
+    @ApiModelProperty("订单标准价格")
+    private BigDecimal orderPrice;
+
+    @ApiModelProperty("实收账款(元)")
+    private BigDecimal orderReceived;
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("订单状态 0待支付,1已支付 4完全退款")
+    private Integer orderStatus;
+
+    @ApiModelProperty("订单类型:1,对公 2,微信 3,线下")
+    private Integer orderType;
+
+    @ApiModelProperty("未收账款(元)")
+    private BigDecimal orderUncollected;
+
+    @ApiModelProperty("支付价格")
+    private BigDecimal payPrice;
+
+    @ApiModelProperty("支付时间/审核时间")
+    private Long payTime;
+
+    @ApiModelProperty("退款状态:0待审核,1未通过,2未退款,3已完成")
+    private Integer refundStatus;
+
+    @ApiModelProperty("1有效 0无效")
+    private Integer status;
+
+    @ApiModelProperty("用户名称")
+    private String userName;
+
+}

+ 71 - 0
src/main/java/com/zhongzheng/vo/OrderPersonVo.java

@@ -0,0 +1,71 @@
+package com.zhongzheng.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("渠道新增参数")
+public class OrderPersonVo implements Serializable {
+
+    @ApiModelProperty("")
+    private Long orderPersonId;
+    /** 订单编号 */
+    @ApiModelProperty("订单编号")
+    private String orderSn;
+    /** 标准价格 */
+    @ApiModelProperty("标准价格")
+    private BigDecimal price;
+    /** 成交价格 */
+    @ApiModelProperty("成交价格")
+    private BigDecimal realPrice;
+    /** 退款状态 0(无和学员端拒绝退款)  1退款中 2已退款 3拒绝退款 */
+    @ApiModelProperty("退款状态 0(无和学员端拒绝退款)  1退款中 2已退款 3拒绝退款")
+    private Integer refundStatus;
+    /** 支付状态 1未收费 2部分收费 3完全收费 4免费 (退款此状态保持不变) */
+    @ApiModelProperty("支付状态 1未收费 2部分收费 3完全收费 4免费 (退款此状态保持不变) ")
+    private Integer payStatus;
+    /** 教育类型 */
+    @ApiModelProperty("教育类型")
+    private String eduName;
+    /** 业务名称 */
+    @ApiModelProperty("业务名称")
+    private String businessName;
+    /** 科目名称 */
+    @ApiModelProperty("科目名称")
+    private String subjectName;
+    /** 年份 */
+    @ApiModelProperty("年份")
+    private String year;
+    /** 员工ID*/
+    @ApiModelProperty("员工ID")
+    private Long personId;
+    /**员工名称 */
+    @ApiModelProperty("员工名称")
+    private String personName;
+    /** 员工身份证 */
+    @ApiModelProperty("员工身份证")
+    private String idCard;
+
+    @ApiModelProperty("性别")
+    private String gender;
+    /** 员工手机号码 */
+    @ApiModelProperty("员工手机号码")
+    private String phone;
+
+    /** 状态 1正常 0关闭 */
+    @ApiModelProperty("")
+    private Integer status;
+    /** 添加时间 */
+    @ApiModelProperty("")
+    private Long createTime;
+    /** 修改时间 */
+    @ApiModelProperty("")
+    private Long updateTime;
+    /** 租户ID */
+    private Long tenantId;
+
+}

+ 109 - 0
src/main/java/com/zhongzheng/vo/OrderRefundDetailVo.java

@@ -0,0 +1,109 @@
+package com.zhongzheng.vo;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@ApiModel("渠道新增参数")
+public class OrderRefundDetailVo implements Serializable {
+
+
+    @ApiModelProperty("业务名称")
+    private String businessName;
+
+    @ApiModelProperty("渠道名称")
+    private String channelName;
+
+    @ApiModelProperty("审核状态:0待审核,1已通过,2未通过,3已撤销")
+    private Integer checkStatus;
+
+    @ApiModelProperty("公司名称")
+    private String companyName;
+
+    @ApiModelProperty("创建时间")
+    private Long createTime;
+
+    @ApiModelProperty("教育类型")
+    private String eduName;
+
+    @ApiModelProperty("发票状态:0未开票,1已开票,2待审核,,3已审核,4审核未通过")
+    private Integer invoiceStatus;
+
+    @ApiModelProperty("订单数量")
+    private Integer orderNum;
+
+    @ApiModelProperty("订单人员数量")
+    private Integer orderPersonNum;
+
+    @ApiModelProperty("订单标准价格")
+    private BigDecimal orderPrice;
+
+    @ApiModelProperty("实收账款(元)")
+    private BigDecimal orderReceived;
+
+    @ApiModelProperty("订单号")
+    private String orderSn;
+
+    @ApiModelProperty("订单状态 0待支付,1已支付 4完全退款")
+    private Integer orderStatus;
+
+    @ApiModelProperty("订单类型:1,对公 2,微信 3,线下")
+    private Integer orderType;
+
+    @ApiModelProperty("未收账款(元)")
+    private BigDecimal orderUncollected;
+
+    @ApiModelProperty("支付价格")
+    private BigDecimal payPrice;
+
+    @ApiModelProperty("支付时间/审核时间")
+    private Long payTime;
+
+    @ApiModelProperty("退款状态:0待审核,1未通过,2未退款,3已完成")
+    private Integer refundStatus;
+
+    @ApiModelProperty("1有效 0无效")
+    private Integer status;
+
+    @ApiModelProperty("用户名称")
+    private String userName;
+
+    @ApiModelProperty("订单学员集合")
+    private List<OrderPersonVo> orderPersonList;
+
+    @ApiModelProperty("订单附件集合")
+    private List<OrderAttachmentVo> orderAttachmentVos;
+
+    private Long tenantId;
+
+    @ApiModelProperty("退款ID")
+    private Long refundId;
+
+    @ApiModelProperty("退款号")
+    private String refundSn;
+
+    @ApiModelProperty("退款金额")
+    private BigDecimal refundFee;
+
+    @ApiModelProperty("申请原因")
+    private String applyReason;
+
+    @ApiModelProperty("收款人")
+    private String payee;
+
+    @ApiModelProperty("收款银行")
+    private String payeeBank;
+
+    @ApiModelProperty("收款账号")
+    private String payeeBankAccount;
+
+    @ApiModelProperty("审批状态 0 待审核 1 通过 2未通过")
+    private Integer periodStatus;
+
+}

+ 38 - 0
src/main/java/com/zhongzheng/vo/OrderSettlementVo.java

@@ -0,0 +1,38 @@
+package com.zhongzheng.vo;
+
+import com.zhongzheng.bo.OrderSettlementDetailBo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@ApiModel("订单结算")
+public class OrderSettlementVo implements Serializable {
+
+
+    @ApiModelProperty("渠道ID")
+    private Long channelId;
+
+    @ApiModelProperty("公司ID")
+    private Long companyId;
+
+    @ApiModelProperty("订单员工数量")
+    private Integer orderUserNum;
+
+    @ApiModelProperty("订单数量")
+    private Integer orderGoodsNum;
+
+    @ApiModelProperty("原价")
+    private BigDecimal originalPrice;
+
+    @ApiModelProperty("订单价格")
+    private BigDecimal orderPrice;
+
+    @ApiModelProperty("订单员工集合")
+    private List<OrderSettlementDetailBo> settlementList;
+
+}

+ 20 - 0
src/main/java/com/zhongzheng/vo/UserChannelVo.java

@@ -0,0 +1,20 @@
+package com.zhongzheng.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("获取渠道关联账号")
+public class UserChannelVo implements Serializable {
+
+    @ApiModelProperty("用户ID")
+    private Long userId;
+
+    @ApiModelProperty("用户名称")
+    private String userName;
+
+}

+ 0 - 0
src/main/java/com/zhongzheng/vo/relevance/BusinessVo.java


+ 0 - 0
src/main/java/com/zhongzheng/vo/relevance/ConfigGoodsDetailVo.java


+ 0 - 0
src/main/java/com/zhongzheng/vo/relevance/ConfigGoodsVo.java


+ 38 - 0
src/main/java/com/zhongzheng/vo/relevance/EducationTypeVo.java

@@ -0,0 +1,38 @@
+package com.zhongzheng.vo.relevance;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel("教育类型视图对象")
+public class EducationTypeVo implements Serializable {
+
+    @ApiModelProperty("id")
+    private Long id;
+
+    /** 教育类型名称 */
+    @ApiModelProperty("教育类型名称")
+    private String educationName;
+
+    /** 编码JY */
+    @ApiModelProperty("编码JY")
+    private String encoder;
+
+    /** 备注 */
+    @ApiModelProperty("备注")
+    private String remark;
+
+    /** 0 未启用 1启用 */
+    @ApiModelProperty("0 未启用 1启用")
+    private Integer status;
+
+    @ApiModelProperty("1,2,3,4 逗号隔开 代表层次1项目类型,2业务类型,3院校,4专业")
+    private String tireStatus;
+
+    /** 排序 */
+    @ApiModelProperty("排序")
+    private Integer sort;
+}

+ 0 - 0
src/main/java/com/zhongzheng/vo/relevance/GoodsBagVo.java


+ 0 - 0
src/main/java/com/zhongzheng/vo/relevance/GoodsBusinessVo.java


+ 67 - 0
src/main/java/com/zhongzheng/vo/relevance/GoodsVo.java

@@ -0,0 +1,67 @@
+package com.zhongzheng.vo.relevance;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("商品视图")
+public class GoodsVo implements Serializable {
+
+    @ApiModelProperty("id")
+    private Long goodsId;
+
+    @ApiModelProperty("年份")
+    private Long year;
+
+    @ApiModelProperty("商品类型 1视频2题库 3补考 4前培 ")
+    private Integer goodsType;
+
+    @ApiModelProperty("教育类型id")
+    private Long educationTypeId;
+
+    @ApiModelProperty("业务层次id")
+    private Long businessId;
+
+    @ApiModelProperty("所属专业")
+    private Long majorId;
+
+    @ApiModelProperty("商品名称")
+    private String goodsName;
+
+    @ApiModelProperty("标准价格")
+    private BigDecimal standPrice;
+
+    @ApiModelProperty("最低价格")
+    private BigDecimal lowestPrice;
+
+    @ApiModelProperty("状态 1有效 0无效")
+    private Integer status;
+
+    @ApiModelProperty("1上架 0未上架")
+    private Integer goodsStatus;
+
+    @ApiModelProperty("封面地址")
+    private String coverUrl;
+
+    @ApiModelProperty("项目ID")
+    private Long projectId;
+
+    @ApiModelProperty("教育名称")
+    private String educationName;
+
+    @ApiModelProperty("项目名称")
+    private String projectName;
+
+    @ApiModelProperty("业务名称")
+    private String businessName;
+
+    @ApiModelProperty("专业名称")
+    private String categoryName;
+
+    @ApiModelProperty("业务别名")
+    private String aliasName;
+}

+ 21 - 0
src/main/java/com/zhongzheng/vo/relevance/RelevanceInfoVo.java

@@ -0,0 +1,21 @@
+package com.zhongzheng.vo.relevance;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel("关联的机构信息")
+public class RelevanceInfoVo implements Serializable {
+
+    /** 关联的机构ID */
+    @ApiModelProperty("关联的机构ID")
+    private String relevanceId;
+
+    /** 机构名称 */
+    @ApiModelProperty("机构名称")
+    private String relevanceName;
+}

+ 0 - 0
src/main/java/com/zhongzheng/vo/relevance/SubjectMarketingVo.java


+ 32 - 0
src/main/java/com/zhongzheng/vo/relevance/SubjectVo.java

@@ -0,0 +1,32 @@
+package com.zhongzheng.vo.relevance;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel("科目视图对象")
+public class SubjectVo implements Serializable {
+
+
+    /** $pkColumn.columnComment */
+    @ApiModelProperty("id")
+    private Long id;
+
+    @ApiModelProperty("编码KM")
+    private String encoder;
+
+    @ApiModelProperty("备注")
+    private String remark;
+
+    @ApiModelProperty("0 未启用 1启用 -1删除")
+    private Integer status;
+
+    @ApiModelProperty("科目名称")
+    private String subjectName;
+
+    @ApiModelProperty("排序")
+    private Integer sort;
+}

+ 33 - 0
src/main/resources/mapper/ChannelMapper.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhongzheng.mapper.ChannelMapper">
+
+
+    <select id="getUserChannelList" parameterType="com.zhongzheng.bo.ChannelQueryBo" resultType="com.zhongzheng.domian.Channel">
+        SELECT
+            c.*
+        FROM
+            user_channel uc
+                LEFT JOIN channel c ON uc.channel_id = c.channel_id
+        WHERE
+            uc.user_id = #{queryBo.userId}
+        <if test="queryBo.eduName != null and queryBo.eduName != ''">
+            AND c.edu_name = #{queryBo.eduName}
+        </if>
+        <if test="queryBo.businessName != null and queryBo.businessName != ''">
+            AND c.business_name =#{queryBo.businessName}
+        </if>
+        <if test="queryBo.name != null and queryBo.name != ''">
+            AND c.`name` like concat('%', #{queryBo.name}, '%')
+        </if>
+        <if test="queryBo.type != null and queryBo.type != ''">
+            AND c.type =#{queryBo.type}
+        </if>
+        <if test="queryBo.status != null and queryBo.status != ''">
+            AND c.`status` =#{queryBo.status}
+        </if>
+        ORDER BY c.create_time DESC
+    </select>
+</mapper>

+ 11 - 0
src/main/resources/mapper/OrderAttachmentMapper.xml

@@ -0,0 +1,11 @@
+<?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.zhongzheng.mapper.OrderAttachmentMapper">
+
+
+    <select id="listByOrderSn" parameterType="java.lang.String" resultType="com.zhongzheng.domian.OrderAttachment">
+        SELECT * FROM order_attachment WHERE order_sn = #{orderSn}
+    </select>
+</mapper>

+ 10 - 0
src/main/resources/mapper/OrderInvoiceMapper.xml

@@ -0,0 +1,10 @@
+<?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.zhongzheng.mapper.OrderInvoiceMapper">
+
+    <select id="getOrderInvoiceById" parameterType="java.lang.Long" resultType="com.zhongzheng.domian.OrderInvoice">
+        SELECT * FROM order_invoice WHERE invoice_id = #{invoiceId}
+    </select>
+</mapper>

+ 200 - 0
src/main/resources/mapper/OrderMapper.xml

@@ -0,0 +1,200 @@
+<?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.zhongzheng.mapper.OrderMapper">
+
+    <select id="getOrderList" parameterType="com.zhongzheng.bo.OrderQueryBo" resultType="com.zhongzheng.vo.OrderListVo">
+        SELECT
+            o.order_sn,
+            o.check_status,
+            o.create_time,
+            o.invoice_status,
+            o.order_price,
+            o.pay_price,
+            o.order_status,
+            o.order_type,
+            o.order_uncollected,
+            o.order_received,
+            o.pay_time,
+            o.refund_status,
+            o.`status`,
+            c.`name` AS channelName,
+            c.edu_name,
+            c.business_name,
+            sc.company_name,
+            su.user_name
+        FROM
+            `order` o
+                LEFT JOIN channel c ON o.channel_id = c.channel_id
+                LEFT JOIN sys_company sc ON o.company_id = sc.company_name
+                LEFT JOIN sys_user su ON o.user_id = su.user_id
+        WHERE
+            1 = 1
+        AND o.user_id = #{bo.userId}
+        <if test="bo.eduName != null and bo.eduName != ''">
+            AND c.edu_name = #{bo.eduName}
+        </if>
+        <if test="bo.businessName != null and bo.businessName != ''">
+            AND c.business_name =#{bo.businessName}
+        </if>
+        <if test="bo.channelName != null and bo.channelName != ''">
+            AND c.`name` like concat('%', #{bo.channelName}, '%')
+        </if>
+        <if test="bo.companyName != null and bo.companyName != ''">
+            AND sc.company_name =#{bo.companyName}
+        </if>
+        <if test="bo.checkStatus != null and bo.checkStatus != ''">
+            AND o.check_status =#{bo.checkStatus}
+        </if>
+        <if test="bo.orderSn != null and bo.orderSn != ''">
+            AND o.order_sn =#{bo.orderSn}
+        </if>
+        <if test="bo.orderStatus != null and bo.orderStatus != ''">
+            AND o.order_status =#{bo.orderStatus}
+        </if>
+        <if test="bo.orderType != null and bo.orderType != ''">
+            AND o.order_type =#{bo.orderType}
+        </if>
+        <if test="bo.userName != null and bo.userName != ''">
+            AND su.user_name =#{bo.userName}
+        </if>
+        <if test="bo.startTime != null and bo.startTime != ''">
+            AND o.create_time &gt;= #{bo.startTime}
+        </if>
+        <if test="bo.endTime != null and bo.endTime != ''">
+            AND o.create_time &lt;= #{bo.endTime}
+        </if>
+        ORDER BY o.create_time DESC
+    </select>
+
+    <select id="getOrderDetail" parameterType="java.lang.String" resultType="com.zhongzheng.vo.OrderDetailVo">
+        SELECT
+            o.order_sn,
+            o.check_status,
+            o.create_time,
+            o.invoice_status,
+            o.order_price,
+            o.pay_price,
+            o.order_status,
+            o.order_type,
+            o.order_uncollected,
+            o.order_received,
+            o.pay_time,
+            o.refund_status,
+            o.`status`,
+            c.`name` AS channelName,
+            c.edu_name,
+            c.business_name,
+            sc.company_name,
+            su.user_name
+        FROM
+            `order` o
+                LEFT JOIN channel c ON o.channel_id = c.channel_id
+                LEFT JOIN sys_company sc ON o.company_id = sc.company_name
+                LEFT JOIN sys_user su ON o.user_id = su.user_id
+        WHERE
+            o.order_sn = #{orderSn}
+    </select>
+
+
+    <select id="getFinanceOrderList" parameterType="com.zhongzheng.bo.OrderQueryBo" resultType="com.zhongzheng.vo.OrderListVo">
+        SELECT
+        o.order_sn,
+        o.check_status,
+        o.create_time,
+        o.invoice_status,
+        o.order_price,
+        o.pay_price,
+        o.order_status,
+        o.order_type,
+        o.order_uncollected,
+        o.order_received,
+        o.pay_time,
+        o.refund_status,
+        o.`status`,
+        c.`name` AS channelName,
+        c.edu_name,
+        c.business_name,
+        sc.company_name,
+        su.user_name
+        FROM
+        `order` o
+        LEFT JOIN channel c ON o.channel_id = c.channel_id
+        LEFT JOIN sys_company sc ON o.company_id = sc.company_name
+        LEFT JOIN sys_user su ON o.user_id = su.user_id
+        WHERE
+        1 = 1
+        AND o.user_id = #{bo.userId}
+        <if test="bo.eduName != null and bo.eduName != ''">
+            AND c.edu_name = #{bo.eduName}
+        </if>
+        <if test="bo.businessName != null and bo.businessName != ''">
+            AND c.business_name =#{bo.businessName}
+        </if>
+        <if test="bo.channelName != null and bo.channelName != ''">
+            AND c.`name` like concat('%', #{bo.channelName}, '%')
+        </if>
+        <if test="bo.companyName != null and bo.companyName != ''">
+            AND sc.company_name =#{bo.companyName}
+        </if>
+        <if test="bo.checkStatus != null and bo.checkStatus != ''">
+            AND o.check_status =#{bo.checkStatus}
+        </if>
+        <if test="bo.orderSn != null and bo.orderSn != ''">
+            AND o.order_sn =#{bo.orderSn}
+        </if>
+        <if test="bo.orderStatus != null and bo.orderStatus != ''">
+            AND o.order_status =#{bo.orderStatus}
+        </if>
+        <if test="bo.orderType != null and bo.orderType != ''">
+            AND o.order_type =#{bo.orderType}
+        </if>
+        <if test="bo.userName != null and bo.userName != ''">
+            AND su.user_name =#{bo.userName}
+        </if>
+        <if test="bo.startTime != null and bo.startTime != ''">
+            AND o.create_time &gt;= #{bo.startTime}
+        </if>
+        <if test="bo.endTime != null and bo.endTime != ''">
+            AND o.create_time &lt;= #{bo.endTime}
+        </if>
+        ORDER BY o.create_time DESC
+    </select>
+
+    <select id="getFinanceOrderDetail" parameterType="java.lang.String" resultType="com.zhongzheng.vo.OrderDetailVo">
+        SELECT
+            o.order_sn,
+            o.check_status,
+            o.create_time,
+            o.invoice_status,
+            o.order_price,
+            o.pay_price,
+            o.order_status,
+            o.order_type,
+            o.order_uncollected,
+            o.order_received,
+            o.pay_time,
+            o.refund_status,
+            o.`status`,
+            o.tenant_id,
+            c.`name` AS channelName,
+            c.edu_name,
+            c.business_name,
+            sc.company_name,
+            su.user_name
+        FROM
+            `order` o
+                LEFT JOIN channel c ON o.channel_id = c.channel_id
+                LEFT JOIN sys_company sc ON o.company_id = sc.company_name
+                LEFT JOIN sys_user su ON o.user_id = su.user_id
+        WHERE
+            o.order_sn = #{orderSn}
+    </select>
+
+    <select id="getOrderInvoiceApply" parameterType="java.lang.String" resultType="com.zhongzheng.vo.OrderInvoiceVo">
+        SELECT * FROM order_invoice WHERE order_sn = #{orderSn} AND `status` = 1
+    </select>
+
+
+</mapper>

+ 10 - 0
src/main/resources/mapper/OrderPersonMapper.xml

@@ -0,0 +1,10 @@
+<?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.zhongzheng.mapper.OrderPersonMapper">
+
+    <select id="listPersonBySn" parameterType="java.lang.String" resultType="com.zhongzheng.domian.OrderPerson">
+        SELECT * FROM order_person WHERE order_sn = #{orderSn}
+    </select>
+</mapper>

+ 10 - 0
src/main/resources/mapper/OrderRefundMapper.xml

@@ -0,0 +1,10 @@
+<?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.zhongzheng.mapper.OrderRefundMapper">
+
+    <select id="getRefundBySn" parameterType="java.lang.String" resultType="com.zhongzheng.domian.OrderRefund">
+        SELECT * FROM order_refund WHERE order_sn = #{orderSn} AND `status` = 1
+    </select>
+</mapper>