Jelajahi Sumber

支付订单

plf 4 tahun lalu
induk
melakukan
9c47f7b425
51 mengubah file dengan 6652 tambahan dan 24 penghapusan
  1. 6 0
      .gitignore
  2. 6 0
      pom.xml
  3. 51 0
      src/main/java/com/caimei/config/WebConfiguration.java
  4. 92 0
      src/main/java/com/caimei/config/WxConfig.java
  5. 51 0
      src/main/java/com/caimei/controller/OrderApi.java
  6. 201 0
      src/main/java/com/caimei/controller/PayOrderApi.java
  7. 76 0
      src/main/java/com/caimei/mapper/OrderMapper.java
  8. 164 0
      src/main/java/com/caimei/mapper/PayOrderMapper.java
  9. 82 0
      src/main/java/com/caimei/model/dto/PaymentDto.java
  10. 10 0
      src/main/java/com/caimei/model/enumerate/KeyFormat.java
  11. 75 0
      src/main/java/com/caimei/model/enumerate/ReceivablesType.java
  12. 186 0
      src/main/java/com/caimei/model/po/CmDiscernReceiptPo.java
  13. 126 0
      src/main/java/com/caimei/model/po/CmPayShopPo.java
  14. 71 0
      src/main/java/com/caimei/model/po/CmPayShopRecordPo.java
  15. 61 0
      src/main/java/com/caimei/model/po/CmReceiptOrderRelationPo.java
  16. 11 0
      src/main/java/com/caimei/model/vo/CartProductVo.java
  17. 169 0
      src/main/java/com/caimei/model/vo/DiscernReceiptVo.java
  18. 15 0
      src/main/java/com/caimei/model/vo/LogisticsBatchVo.java
  19. 67 0
      src/main/java/com/caimei/model/vo/OrderPayLinkVo.java
  20. 341 0
      src/main/java/com/caimei/model/vo/OrderProductVo.java
  21. 351 0
      src/main/java/com/caimei/model/vo/OrderVo.java
  22. 369 0
      src/main/java/com/caimei/model/vo/ShopOrderVo.java
  23. 79 0
      src/main/java/com/caimei/model/vo/SplitAccountVo.java
  24. 25 0
      src/main/java/com/caimei/service/OrderService.java
  25. 82 0
      src/main/java/com/caimei/service/PayOrderService.java
  26. 2 18
      src/main/java/com/caimei/service/impl/LoginServiceImpl.java
  27. 125 0
      src/main/java/com/caimei/service/impl/OrderServiceImpl.java
  28. 2 0
      src/main/java/com/caimei/service/impl/OrderSubmitServiceImpl.java
  29. 655 0
      src/main/java/com/caimei/service/impl/PayOrderServiceImpl.java
  30. 1 0
      src/main/java/com/caimei/service/impl/ShoppingCartServiceImpl.java
  31. 128 0
      src/main/java/com/caimei/util/AES.java
  32. 601 0
      src/main/java/com/caimei/util/Base64.java
  33. 36 0
      src/main/java/com/caimei/util/CheckUtils.java
  34. 7 0
      src/main/java/com/caimei/util/ConfigureEncryptAndDecrypt.java
  35. 373 0
      src/main/java/com/caimei/util/ConvertUtils.java
  36. 164 0
      src/main/java/com/caimei/util/Digest.java
  37. 85 0
      src/main/java/com/caimei/util/HashUtil.java
  38. 167 0
      src/main/java/com/caimei/util/HttpClient4Utils.java
  39. 23 0
      src/main/java/com/caimei/util/KeyGenerator.java
  40. 133 0
      src/main/java/com/caimei/util/KeyPair.java
  41. 206 0
      src/main/java/com/caimei/util/KeyWorker.java
  42. 89 0
      src/main/java/com/caimei/util/Md5Util.java
  43. 141 0
      src/main/java/com/caimei/util/PayUtils.java
  44. 106 0
      src/main/java/com/caimei/util/RSAUtil.java
  45. 98 0
      src/main/java/com/caimei/util/RandomCodeGenerator.java
  46. 174 0
      src/main/java/com/caimei/util/XmlKeyBuilder.java
  47. 0 2
      src/main/resources/config/beta/application-beta.yml
  48. 10 2
      src/main/resources/config/dev/application-dev.yml
  49. 0 2
      src/main/resources/config/prod/application-prod.yml
  50. 118 0
      src/main/resources/mapper/OrderMapper.xml
  51. 441 0
      src/main/resources/mapper/PayOrderMapper.xml

+ 6 - 0
.gitignore

@@ -9,6 +9,12 @@
 *.war
 *.ear
 
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
 # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
 hs_err_pid*
 

+ 6 - 0
pom.xml

@@ -118,6 +118,12 @@
             <artifactId>knife4j-spring-boot-starter</artifactId>
             <version>3.0.2</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.7</version>
+        </dependency>
     </dependencies>
 
     <profiles>

+ 51 - 0
src/main/java/com/caimei/config/WebConfiguration.java

@@ -0,0 +1,51 @@
+package com.caimei.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.config.CorsRegistry;
+import org.springframework.web.reactive.config.WebFluxConfigurer;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+import java.net.InetSocketAddress;
+import java.util.Objects;
+
+@Configuration
+public class WebConfiguration implements WebFluxConfigurer {
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry
+                .addMapping("/**")
+                .allowedOrigins("*")
+                .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTION")
+                .allowedHeaders("header1", "header2", "header3")
+                .exposedHeaders("header1", "header2")
+                .allowCredentials(true)
+                .maxAge(3600);
+    }
+
+    /**
+     * https://stackoverflow.com/questions/51192630/how-do-you-get-clients-ip-address-spring-webflux-websocket?rq=1
+     * https://stackoverflow.com/questions/50981136/how-to-get-client-ip-in-webflux
+     * https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-filters
+     * 由于在低版本的 spring-webflux 中不支持直接获得请求 IP(https://jira.spring.io/browse/SPR-16681),因此写了一个补丁曲线救国,
+     * 从 org.springframework.web.server.ServerWebExchange 中获得 IP 后,在放到 header 里
+     */
+    @Component
+    public static class RetrieveClientIpWebFilter implements WebFilter {
+
+        @Override
+        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
+            InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
+            String clientIp = Objects.requireNonNull(remoteAddress).getAddress().getHostAddress();
+            ServerHttpRequest mutatedServerHttpRequest = exchange.getRequest().mutate().header("X-CLIENT-IP", clientIp).build();
+            ServerWebExchange mutatedServerWebExchange = exchange.mutate().request(mutatedServerHttpRequest).build();
+            return chain.filter(mutatedServerWebExchange);
+        }
+    }
+}
+ 

+ 92 - 0
src/main/java/com/caimei/config/WxConfig.java

@@ -0,0 +1,92 @@
+package com.caimei.config;
+
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.model.ResponseJson;
+import com.caimei.util.HttpRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 微信工具类
+ *
+ * @author LG
+ * @create 2017-09-21
+ **/
+@Slf4j
+@Component
+public class WxConfig {
+    public static String CrmAppId;
+
+    @Value("${wx.crmAppId}")
+    public void setCrmAppId(String crmAppId) {
+        CrmAppId = crmAppId;
+    }
+
+    public static String CrmAppSecret;
+
+    @Value("${wx.crmAppSecret}")
+    public void setCrmAppSecret(String crmAppSecret) {
+        CrmAppSecret = crmAppSecret;
+    }
+
+    public static String AppId;
+
+    @Value("${wx.AppId}")
+    public void setAppId(String appId) {
+        AppId = appId;
+    }
+
+    public static String AppSecret;
+
+    @Value("${wx.AppSecret}")
+    public void setAppSecret(String appSecret) {
+        AppSecret = appSecret;
+    }
+
+    /**
+     * 网页授权登录,通过code获取access_token
+     *
+     * @param code wxcode
+     * @return
+     * @throws Exception
+     */
+    public static Map<String, Object> getAccessTokenMap(String code) throws Exception {
+        String link = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
+        //微信公众号
+        link = link.replace("APPID", CrmAppId);
+        link = link.replace("SECRET", CrmAppSecret);
+        //填写第一步获取的code参数
+        link = link.replace("CODE", code);
+        String result = HttpRequest.sendGet(link);
+        log.info(result);
+        Map<String, Object> map = JSONObject.parseObject(result, Map.class);
+        return map;
+    }
+
+    /**
+     * 小程序授权,获取openId
+     *
+     * @param code
+     * @return
+     */
+    public static String getWxMallOpenId(String code) throws Exception {
+        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
+        Map<String, String> requestUrlParam = new HashMap<>(4);
+        //小程序appId
+        requestUrlParam.put("appid", AppId);
+        //小程序appSecret
+        requestUrlParam.put("secret", AppSecret);
+        //小程序端返回的code
+        requestUrlParam.put("js_code", code);
+        //默认参数
+        requestUrlParam.put("grant_type", "authorization_code");
+        //发送post请求读取调用微信接口获取openid用户唯一标识
+        return HttpRequest.sendPost(requestUrl, requestUrlParam);
+    }
+}

+ 51 - 0
src/main/java/com/caimei/controller/OrderApi.java

@@ -0,0 +1,51 @@
+package com.caimei.controller;
+
+import com.caimei.model.ResponseJson;
+import com.caimei.model.vo.OrderVo;
+import com.caimei.service.OrderService;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Api(tags = "订单相关")
+@RestController
+@RequestMapping("/order")
+public class OrderApi {
+    private OrderService orderService;
+
+    @Autowired
+    public void setOrderService(OrderService orderService) {
+        this.orderService = orderService;
+    }
+
+    /**
+     * 我的订单
+     */
+    @ApiOperation("我的订单")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderState", required = true, value = "1:待确认,2:待付款,3:待发货,4:已发货,5:退货款"),
+            @ApiImplicitParam(name = "organizeId", required = true, value = "组织id"),
+            @ApiImplicitParam(name = "userId", required = false, value = "机构用户id"),
+            @ApiImplicitParam(name = "pageNum", required = false, value = "第几页"),
+            @ApiImplicitParam(name = "pageSize", required = false, value = "一页多少条")
+    })
+    @GetMapping("/list")
+    public ResponseJson<PageInfo<OrderVo>> getOrderList(Integer orderState, Integer userId, Integer organizeId,
+                                                        @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+                                                        @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
+        return orderService.getOrderList(orderState, userId, organizeId, pageNum, pageSize);
+    }
+}

+ 201 - 0
src/main/java/com/caimei/controller/PayOrderApi.java

@@ -0,0 +1,201 @@
+package com.caimei.controller;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.config.WxConfig;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.dto.PaymentDto;
+import com.caimei.model.vo.OrderPayLinkVo;
+import com.caimei.service.PayOrderService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.reactive.function.server.ServerRequest;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2020/5/6
+ */
+@Api(tags = "订单支付")
+@Slf4j
+@RestController
+@RequestMapping("/PayOrder")
+public class PayOrderApi {
+    private PayOrderService payOrderService;
+
+    @Autowired
+    public void setPayOrderService(PayOrderService payOrderService) {
+        this.payOrderService = payOrderService;
+    }
+
+    @Value("${caimei.notifyUrl}")
+    private String notifyUrl;
+
+    @Value("${caimei.redirectLink}")
+    private String redirectLink;
+
+    @Value("${caimei.linkPage}")
+    private String linkPage;
+
+    /**
+     * 获取线上支付开关状态
+     *
+     * @return
+     */
+    @ApiOperation("获取线上支付开关状态")
+    @GetMapping("/onLineSwitch")
+    public ResponseJson<Integer> onLineSwitch() {
+        return payOrderService.getPayOnLineSwitch();
+    }
+
+    /**
+     * 收银台数据显示
+     */
+    @GetMapping("/checkoutCounter")
+    public ResponseJson<Map<String, Object>> checkoutCounter(Integer orderId) {
+        if (null == orderId) {
+            return ResponseJson.error("参数异常", null);
+        }
+        return payOrderService.checkoutCounter(orderId);
+    }
+
+    /**
+     * 微信线上支付
+     */
+    @ApiOperation("微信线上支付")
+    @PostMapping("/miniWxPay")
+    public ResponseJson<JSONObject> miniWxPay(@RequestBody PaymentDto payment, ServerRequest request) {
+        if (!"WEIXIN".equals(payment.getPayWay()) || payment.getPayAmount() == null || payment.getPayAmount() < 2) {
+            return ResponseJson.error("参数异常", null);
+        }
+        String clientIp = request.headers().asHttpHeaders().getFirst("X-CLIENT-IP");
+        payment.setClientIp(clientIp);
+        Map<String, Object> map = null;
+        if (null == payment.getState()) {
+            //小程序微信快捷支付
+            payment.setPayType("MINIAPP_WEIXIN");
+            String infos;
+            try {
+                infos = WxConfig.getWxMallOpenId(payment.getCode());
+            } catch (Exception e) {
+                log.error("错误信息", e);
+                return ResponseJson.error("wx小程序获取openid失败", null);
+            }
+            map = JSONObject.parseObject(infos, Map.class);
+        } else {
+            //pc微信扫码支付,微信公众号支付
+            payment.setPayType("JSAPI_WEIXIN");
+            try {
+                map = WxConfig.getAccessTokenMap(payment.getCode());
+            } catch (Exception e) {
+                log.error("错误信息", e);
+                return ResponseJson.error("wx公众号获取openid失败", null);
+            }
+        }
+        String openid = (String) map.get("openid");
+        if (openid == null) {
+            return ResponseJson.error("wx获取openid失败", null);
+        }
+        payment.setOpenid(openid);
+        payment.setNotifyUrl(notifyUrl);
+        log.info("wx支付openid>>>>>" + openid);
+        return payOrderService.pay(payment);
+    }
+
+    /**
+     * 支付异步通知回调
+     */
+    @GetMapping("/paymentCallback")
+    public String paymentCallback(ServerRequest request) throws Exception {
+        log.info("异步回调通知>>>>>>>start");
+        String data = request.pathVariable("data");
+        if (StringUtils.isBlank(data)) {
+            return "回调参数失败";
+        }
+        return payOrderService.paymentCallback(data);
+    }
+
+    /**
+     * 小程序生成网银支付链接
+     */
+    @ApiOperation("小程序生成网银支付链接")
+    @PostMapping("/payLink")
+    public ResponseJson<String> payLink(@RequestBody OrderPayLinkVo orderPayLink) {
+        orderPayLink.setRedirectLink(redirectLink);
+        return payOrderService.payLink(orderPayLink);
+    }
+
+    /**
+     * 支付链接重定向到页面
+     */
+    @ApiOperation("支付链接重定向到页面")
+    @GetMapping("/jumpPage")
+    public void jumpPage(String linkLogo, ServerHttpResponse response) throws IOException {
+        payOrderService.jumpPage(linkLogo, linkPage, response);
+    }
+
+    /**
+     * 链接数据
+     */
+    @ApiOperation("链接数据")
+    @GetMapping("/linkData")
+    public ResponseJson<Map<String, Object>> linkData(String linkLogo) {
+        if (StringUtils.isBlank(linkLogo)) {
+            return ResponseJson.error("参数异常", null);
+        }
+        return payOrderService.linkData(linkLogo);
+    }
+
+    /**
+     * pc端支付,银联,支付宝
+     */
+    @ApiOperation("pc端支付,银联,支付宝支付")
+    @PostMapping("/pcMallPay")
+    public ResponseJson<JSONObject> pcMallPay(@RequestBody PaymentDto payment) {
+        if (null == payment || StringUtils.isBlank(payment.getPayWay()) || StringUtils.isBlank(payment.getReturnUrl()) || payment.getPayAmount() == null) {
+            return ResponseJson.error("参数异常", null);
+        }
+        if ("UNIONPAY".equals(payment.getPayWay())) {
+            //银联支付
+            payment.setPayType("GATEWAY_UNIONPAY");
+        } else if ("ALIPAY".equals(payment.getPayWay())) {
+            //支付宝支付
+            payment.setPayType("ALIPAY_H5");
+        }
+        payment.setNotifyUrl(notifyUrl);
+        return payOrderService.pay(payment);
+    }
+
+    /**
+     * 判断此次支付是否完成
+     */
+    @ApiOperation("判断此次支付是否完成")
+    @GetMapping("/payWhetherSuccess")
+    public ResponseJson<String> payWhetherSuccess(Integer orderId, Integer paySuccessCounter) {
+        if (null == orderId || null == paySuccessCounter) {
+            return ResponseJson.error("参数异常", null);
+        }
+        return payOrderService.payWhetherSuccess(orderId, paySuccessCounter);
+    }
+
+    /**
+     * 查询本次支付订单是否完成
+     */
+    @GetMapping("/findOrderStatus")
+    public ResponseJson<JSONObject> findOrderStatus(String mbOrderId) {
+        if (null == mbOrderId) {
+            return ResponseJson.error("参数异常", null);
+        }
+        return payOrderService.findOrderStatus(mbOrderId);
+    }
+}

+ 76 - 0
src/main/java/com/caimei/mapper/OrderMapper.java

@@ -0,0 +1,76 @@
+package com.caimei.mapper;
+
+import com.caimei.model.po.PromotionsPo;
+import com.caimei.model.vo.*;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Mapper
+public interface OrderMapper {
+
+    /**
+     * 查询
+     *
+     * @param userId
+     * @param orderState
+     * @return
+     */
+    List<OrderVo> findOrderList(@Param("userId") Integer userId, @Param("orderState") Integer orderState, @Param("organizeId") Integer organizeId);
+
+    /**
+     * 查询物流详情
+     *
+     * @param orderId
+     * @return
+     */
+    List<LogisticsBatchVo> findLogistics(Integer orderId);
+
+
+    /**
+     * 查询该主订单下所有子订单
+     *
+     * @param orderId
+     * @return
+     */
+    List<ShopOrderVo> findAllShopOrder(Integer orderId);
+
+    /**
+     * 查询子订单下所有商品
+     *
+     * @param shopOrderId
+     * @return
+     */
+    List<OrderProductVo> findOrderProduct(Integer shopOrderId);
+
+    /**
+     * 查询订单促销信息
+     *
+     * @param orderPromotionsId
+     * @return
+     */
+    PromotionsPo findOrderPromotionsById(Integer orderPromotionsId);
+
+    /**
+     * 查询收款记录
+     *
+     * @param orderId
+     * @return
+     */
+    List<DiscernReceiptVo> findDiscernReceipt(Integer orderId);
+
+    /**
+     * 查询是否有过线下支付,订单款
+     *
+     * @param orderId
+     * @return
+     */
+    DiscernReceiptVo findOfflinePayment(Integer orderId);
+}

+ 164 - 0
src/main/java/com/caimei/mapper/PayOrderMapper.java

@@ -0,0 +1,164 @@
+package com.caimei.mapper;
+
+import com.caimei.model.po.*;
+import com.caimei.model.vo.*;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Mapper
+public interface PayOrderMapper {
+    /**
+     * 查询订单信息
+     *
+     * @param orderId
+     * @return
+     */
+    OrderVo findOrder(Integer orderId);
+
+    /**
+     * 查询支付记录
+     *
+     * @param order
+     * @return
+     */
+    List<DiscernReceiptVo> getDiscernReceipt(OrderVo order);
+
+    /**
+     * 更新订单信息
+     *
+     * @param order
+     */
+    void updateSelective(OrderVo order);
+
+    /**
+     * 保存收款记录
+     *
+     * @param discernReceipt
+     */
+    void insertDiscernReceipt(CmDiscernReceiptPo discernReceipt);
+
+    /**
+     * 保存订单收款关系
+     *
+     * @param relation
+     */
+    void insertOrderRelation(CmReceiptOrderRelationPo relation);
+
+    /**
+     * 查询线上支付链接
+     *
+     * @param orderId
+     * @param amount
+     * @return
+     */
+    OrderPayLinkVo findOrderPayLink(@Param("orderId") Integer orderId, @Param("amount") BigDecimal amount);
+
+    /**
+     * 更新线上支付链接状态
+     *
+     * @param orderPayLink
+     */
+    void updateOrderPayLink(OrderPayLinkVo orderPayLink);
+
+    /**
+     * 保存线上支付链接
+     *
+     * @param orderPayLink
+     */
+    void insertOrderPayLink(OrderPayLinkVo orderPayLink);
+
+    /**
+     * 查询线上支付链接
+     *
+     * @param linkLogo
+     * @return
+     */
+    OrderPayLinkVo getOrderPayLink(String linkLogo);
+
+    /**
+     * 查询用户信息
+     *
+     * @param userId
+     * @return
+     */
+    UserPo findUser(Long userId);
+
+    /**
+     * 查询订单商品
+     *
+     * @param orderId
+     * @return
+     */
+    List<OrderProductVo> findAllOrderProduct(Integer orderId);
+
+    /**
+     * 保存分账信息
+     *
+     * @param splitAccount
+     */
+    void insertSplitAccount(SplitAccountVo splitAccount);
+
+    /**
+     * 获取线上支付开关状态
+     */
+    Integer getPayOnLineSwitch();
+
+    /**
+     * 查询子商户商编
+     *
+     * @param shopId
+     * @return
+     */
+    String findCommercialCode(Long shopId);
+
+    /**
+     * 查询已支付金额
+     *
+     * @param orderProductId
+     * @return
+     */
+    BigDecimal findPaidAmount(Integer orderProductId);
+
+    /**
+     * 查询子订单
+     *
+     * @param orderId
+     * @return
+     */
+    List<ShopOrderVo> findShopOrder(Integer orderId);
+
+    /**
+     * 查询已支付运费
+     *
+     * @param orderId
+     * @param shopId
+     * @return
+     */
+    BigDecimal findShipping(@Param("orderId") Integer orderId, @Param("shopId") Integer shopId);
+
+    /**
+     * 修改子订单付款状态及付款金额
+     *
+     * @param shopOrderId 子订单id
+     * @param paidShop    已支付金额
+     * @param payStatus   付款状态
+     */
+    void updateShopOrderByPayStatus(@Param("shopOrderId") Integer shopOrderId, @Param("paidShop") BigDecimal paidShop, @Param("payStatus") String payStatus);
+
+    /**
+     * 修改主订单付款状态
+     *
+     * @param orderId
+     * @param payStatus
+     */
+    void updateOrderByPayStatus(@Param("orderId") Integer orderId, @Param("payStatus") String payStatus);
+}

+ 82 - 0
src/main/java/com/caimei/model/dto/PaymentDto.java

@@ -0,0 +1,82 @@
+package com.caimei.model.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2020/5/7
+ */
+@Data
+public class PaymentDto implements Serializable {
+
+    /**
+     * 支付金额,单位分,必须大于2
+     */
+    private Integer payAmount;
+
+    /**
+     * 支付方式,
+     * 银联:UNIONPAY,
+     * 微信:WEIXIN,
+     * 支付宝:ALIPAY
+     */
+    private String payWay;
+
+    /**
+     * 支付类型(现阶段主要是区分微信支付)
+     * 微信小程序支付: MINIAPP_WEIXIN
+     * 微信公众号支付: JSAPI_WEIXIN
+     */
+    private String payType;
+
+    /**
+     * 银行编码(银联支付使用)
+     */
+    private String bankCode;
+
+    /**
+     * 用户类型(银联支付使用)
+     * 企业:ENTERPRISE
+     * 个人:USER
+     */
+    private String userType;
+
+    /**
+     * 页面回调地址
+     */
+    private String returnUrl;
+
+    /**
+     * 微信小程序code,微信小程序支付使用
+     */
+    private String code;
+
+    /**
+     * 主订单id
+     */
+    private Integer orderId;
+
+    /**
+     * 微信支付使用
+     */
+    private String openid;
+
+    /**
+     * 异步通知回调
+     */
+    private String notifyUrl;
+
+    /**
+     * 微信公众号state参数
+     */
+    private String state;
+
+    /**
+     * 用户ip地址
+     */
+    private String clientIp;
+}

+ 10 - 0
src/main/java/com/caimei/model/enumerate/KeyFormat.java

@@ -0,0 +1,10 @@
+package com.caimei.model.enumerate;
+
+/*
+2015-01-23
+*/
+public enum KeyFormat {
+    ASN,
+    XML,
+    PEM
+}

+ 75 - 0
src/main/java/com/caimei/model/enumerate/ReceivablesType.java

@@ -0,0 +1,75 @@
+package com.caimei.model.enumerate;
+/**
+ *支付类型枚举类
+ *
+ * @author Aslee
+ */
+public enum ReceivablesType {
+    JIANSHE(1, "建设银行-7297"),
+    GUANGFA(2, "广发银行-0115"),
+    ZHONGXING_1(3, "中信银行-7172"),
+    ZHONGXING_2(4, "中信银行-0897"),
+    ZHONGXING_2_1(5, "中信银行-0897-财付通"),
+    ZHONGXING_2_2(6, "中信银行-0897-支付宝"),
+    XIANSHANG_ZFB(7, "线上-支付宝"),
+    XIANSHANG_WX(8, "线上-微信支付"),
+    XIANSHANG_KUAIQIAN(9, "线上-快钱支付"),
+    KOUTOUFANYONG(10, "口头返佣"),
+    GUANGFA_1(11, "广发银行-5461"),
+    WANGYIN(12, "企业网银"),
+    PCWX(13, "微信支付"),
+    ZHIFUBAO(14, "支付宝"),
+    MINIWX(15, "微信支付"),
+    YUEDIKO(16, "余额抵扣");
+
+
+    private int code;
+    private String desc;
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    ReceivablesType(int code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public static String getReceivablesType(int code) {
+        for (ReceivablesType type : ReceivablesType.values()) {
+            if (type.getCode() == code) {
+                return type.desc;
+            }
+        }
+        return "";
+    }
+
+    public static ReceivablesType getReceivablesType(String desc) {
+        for (ReceivablesType type : ReceivablesType.values()) {
+            if (type.getDesc().contains(desc)) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+    public static Boolean havaHandlingFee(int code) {
+        boolean res = false;
+        if (ReceivablesType.ZHONGXING_2_1.getCode() == code || ReceivablesType.ZHONGXING_2_2.getCode() == code || ReceivablesType.ZHONGXING_2.getCode() == code || ReceivablesType.GUANGFA_1.getCode() == code) {
+            res = true;
+        }
+        return res;
+    }
+}

+ 186 - 0
src/main/java/com/caimei/model/po/CmDiscernReceiptPo.java

@@ -0,0 +1,186 @@
+package com.caimei.model.po;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import lombok.Data;
+
+/**
+ * cm_discern_receipt
+ * @author 
+ */
+@Data
+public class CmDiscernReceiptPo implements Serializable {
+    private Long id;
+
+    /**
+     * 用户付款方式:1线上,2线下,3余额抵扣
+     */
+    private String payWay;
+
+    /**
+     * 付款类型:1建设银行7297、2广发银行0115、3中信银行7172、4中信银行0897、5中信银行0897-财付通、6中信银行0897-支付宝、7线上-支付宝、8线上-微信支付、9线上-快钱支付、10口头返佣、11广发银行5461、12PC-B2B网银、13PC-微信支付、14PC-支付宝、15小程序-微信支付、16余额抵扣、17PC-B2C网银
+     */
+    private String payType;
+
+    /**
+     * 收款款项类型:1订单款,2非订单款,3返佣款
+     */
+    private String receiptType;
+
+    /**
+     * 收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【订单款项状态:12345】【非订单款项状态:125】【返佣款状态:125】【线上支付成功为审核通过】
+     */
+    private String receiptStatus;
+
+    /**
+     * 短信内容
+     */
+    private String smsContent;
+
+    /**
+     * 短信内容Md5加密串(适用于二次短信匹配查询)
+     */
+    private String smsMd5Code;
+
+    /**
+     * 短信订单标识内容(适用打款备注订单Id)
+     */
+    private String orderFlag;
+
+    /**
+     * 收款金额(线上一次性付款和支付金额一致)
+     */
+    private BigDecimal receiptAmount;
+
+    /**
+     * 手续费(个别银行存在手续费)
+     */
+    private BigDecimal handlingFee;
+
+    /**
+     * 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联(线上支付默认为4)
+     */
+    private String confirmType;
+
+    /**
+     * 收款人权限ID(对应receipt_user_permission表)
+     */
+    private Integer receiptUserPermissionID;
+
+    /**
+     * 确认人权限ID(对应receipt_user_permission表)
+     */
+    private Long confirmUserPermissionID;
+
+    /**
+     * 审核人权限ID(对应receipt_user_permission表)
+     */
+    private Long reviewUserPermissionID;
+
+    /**
+     * 撤销人ID(对应sys_user表)
+     */
+    private Long cancelUserPermissionID;
+
+    /**
+     * 交易号--[线上字段]
+     */
+    private String transactionNum;
+
+    /**
+     * 银行Id--[线上字段]
+     */
+    private Integer bankID;
+
+    /**
+     * 银行卡号--[线上字段]
+     */
+    private String bankCode;
+
+    /**
+     * 快钱支付类型ID--[线上字段]
+     */
+    private Integer kuaiQianPayTypeID;
+
+    /**
+     * 块钱支付ID--[线上字段]
+     */
+    private Integer kuaiQianPayerID;
+
+    /**
+     * 支付状态 1支付成功--[线上字段]
+     */
+    private String rePayFlag;
+
+    /**
+     * 支付金额(可能废弃如果只需要一个支付就使用receiptAmount)--[线上字段]
+     */
+    private Double actualAmount;
+
+    /**
+     * 支付回调返回数据--[线上字段]
+     */
+    private String formData;
+
+    /**
+     * 支付问题--[线上字段]
+     */
+    private String problem;
+
+    /**
+     * 非订单款说明(适用协销确认的时候区分订单和非订单款)
+     */
+    private String noOrderReason;
+
+    /**
+     * 抹平备注(目前适用后台直接抹平账户操作)
+     */
+    private String balanceAccountsRemark;
+
+    /**
+     * 返佣关联备注
+     */
+    private String rebateRemarks;
+
+    /**
+     * 审核不通过原因
+     */
+    private String reviewReason;
+
+    /**
+     * 撤销原因
+     */
+    private String cancelReason;
+
+    /**
+     * 收款时间
+     */
+    private String receiptDate;
+
+    /**
+     * 确认时间
+     */
+    private String confirmDate;
+
+    /**
+     * 审核时间
+     */
+    private String reviewDate;
+
+    /**
+     * 撤销时间
+     */
+    private String cancelDate;
+
+    /**
+     * 更新操作时间
+     */
+    private String updateDate;
+
+    /**
+     * 作废标记 0 否,其余是
+     */
+    private String delFlag;
+
+    private static final long serialVersionUID = 1L;
+}

+ 126 - 0
src/main/java/com/caimei/model/po/CmPayShopPo.java

@@ -0,0 +1,126 @@
+package com.caimei.model.po;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import lombok.Data;
+
+/**
+ * cm_pay_shop
+ * @author 
+ */
+@Data
+public class CmPayShopPo implements Serializable {
+    private Long id;
+
+    /**
+     * 供应商Id
+     */
+    private Integer shopID;
+
+    /**
+     * 付款单名称
+     */
+    private String name;
+
+    /**
+     * 付款账号的户名
+     */
+    private String bankAccountName;
+
+    /**
+     * 付款账号
+     */
+    private String bankAccount;
+
+    /**
+     * 付款账号的开户行
+     */
+    private String bankName;
+
+    /**
+     *  付款账号的类型 0公账, 1私账
+     */
+    private String type;
+
+    /**
+     * 付供应商总金额
+     */
+    private BigDecimal totalAmount;
+
+    /**
+     * 余额支付
+     */
+    private BigDecimal balancePayFee;
+
+    /**
+     * 转账支付
+     */
+    private BigDecimal transferPayFee;
+
+    /**
+     * 付款方式 1建设银行7297, 2中信银行0897, 3中信银行7172, 4广发银行0115, 5广发银行5461, 6线上分账
+     */
+    private String payType;
+
+    /**
+     * 付款抹平金额(总)
+     */
+    private BigDecimal wipePayment;
+
+    /**
+     * 付款抹平备注(文字)
+     */
+    private String wipeRemarks;
+
+    /**
+     * 付款抹平备注(图片),以"##"隔开
+     */
+    private String wipeRemarkImages;
+
+    /**
+     * 抹平申请时间
+     */
+    private String wipeTime;
+
+    /**
+     * 申请人ID
+     */
+    private Integer applicant;
+
+    /**
+     * 申请时间
+     */
+    private String applyTime;
+
+    /**
+     * 审核人ID
+     */
+    private Integer reviewer;
+
+    /**
+     * 审核时间
+     */
+    private String reviewTime;
+
+    /**
+     * 付款时间
+     */
+    private String payTime;
+
+    /**
+     * 审核状态  0待审核,  1审核通过  2审核不通过
+     */
+    private String status;
+
+    /**
+     * 审核不通过原因
+     */
+    private String reason;
+
+    /**
+     * 删除标记 0 否,其余是
+     */
+    private String delFlag;
+
+    private static final long serialVersionUID = 1L;
+}

+ 71 - 0
src/main/java/com/caimei/model/po/CmPayShopRecordPo.java

@@ -0,0 +1,71 @@
+package com.caimei.model.po;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import lombok.Data;
+
+/**
+ * cm_pay_shop_record
+ * @author 
+ */
+@Data
+public class CmPayShopRecordPo implements Serializable {
+    private Long id;
+
+    /**
+     * 供应商Id
+     */
+    private Integer shopID;
+
+    /**
+     * 子订单ID
+     */
+    private Integer shopOrderID;
+
+    /**
+     * 子订单No
+     */
+    private String shopOrderNo;
+
+    /**
+     * 付款金额
+     */
+    private BigDecimal payAmount;
+
+    /**
+     * 付款抹平金额
+     */
+    private BigDecimal wipePayment;
+
+    /**
+     * 付款方式 1建设银行7297, 2中信银行0897, 3中信银行7172, 4广发银行0115, 5广发银行5461, 6线上分账
+     */
+    private String payType;
+
+    /**
+     * 付款时间
+     */
+    private String payTime;
+
+    /**
+     * 抹平申请时间
+     */
+    private String wipeTime;
+
+    /**
+     * 付款单表id
+     */
+    private Integer payShopID;
+
+    /**
+     * 0待审核, 1审核通过, 2审核不通过
+     */
+    private String status;
+
+    /**
+     * 删除标记 0 否,其余是
+     */
+    private String delFlag;
+
+    private static final long serialVersionUID = 1L;
+}

+ 61 - 0
src/main/java/com/caimei/model/po/CmReceiptOrderRelationPo.java

@@ -0,0 +1,61 @@
+package com.caimei.model.po;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import lombok.Data;
+
+/**
+ * cm_receipt_order_relation
+ * @author 
+ */
+@Data
+public class CmReceiptOrderRelationPo implements Serializable {
+    private Long id;
+
+    /**
+     * 关系类型:1返佣订单(返佣款)、2非返佣订单(订单款或者非订单款)
+     */
+    private String relationType;
+
+    /**
+     * 识别款项Id(对应cm_discern_receipt表)
+     */
+    private Integer receiptID;
+
+    /**
+     * 订单Id(relationType值为1是为子订单ID,为2时为主订单ID)
+     */
+    private Integer orderID;
+
+    /**
+     * 关联方式: 1手动 2自动
+     */
+    private String associationType;
+
+    /**
+     * 米花科技平台唯一流水号
+     */
+    private String mbOrderId;
+
+    /**
+     *  商户唯一订单请求号(订单编号T随机时间戳)
+     */
+    private String orderRequestNo;
+
+    /**
+     * 分账状态:0待分账,1已分账(只针对线上支付)
+     */
+    private String splitStatus;
+
+    /**
+     * 关联金额:1普通收款(线下):收款具体对该应母订单的收金额、2线上支付:付款金额就等于该金额、3返佣收款:默认为0
+     */
+    private BigDecimal associateAmount;
+
+    /**
+     * 删除标记 0 否,其余是
+     */
+    private String delFlag;
+
+    private static final long serialVersionUID = 1L;
+}

+ 11 - 0
src/main/java/com/caimei/model/vo/CartProductVo.java

@@ -5,6 +5,7 @@ import lombok.Data;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
+import java.util.List;
 
 /**
  * Description
@@ -14,6 +15,11 @@ import java.math.BigDecimal;
  */
 @Data
 public class CartProductVo implements Serializable {
+    /**
+     * 购物车id
+     */
+    private Integer cartId;
+
     /**
      * 供应商id
      */
@@ -98,5 +104,10 @@ public class CartProductVo implements Serializable {
      * 促销活动
      */
     private PromotionsPo promotion;
+
+    /**
+     * 阶梯价格数据
+     */
+    private List<LadderPriceVo> ladderPriceList;
 }
 

+ 169 - 0
src/main/java/com/caimei/model/vo/DiscernReceiptVo.java

@@ -0,0 +1,169 @@
+package com.caimei.model.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Data
+public class DiscernReceiptVo implements Serializable {
+    private Long id;
+
+    /**
+     * 用户付款方式:1线上,2线下,3余额抵扣
+     */
+    private String payWay;
+
+    /**
+     * 付款类型:1建设银行7297、2广发银行0115、3中信银行7172、4中信银行0897、5中信银行0897-财付通、6中信银行0897-支付宝、7线上-支付宝、8线上-微信支付、9线上-快钱支付、10口头返佣、11广发银行5461
+     */
+    private String payType;
+
+    /**
+     * 收款款项类型:1订单款,2非订单款,3返佣款
+     */
+    private String receiptType;
+
+    /**
+     * 收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【订单款项状态:12345】【非订单款项状态:125】【返佣款状态:125】【线上支付成功为审核通过】
+     */
+    private String receiptStatus;
+
+    /**
+     * 短信内容
+     */
+    private String smsContent;
+
+    /**
+     * 短信内容Md5加密串(适用于二次短信匹配查询)
+     */
+    private String smsMd5Code;
+
+    /**
+     * 短信订单标识内容
+     */
+    private String orderFlag;
+
+    /**
+     * 收款金额(线上一次性付款和支付金额一致)
+     */
+    private BigDecimal receiptAmount;
+
+    /**
+     * 手续费(个别银行存在手续费)
+     */
+    private BigDecimal handlingFee;
+
+    /**
+     * 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联
+     */
+    private String confirmType;
+
+    /**
+     * 确认人权限ID(对应receipt_user_permission表)
+     */
+    private Long confirmUserPermissionID;
+
+    /**
+     * 审核人权限ID(对应receipt_user_permission表)
+     */
+    private Long reviewUserPermissionID;
+
+    /**
+     * 撤销人ID(对应sys_user表)
+     */
+    private Long cancelUserPermissionID;
+
+    /**
+     * 交易号--[线上字段]
+     */
+    private String transactionNum;
+
+    /**
+     * 银行Id--[线上字段]
+     */
+    private Integer bankID;
+
+    /**
+     * 银行卡号--[线上字段]
+     */
+    private String bankCode;
+
+    /**
+     * 快钱支付类型ID--[线上字段]
+     */
+    private Integer kuaiQianPayTypeID;
+
+    /**
+     * 块钱支付ID--[线上字段]
+     */
+    private Integer kuaiQianPayerID;
+
+    /**
+     * 支付状态 1支付成功--[线上字段]
+     */
+    private String rePayFlag;
+
+    /**
+     * 支付金额(可能废弃如果只需要一个支付就使用receiptAmount)--[线上字段]
+     */
+    private Double actualAmount;
+
+    /**
+     * 支付回调返回数据--[线上字段]
+     */
+    private String formData;
+
+    /**
+     * 支付问题--[线上字段]
+     */
+    private String problem;
+
+    /**
+     * 非订单款说明(适用协销确认的时候区分订单和非订单款)
+     */
+    private String noOrderReason;
+
+    /**
+     * 审核不通过原因
+     */
+    private String reviewReason;
+
+    /**
+     * 撤销原因
+     */
+    private String cancelReason;
+
+    /**
+     * 收款时间
+     */
+    private String receiptDate;
+
+    /**
+     * 确认时间
+     */
+    private String confirmDate;
+
+    /**
+     * 审核时间
+     */
+    private String reviewDate;
+
+    /**
+     * 撤销时间
+     */
+    private String cancelDate;
+
+    /**
+     * 单次收款金额
+     */
+    private BigDecimal associateAmount;
+
+    private static final long serialVersionUID = 1L;
+}

+ 15 - 0
src/main/java/com/caimei/model/vo/LogisticsBatchVo.java

@@ -0,0 +1,15 @@
+package com.caimei.model.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Data
+public class LogisticsBatchVo implements Serializable {
+}

+ 67 - 0
src/main/java/com/caimei/model/vo/OrderPayLinkVo.java

@@ -0,0 +1,67 @@
+package com.caimei.model.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Data
+public class OrderPayLinkVo implements Serializable {
+    private Integer id;
+
+    /**
+     * 订单id
+     */
+    private Long orderId;
+
+    /**
+     * 链接标识
+     */
+    private String linkLogo;
+
+    /**
+     * 本次待付款金额
+     */
+    private BigDecimal unpaidAmount;
+
+    /**
+     * 链接生成时间
+     */
+    private Date generateTime;
+
+    /**
+     * 链接有效时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date effectiveTime;
+
+    /**
+     * 支付状态,0未支付,1已支付
+     */
+    private String payStatus;
+
+    /**
+     * 支付类型,1企业网银,2个人网银
+     */
+    private String payType;
+
+    /**
+     * 删除标记 0 否,其余是
+     */
+    private String delFlag;
+
+    /**
+     * 重定向链接
+     */
+    private String redirectLink;
+
+    private static final long serialVersionUID = 1L;
+}

+ 341 - 0
src/main/java/com/caimei/model/vo/OrderProductVo.java

@@ -0,0 +1,341 @@
+package com.caimei.model.vo;
+
+import com.caimei.model.po.PromotionsPo;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Data
+public class OrderProductVo implements Serializable {
+    private Integer orderProductId;
+
+    /**
+     * 主订单编号
+     */
+    private String orderNo;
+
+    /**
+     * 主订单ID
+     */
+    private Long orderId;
+
+    /**
+     * 订单Id
+     */
+    private Integer shopOrderId;
+
+    /**
+     * 子订单编号
+     */
+    private String shopOrderNo;
+
+    /**
+     * 供应商ID
+     */
+    private Long shopId;
+
+    /**
+     * 商品Id(采美商城和组织小程序都保存product表ID)
+     */
+    private Integer productId;
+
+    /**
+     * 组织的商品Id,关联cm_mall_organize_products表ID[适用于组织订单]
+     */
+    private Integer organizeProductId;
+
+    /**
+     * 采美组织默认为null,具体对应cm_mall_organize表ID[适用于组织订单]
+     */
+    private Integer organizeId;
+
+    /**
+     * 购买数量
+     */
+    private Integer num;
+
+    /**
+     * 赠送数量
+     */
+    private Integer presentNum;
+
+    /**
+     * 出库类型 0 采美出库  1 供应商出库
+     */
+    private String outStoreType;
+
+    /**
+     * skuId
+     */
+    private Integer skuID;
+
+    /**
+     * sku属性
+     */
+    private String props;
+
+    /**
+     * 属性名
+     */
+    private String propName;
+
+    /**
+     * 商品编号
+     */
+    private String productNo;
+
+    /**
+     * 商品价格(协销 市场价 普通 购买价)
+     */
+    private BigDecimal price;
+
+    /**
+     * 市场价 = 商品表市场价
+     */
+    private BigDecimal normalPrice;
+
+    /**
+     * 购买时商品成本价
+     */
+    private BigDecimal costPrice;
+
+    /**
+     * 记录普通用户购买时价格  活动价优先
+     */
+    private BigDecimal price0;
+
+    /**
+     * 记录会员用户购买时价格  活动价优先
+     */
+    private BigDecimal price1;
+
+    /**
+     * 总价  = price X num
+     */
+    private BigDecimal totalAmount;
+
+    /**
+     * 总价  = discountPrice X num + totalAddedValueTax
+     */
+    private BigDecimal totalFee;
+
+    /**
+     * 应付金额 = totalFee - discountFee
+     */
+    private BigDecimal shouldPayFee;
+
+    /**
+     * 折扣比例
+     */
+    private BigDecimal discount;
+
+    /**
+     * 折后单价
+     */
+    private BigDecimal discountPrice;
+
+    /**
+     * 是否含税 0不含税 1含税 2未知
+     */
+    private String includedTax;
+
+    /**
+     * 发票类型 1增值税专用发票 2增值税普通发票 3不开发票
+     */
+    private String invoiceType;
+
+    /**
+     * 启用阶梯价格标识 0否 1是
+     */
+    private String ladderPriceFlag;
+
+    /**
+     * 后台设置该商品税率
+     */
+    private BigDecimal taxRate;
+
+    /**
+     * 供应商税率:增值专用发票默认13%,增值税普通发票6%取值范围[0-100]
+     */
+    private BigDecimal supplierTaxRate;
+
+    /**
+     * 单个税费=税率X折后单价
+     */
+    private BigDecimal addedValueTax;
+
+    /**
+     * 总税费=单个税费X购买数量
+     */
+    private BigDecimal totalAddedValueTax;
+
+    /**
+     * 总税费(应付税费)默认值和应收税费一样
+     */
+    private BigDecimal shouldPayTotalTax;
+
+    /**
+     * 单个付供应商税费
+     */
+    private BigDecimal singleShouldPayTotalTax;
+
+    /**
+     * 商品费
+     */
+    private BigDecimal shopProductAmount;
+
+    /**
+     * 该商品总的应付供应商金额
+     */
+    private BigDecimal shopFee;
+
+    /**
+     * 该商品总的应付第三方金额
+     */
+    private BigDecimal otherFee;
+
+    /**
+     * 该商品总的应付采美金额 (受赠品影响)
+     */
+    private BigDecimal cmFee;
+
+    /**
+     * 后台设置的单个应付供应商金额
+     */
+    private BigDecimal singleShopFee;
+
+    /**
+     * 后台设置单个应付第三方金额
+     */
+    private BigDecimal singleOtherFee;
+
+    /**
+     * 后台计算的单个应付采美金额
+     */
+    private BigDecimal singleCmFee;
+
+    /**
+     * 订单商品状态
+     */
+    private String status;
+
+    /**
+     * 是否已评论:1是,空或0未评论(V5.0.0版本后已废弃--)
+     */
+    private String commentFlag;
+
+    /**
+     * 获取到的总采美豆值
+     */
+    private BigDecimal totalBeans;
+
+    /**
+     * 使用余额金额
+     */
+    private Double useBalanceAmount;
+
+    /**
+     * 使用采美豆数量
+     */
+    private Integer useBeanAmount;
+
+    /**
+     * 未出库数量
+     */
+    private Integer notOutStore;
+
+    /**
+     * 当前采美豆专区价格(采美豆)
+     */
+    private Integer cmbeanPrice;
+
+    /**
+     * 下单时商品购买价格类型快照 0 机构价,1活动价 ,2阶梯价
+     */
+    private String isActProduct;
+
+    /**
+     * 是否是赠品 0 不是 1 是
+     */
+    private String isGiftProduct;
+
+    /**
+     * 活动信息 已享受满XX减XX 之类
+     */
+    private String productActInfo;
+
+    /**
+     * 订单商品再次购买标识 0否 1是
+     */
+    private String buyAgainFlag;
+
+    /**
+     * 订单商品供应商确认标志 0否 1是
+     */
+    private String confirmProductFlag;
+
+    /**
+     * 支付状态 0 未进账 1 待财务审核 2 已进账(适用协销的单笔线下进账和自助订单线下或异常进账)
+     */
+    private String payStatus;
+
+    /**
+     * 供应商名称
+     */
+    private String shopName;
+
+    /**
+     * 商品名称
+     */
+    private String name;
+
+    /**
+     * 商品单位
+     */
+    private String productUnit;
+
+    private String productImage;
+
+    /**
+     * 活动类型 1000 热卖 1001 团购 1003 满减 1004满赠 1005 买赠
+     */
+    private String actType;
+
+    /**
+     * 活动优惠  类似满减优惠金额
+     */
+    private BigDecimal actPreferential;
+
+    /**
+     * 商品类型(0正常商品,1协商赠品,2促销赠品)
+     */
+    private String productType;
+
+    /**
+     * 订单促销id
+     */
+    private Integer orderPromotionsId;
+
+    /**
+     * 优惠 (price - discountPrice) * num
+     */
+    private BigDecimal preferential;
+
+    /**
+     * 协销订单:经理折扣(平摊到每个商品上,  按照每种商品的总价占订单总价的比例来均分);普通订单 无
+     */
+    private BigDecimal discountFee;
+
+    private Integer cancelNum;
+
+    private PromotionsPo ProductPromotion;
+
+    private static final long serialVersionUID = 1L;
+}

+ 351 - 0
src/main/java/com/caimei/model/vo/OrderVo.java

@@ -0,0 +1,351 @@
+package com.caimei.model.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Data
+public class OrderVo implements Serializable {
+    /**
+     * orderID
+     */
+    private Integer orderId;
+
+    /**
+     * 订单编号(后台:p,网站:W,小程序:x,第三方:T)
+     */
+    private String orderNo;
+
+    /**
+     * 采美组织默认为0,具体对应cm_mall_organize表ID
+     */
+    private Integer organizeID;
+
+    private Long userID;
+
+    /**
+     * 下单人
+     */
+    private Integer buyUserID;
+
+    /**
+     * 子订单ID
+     */
+    private String shopOrderIDs;
+
+    /**
+     * 0:个人自己下单 1:企业自己下单 2:员工帮会所下单 3:协销帮会所下单  4:后台下单 5:采美豆订单【1、5已弃用】
+     */
+    private Integer orderSubmitType;
+
+    /**
+     * 订单类型 0协销订单、 1普通订单
+     */
+    private Integer orderType;
+
+    /**
+     * 二手商品订单标识  0非二手商品订单、 1二手商品订单
+     */
+    private String secondHandOrderFlag;
+
+    /**
+     * 确认付款供应商标志,0未确认,1已确认
+     */
+    private String affirmPaymentFlag;
+
+    /**
+     * 是否包含活动商品(受订单未支付自动关闭时间影响)  0 否 1 是
+     */
+    private String hasActProduct;
+
+    /**
+     * 订单自动关闭时间点单位毫秒(v5.0版本已废弃)
+     */
+    private BigDecimal autoCloseTimeMills;
+
+    /**
+     * 0待确认,11待收待发,12待收部发,13待收全发,21部收待发,22部收部发,23部收全发,31已收待发,32已收部发,33已收全发,4交易完成,5订单完成,6已关闭,7交易全退
+     */
+    private String status;
+
+    /**
+     * (收款买家)收款状态:1待收款、2部分收款、3已收款
+     */
+    private String receiptStatus;
+
+    /**
+     * (付款供应商)付款状态:1待付款、2部分付款、3已付款
+     */
+    private String payStatus;
+
+    /**
+     * 发货状态:1待发货、2部分发货、3已发货
+     */
+    private String sendOutStatus;
+
+    /**
+     * 退货退款类型:1部分退、2全部退
+     */
+    private String refundType;
+
+    /**
+     * 已支付成功次数统计(适用线上多笔付款用来确认当前是哪一笔)
+     */
+    private Integer paySuccessCounter;
+
+    /**
+     * 是否已支付 未支付0 已支付1
+     */
+    private String payFlag;
+
+    /**
+     * 是否能走线上支付 0可以 1不可以 只能线下
+     */
+    private String onlinePayFlag;
+
+    /**
+     * 商品总金额 (商品单价乘以数量,再加上税费)
+     */
+    private BigDecimal productTotalFee;
+
+    /**
+     * 小计金额 (商品折后单价乘以数量,再加上税费)
+     */
+    private BigDecimal orderTotalFee;
+
+    /**
+     * 订单总额(小计金额减去经理折扣后,再加上运费)
+     */
+    private BigDecimal payTotalFee;
+
+    /**
+     * 真实支付金额(订单总额减去抵扣的账户余额)
+     */
+    private BigDecimal payableAmount;
+
+    /**
+     * 余额支付金额
+     */
+    private BigDecimal balancePayFee;
+
+    /**
+     * 总优惠 自助下单活动优惠 协销下单price-折后单价
+     */
+    private BigDecimal preferential;
+
+    /**
+     * 经理折扣
+     */
+    private BigDecimal discountFee;
+
+    /**
+     * 促销满减优惠
+     */
+    private BigDecimal promotionFullReduction;
+
+    private Long spID;
+
+    private Long mainSpID;
+
+    /**
+     * 订单备注
+     */
+    private String note;
+
+    /**
+     * 会所ID
+     */
+    private Long clubID;
+
+    /**
+     * 会所扫描确认时间
+     */
+    private String clubScanTime;
+
+    /**
+     * 支付方式,(协销订单可能会存在多种进账方式用,隔开)(v5.0版本已废弃)
+     */
+    private String payWay;
+
+    /**
+     * 订单来源:1WWW、2CRM、3APP[历史数据]、4客服、5外单、6小程序[采美,星范等]
+     */
+    private String orderSource;
+
+    /**
+     * 订单取消时间
+     */
+    private String closeTime;
+
+    /**
+     * 订单确认时间
+     */
+    private String confirmTime;
+
+    /**
+     * 订单支付时间
+     */
+    private String payTime;
+
+    /**
+     * 订单提交时间
+     */
+    private String orderTime;
+
+    /**
+     * 购买总数
+     */
+    private Integer productCount;
+
+    /**
+     * 赠送总数  不计算价格
+     */
+    private Integer presentCount;
+
+    /**
+     * 促销赠品总数
+     */
+    private Integer promotionalGiftsCount;
+
+    /**
+     * 库分期免息状态 0、免息 1、不免息[V5.0.0版本已废弃]
+     */
+    private String cooFreeFlag;
+
+    /**
+     * 库分期分期费率[V5.0.0版本已废弃]
+     */
+    private Integer cooFreeRate;
+
+    /**
+     * 库分期免息金额[V5.0.0版本已废弃]
+     */
+    private BigDecimal cooFreeAmount;
+
+    /**
+     * 是否开发票 没开发票 0 开个人发票 1 开企业发票2
+     */
+    private String invoiceFlag;
+
+    /**
+     * 订单确认标志,0否,1后台确认,2买家确认(适用协销订单并且1或2都算已确认订单,主动订单默认1为确认)
+     */
+    private String confirmFlag;
+
+    /**
+     * 条款ID
+     */
+    private Long clauseID;
+
+    /**
+     * 条款内容
+     */
+    private String clauseContent;
+
+    /**
+     * 条款名称
+     */
+    private String clauseName;
+
+    /**
+     * 更新时间
+     */
+    private String updateDate;
+
+    /**
+     * 免邮标志  运费:-1到付,0包邮,1需要运费,-2仪器到付其它包邮
+     */
+    private String freePostFlag;
+
+    /**
+     * -1到付,0包邮,大于0具体金额,-2仪器到付其它包邮(且运费已使用商品形式存储)
+     */
+    private BigDecimal freight;
+
+    /**
+     * 订单状态 0 有效  其它无效
+     */
+    private String delFlag;
+
+    /**
+     * 包邮券ID  保留字段
+     */
+    private Integer freePostageTicketID;
+
+    /**
+     * 订单是否可拆分   1可拆分 0不可拆分
+     */
+    private String splitFlag;
+
+    /**
+     * 订单取消原因
+     */
+    private String closeReason;
+
+    /**
+     * 邮费订单标识(适用于补录邮费订单) 1是邮费订单  0不是邮费订单
+     */
+    private String postageOrderFlag;
+
+    /**
+     * 第三方订单编号(绑定第三方订单关系),适用第三方发起订单
+     */
+    private String thirdPartyOrderNo;
+
+    /**
+     * 订单是否同步发货物流给第三方,0未同步,1已同步
+     */
+    private String synchronizeFlag;
+
+    private Boolean authority;
+
+    /**
+     * 返佣订单标识,0非返佣订单,1返佣订单
+     */
+    private String rebateFlag;
+
+    /**
+     * 订单0成本标识:订单不是0成本,1订单0成本(订单中所有商品成本为0)
+     */
+    private Integer zeroCostFlag;
+
+    /**
+     * 付款总金额
+     */
+    private BigDecimal receiptAmount;
+
+    /**
+     * 待付总金额
+     */
+    private BigDecimal pendingPayments;
+
+    /**
+     * 退款总金额
+     */
+    private BigDecimal returnedPurchaseFee;
+
+    /**
+     * 支付按钮是否消失,true消失
+     */
+    private boolean payButton = false;
+
+    /**
+     * 订单内是否包含确定能否开发票的商品
+     */
+    private boolean invoiceStatus = false;
+
+    /**
+     * 子订单列表
+     */
+    private List<ShopOrderVo> shopOrderList;
+
+    private static final long serialVersionUID = 1L;
+}

+ 369 - 0
src/main/java/com/caimei/model/vo/ShopOrderVo.java

@@ -0,0 +1,369 @@
+package com.caimei.model.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Data
+public class ShopOrderVo implements Serializable {
+    /**
+     * 子订单ID
+     */
+    private Integer shopOrderId;
+
+    /**
+     * 子订单编号
+     */
+    private String shopOrderNo;
+
+    /**
+     * 订单编号
+     */
+    private String orderNo;
+
+    /**
+     * 主订单ID
+     */
+    private Long orderID;
+
+    /**
+     * 采美组织默认为null,具体对应cm_mall_organize表ID
+     */
+    private Integer organizeID;
+
+    /**
+     * 用户Id
+     */
+    private Integer userID;
+
+    /**
+     * 供应商Id
+     */
+    private Integer shopID;
+
+    /**
+     * 订单促销id(主要针对店铺促销)
+     */
+    private Integer orderPromotionsId;
+
+    /**
+     * 普通订单 1 协销订单0 与cm_order一样
+     */
+    private Integer orderType;
+
+    /**
+     * 0:个人自己下单 1:企业自己下单 2:员工帮会所下单 3:协销帮会所下单  4:后台下单 5:采美豆订单
+     */
+    private Integer orderSubmitType;
+
+    /**
+     * 赠品数
+     */
+    private Integer presentNum;
+
+    /**
+     *   购买数量
+     */
+    private Integer itemCount;
+
+    /**
+     * 已经发货的商品数量
+     */
+    private Integer outStoreNum;
+
+    /**
+     * 子订单发货批次 默认值 = 0 即没过发货
+     */
+    private Integer outStoreTimes;
+
+    /**
+     * 收货地址县区Id
+     */
+    private Integer townID;
+
+    /**
+     * 子订单备注信息
+     */
+    private String note;
+
+    /**
+     * 运费:-1到付,0包邮,其他为具体运费(v5.0版本已废弃,运费已使用商品形式存储)
+     */
+    private Double fee;
+
+    /**
+     * 余额支付时使用的金额
+     */
+    private BigDecimal accountAmount;
+
+    /**
+     * 总金额 = 订单商品totalAmount
+     */
+    private BigDecimal productAmount;
+
+    /**
+     * 总价 = totalFee
+     */
+    private BigDecimal totalAmount;
+
+    /**
+     * 需要支付金额 shouldPayFee +运费
+     */
+    private BigDecimal needPayAmount;
+
+    private BigDecimal discountAmount;
+
+    /**
+     * 经理折扣(v5.0版本已废弃,经理折扣只和主订单有关)
+     */
+    private BigDecimal discountFee;
+
+    /**
+     * 订单总优惠
+     */
+    private BigDecimal preferential;
+
+    /**
+     * 促销满减优惠(不考虑凑单促销)
+     */
+    private BigDecimal promotionFullReduction;
+
+    /**
+     * 是否已支付:1是,0否
+     */
+    private String payFlag;
+
+    /**
+     * 订单提交时间
+     */
+    private String orderTime;
+
+    /**
+     * 支付时间
+     */
+    private String payTime;
+
+    /**
+     * 订单完成时间
+     */
+    private String finishTime;
+
+    /**
+     * 订单彻底完成时间 不能售后 毫秒
+     */
+    private Long autoOverTimeMills;
+
+    /**
+     * 订单状态:见表c_orderstatus或枚举OrderStatus(v5.0版本已废弃只有主订单状态)
+     */
+    private Integer status;
+
+    /**
+     * (付款供应商)付款状态:1待付款、2部分付款、3已付款
+     */
+    private String payStatus;
+
+    /**
+     * 发货状态:1待发货、2部分发货、3已发货
+     */
+    private String sendOutStatus;
+
+    private Integer refundStatus;
+
+    private Integer returnGoodsStatus;
+
+    /**
+     * 收货时间
+     */
+    private String receiveGoodsTime;
+
+    /**
+     * 自动收货时间点 毫秒计算
+     */
+    private Long autoReceiveTimeMills;
+
+    /**
+     * 总税费
+     */
+    private BigDecimal totalAddedValueTax;
+
+    /**
+     * 可退款金额 = 余额抵扣金额
+     */
+    private Double canRefundAmount;
+
+    /**
+     * 退款金额
+     */
+    private Double refundAmount;
+
+    private Integer clubID;
+
+    private Integer spID;
+
+    private Integer mainSpID;
+
+    /**
+     * 订单采美豆个数
+     */
+    private Integer orderBeanAmount;
+
+    /**
+     * 使用采美豆数量
+     */
+    private Integer useBeanAmount;
+
+    /**
+     * 是否使用采美豆
+     */
+    private Integer useBeanFlag;
+
+    /**
+     * 是否可以退货 1可以退款/退货 0不可退款/退货
+     */
+    private Integer canRefundFlag;
+
+    /**
+     * 是否使用余额
+     */
+    private Integer useBalanceFlag;
+
+    /**
+     * 可以退还的采美豆个数
+     */
+    private Integer canRefundBeans;
+
+    /**
+     * 订单包邮时本该支付的运费
+     */
+    private BigDecimal freePostageFee;
+
+    /**
+     * 使用的包邮券Id,为空表示未使用包邮券  保留
+     */
+    private Integer freePostageTicketID;
+
+    /**
+     * 佣金 =  应付采美
+     */
+    private BigDecimal brokerage;
+
+    /**
+     * 后台删除状态 0正常,其他删除
+     */
+    private String delFlag;
+
+    /**
+     * 订单退款金额
+     */
+    private BigDecimal refundsAmount;
+
+    /**
+     * 订单状态标识,1:非退货退款订单、2:退货退款中、3退货退款完成
+     */
+    private String orderStatusFlag;
+
+    /**
+     * 购买状态
+     */
+    private String buyStatus;
+
+    /**
+     * 全部发货时间
+     */
+    private String deliveryTimeMills;
+
+    private Integer orderDeliveryID;
+
+    /**
+     * 订单能否拆分 1 为可拆分, 0为不可拆分
+     */
+    private String splitFlag;
+
+    /**
+     * 是否处于给供应商状态中   0不是的,  1是的
+     */
+    private String paying;
+
+    /**
+     * 商品费
+     */
+    private BigDecimal shopProductAmount;
+
+    /**
+     * 运费
+     */
+    private BigDecimal shopPostFee;
+
+    /**
+     * 税费
+     */
+    private BigDecimal shopTaxFee;
+
+    /**
+     * 付供应商 = 商品费 + 运费 + 税费
+     */
+    private BigDecimal shouldPayShopAmount;
+
+    /**
+     * 已付款金额
+     */
+    private BigDecimal payedShopAmount;
+
+    /**
+     * 付第三方
+     */
+    private BigDecimal shopOtherFee;
+
+    private String receiptedFlag;
+
+    private String receiptedType;
+
+    /**
+     * 固定成本1,  比例成本2  为空就是还没有设置过
+     */
+    private String costType;
+
+    /**
+     * 比例成本的比例值
+     */
+    private BigDecimal proportional;
+
+    /**
+     * 修改应付必填备注信息
+     */
+    private String modifyShouldPayNote;
+
+    /**
+     * 修改应付金额的用户ID
+     */
+    private Long modifyShouldPayUserID;
+
+    /**
+     * 修改应付金额的时间
+     */
+    private Date modifyShouldPayDate;
+
+    /**
+     * 子订单0成本标识:0子订单不是0成本,1子订单0成本(子订单中所有商品成本为0)
+     */
+    private Integer zeroCostFlag;
+
+    /**
+     * 供应商logo
+     */
+    private String shopLogo;
+
+    private List<OrderProductVo> orderProductList;
+
+    private static final long serialVersionUID = 1L;
+}

+ 79 - 0
src/main/java/com/caimei/model/vo/SplitAccountVo.java

@@ -0,0 +1,79 @@
+package com.caimei.model.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Data
+public class SplitAccountVo {
+    private Integer id;
+
+    /**
+     * 主订单id
+     */
+    private Integer orderId;
+
+    /**
+     * 商品id,仅对二手发布商品
+     */
+    private Integer productId;
+
+    /**
+     * 订单商品id
+     */
+    private Integer orderProductId;
+
+    /**
+     * 供应商id
+     */
+    private Integer shopId;
+
+    /**
+     * 分账类型:1公账-专票,2私账-无票,3公账-普票,4供应商子商户
+     */
+    private String type;
+
+    /**
+     * 子商户商编
+     */
+    private String subUserNo;
+
+    /**
+     * 分账金额
+     */
+    private BigDecimal splitAccount;
+
+    /**
+     * 米花科技平台唯一流水号
+     */
+    private String mbOrderId;
+
+    /**
+     * 商户唯一订单请求号(订单编号#随机时间戳)
+     */
+    private String orderRequestNo;
+
+    /**
+     * 付款状态,0待付,1付款成功
+     */
+    private String payStatus;
+
+    /**
+     * 商品类型:1商品成本,2供应商运费,3佣金,4二手发布
+     */
+    private String productType;
+
+    /**
+     * 分账时间
+     */
+    private Date splitTime;
+
+    private static final long serialVersionUID = 1L;
+}

+ 25 - 0
src/main/java/com/caimei/service/OrderService.java

@@ -0,0 +1,25 @@
+package com.caimei.service;
+
+import com.caimei.model.ResponseJson;
+import com.caimei.model.vo.OrderVo;
+import com.github.pagehelper.PageInfo;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+public interface OrderService {
+    /**
+     * 订单数据
+     *
+     * @param orderState
+     * @param userId
+     * @param organizeId
+     * @param pageNum
+     * @param pageSize
+     * @return
+     */
+    ResponseJson<PageInfo<OrderVo>> getOrderList(Integer orderState, Integer userId, Integer organizeId, Integer pageNum, Integer pageSize);
+}

+ 82 - 0
src/main/java/com/caimei/service/PayOrderService.java

@@ -0,0 +1,82 @@
+package com.caimei.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.dto.PaymentDto;
+import com.caimei.model.vo.OrderPayLinkVo;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.reactive.function.server.ServerRequest;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2020/5/9
+ */
+public interface PayOrderService {
+    /**
+     * 获取线上支付开关状态
+     *
+     * @return
+     */
+    ResponseJson<Integer> getPayOnLineSwitch();
+
+    /**
+     * 收银台数据
+     *
+     * @param orderId 订单id
+     * @return
+     */
+    ResponseJson<Map<String, Object>> checkoutCounter(Integer orderId);
+
+    /**
+     * 线上支付
+     *
+     * @param payment 支付参数对象
+     * @return
+     */
+    ResponseJson<JSONObject> pay(PaymentDto payment);
+
+    /**
+     * 异步回调通知
+     */
+    String paymentCallback(String data) throws Exception;
+
+    /**
+     * 小程序生成链接
+     */
+    ResponseJson<String> payLink(OrderPayLinkVo orderPayLink);
+
+    /**
+     * 重定向到收银台页面
+     */
+    void jumpPage(String linkLogo, String linkPage, ServerHttpResponse response) throws IOException;
+
+    /**
+     * 链接数据
+     *
+     * @param linkLogo
+     * @return
+     */
+    ResponseJson<Map<String, Object>> linkData(String linkLogo);
+
+    /**
+     * 判断此次支付是否完成
+     *
+     * @param orderId           订单id
+     * @param paySuccessCounter 付款次数
+     * @return
+     */
+    ResponseJson<String> payWhetherSuccess(Integer orderId, Integer paySuccessCounter);
+
+    /**
+     * 查询第三方订单状态
+     *
+     * @param mbOrderId 平台唯一流水号
+     * @return
+     */
+    ResponseJson<JSONObject> findOrderStatus(String mbOrderId);
+}

+ 2 - 18
src/main/java/com/caimei/service/impl/LoginServiceImpl.java

@@ -2,6 +2,7 @@ package com.caimei.service.impl;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.caimei.config.WxConfig;
 import com.caimei.mapper.LoginMapper;
 import com.caimei.model.ResponseJson;
 import com.caimei.model.dto.BuyerUserDto;
@@ -32,28 +33,11 @@ public class LoginServiceImpl implements LoginService {
     @Resource
     private LoginMapper loginMapper;
 
-    @Value("${wx.AppId}")
-    private String appId;
-
-    @Value("${wx.AppSecret}")
-    private String appSecret;
-
     @Override
     public ResponseJson<BuyerUserVo> authorizationLogin(String code) {
-        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
-        Map<String, String> requestUrlParam = new HashMap<>(4);
-        //小程序appId
-        requestUrlParam.put("appid", appId);
-        //小程序appSecret
-        requestUrlParam.put("secret", appSecret);
-        //小程序端返回的code
-        requestUrlParam.put("js_code", code);
-        //默认参数
-        requestUrlParam.put("grant_type", "authorization_code");
-        //发送post请求读取调用微信接口获取openid用户唯一标识
         String infos;
         try {
-            infos = HttpRequest.sendPost(requestUrl, requestUrlParam);
+            infos = WxConfig.getWxMallOpenId(code);
         } catch (Exception e) {
             log.info("微信服务器异常", e);
             return ResponseJson.error("微信服务器异常", null);

+ 125 - 0
src/main/java/com/caimei/service/impl/OrderServiceImpl.java

@@ -0,0 +1,125 @@
+package com.caimei.service.impl;
+
+import com.caimei.mapper.OrderMapper;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.enumerate.ReceivablesType;
+import com.caimei.model.po.PromotionsPo;
+import com.caimei.model.vo.*;
+import com.caimei.service.OrderService;
+import com.caimei.util.MathUtil;
+import com.caimei.util.ProductUtils;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Service
+public class OrderServiceImpl implements OrderService {
+    @Resource
+    private OrderMapper orderMapper;
+    @Value("${caimei.oldapi}")
+    private String domain;
+
+    @Override
+    public ResponseJson<PageInfo<OrderVo>> getOrderList(Integer orderState, Integer userId, Integer organizeId, Integer pageNum, Integer pageSize) {
+        PageHelper.startPage(pageNum, pageSize);
+        List<OrderVo> orderList = orderMapper.findOrderList(userId, orderState, organizeId);
+        for (OrderVo order : orderList) {
+            //111,待付待收待发
+            if ("11".equals(order.getStatus()) && "1".equals(order.getPayStatus())) {
+                order.setStatus("111");
+            }
+            //判断交易全退情况下,是否发过货,77,交易全退可以查看物流
+            List<LogisticsBatchVo> batchList = orderMapper.findLogistics(order.getOrderId());
+            if ("7".equals(order.getStatus()) && batchList != null && batchList.size() > 0) {
+                order.setStatus("77");
+            }
+            List<ShopOrderVo> shopOrderList = orderMapper.findAllShopOrder(order.getOrderId());
+            for (ShopOrderVo shopOrder : shopOrderList) {
+                List<OrderProductVo> orderProductList = orderMapper.findOrderProduct(shopOrder.getShopOrderId());
+                for (OrderProductVo orderProduct : orderProductList) {
+                    orderProduct.setProductImage(ProductUtils.getImageURL("product", orderProduct.getProductImage(), 0, domain));
+                    //不含税可开票商品,单价/折后单价=税前单价+税费
+                    if ("0".equals(orderProduct.getIncludedTax()) && ("1".equals(orderProduct.getInvoiceType()) || "2".equals(orderProduct.getInvoiceType()))) {
+                        BigDecimal valueTax = MathUtil.div(MathUtil.mul(orderProduct.getPrice(), orderProduct.getTaxRate()), 100);
+                        orderProduct.setPrice(MathUtil.add(orderProduct.getPrice(), valueTax));
+                        orderProduct.setDiscountPrice(MathUtil.add(orderProduct.getPrice(), orderProduct.getAddedValueTax()));
+                    }
+                    //查询订单下商品的促销活动
+                    if (orderProduct.getOrderPromotionsId() != null && orderProduct.getOrderPromotionsId() > 0) {
+                        PromotionsPo promotions = orderMapper.findOrderPromotionsById(orderProduct.getOrderPromotionsId());
+                        orderProduct.setProductPromotion(promotions);
+                        if (promotions != null && promotions.getType() == 1 && promotions.getMode() == 1) {
+                            if ("0".equals(orderProduct.getIncludedTax()) && ("1".equals(orderProduct.getInvoiceType()) || "2".equals(orderProduct.getInvoiceType()))) {
+                                promotions.setTouchPrice(MathUtil.add(promotions.getTouchPrice(), orderProduct.getAddedValueTax()));
+                            }
+                        }
+                    }
+                }
+                shopOrder.setOrderProductList(orderProductList);
+                shopOrder.setShopLogo(ProductUtils.getImageURL("shopLogo", shopOrder.getShopLogo(), 0, domain));
+            }
+            //过滤运费商品
+            shopOrderList.removeIf(shopOrderVo -> shopOrderVo.getShopID() == 998);
+            order.setShopOrderList(shopOrderList);
+            //设置付款金额
+            getDiscernReceipt(order);
+        }
+        PageInfo<OrderVo> pageData = new PageInfo<>(orderList);
+        return ResponseJson.success(pageData);
+    }
+
+    /**
+     * 支付金额,待付金额
+     */
+    public List<DiscernReceiptVo> getDiscernReceipt(OrderVo order) {
+        //支付记录
+        List<DiscernReceiptVo> discernReceiptList = orderMapper.findDiscernReceipt(order.getOrderId());
+        BigDecimal receiptAmount = BigDecimal.ZERO;
+        //订单款
+        if (discernReceiptList != null && discernReceiptList.size() > 0) {
+            for (DiscernReceiptVo discernReceipt : discernReceiptList) {
+                receiptAmount = receiptAmount.add(discernReceipt.getAssociateAmount());
+                if (discernReceipt.getPayType() != null) {
+                    discernReceipt.setPayType(ReceivablesType.getReceivablesType(Integer.parseInt(discernReceipt.getPayType())));
+                }
+            }
+        }
+        //判断是否可以走线上支付
+        DiscernReceiptVo discernReceipt = orderMapper.findOfflinePayment(order.getOrderId());
+        if (null == discernReceipt) {
+            order.setOnlinePayFlag("0");
+        } else {
+            order.setOnlinePayFlag("1");
+        }
+        //付供应商总金额 + 默认手续费 > 订单总金额
+        BigDecimal payTotalFee = order.getPayTotalFee();
+        BigDecimal handlingFee = MathUtil.mul(payTotalFee, 0.0038, 2);
+        if (MathUtil.compare(handlingFee, 8) < 0) {
+            handlingFee = new BigDecimal(8);
+        }
+        List<ShopOrderVo> shopOrderList = orderMapper.findAllShopOrder(order.getOrderId());
+        for (ShopOrderVo shopOrder : shopOrderList) {
+            handlingFee = MathUtil.add(handlingFee, shopOrder.getShouldPayShopAmount());
+        }
+        if (MathUtil.compare(payTotalFee, handlingFee) < 0) {
+            order.setPayButton(true);
+        }
+        //待付总金额
+        order.setPendingPayments(MathUtil.sub(order.getPayableAmount(), receiptAmount));
+        //支付总金额
+        order.setReceiptAmount(receiptAmount);
+        return discernReceiptList;
+    }
+}
+

+ 2 - 0
src/main/java/com/caimei/service/impl/OrderSubmitServiceImpl.java

@@ -17,6 +17,7 @@ import com.caimei.util.ProductUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.interceptor.TransactionAspectSupport;
 import org.springframework.util.CollectionUtils;
 
@@ -108,6 +109,7 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public ResponseJson<Map<String, String>> orderSubmit(Integer cartType, String orderSource, Integer clubUserId, Integer addressId, List<Map<String, Object>> orderInfo, Map<String, Object> payInfo, Map<String, Object> orderInvoice) {
         /*
          * 逻辑处理 start

+ 655 - 0
src/main/java/com/caimei/service/impl/PayOrderServiceImpl.java

@@ -0,0 +1,655 @@
+package com.caimei.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.mapper.PayOrderMapper;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.dto.PaymentDto;
+import com.caimei.model.po.*;
+import com.caimei.model.vo.*;
+import com.caimei.service.PayOrderService;
+import com.caimei.util.MathUtil;
+import com.caimei.util.Md5Util;
+import com.caimei.util.PayUtils;
+import com.caimei.util.RandomCodeGenerator;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2020/5/9
+ */
+@Service
+@Slf4j
+public class PayOrderServiceImpl implements PayOrderService {
+    @Resource
+    private PayOrderMapper payOrderMapper;
+
+    /**
+     * 商户标识
+     */
+    String merAccount = "aa9aee6a148843a6a9e4ea117df4454b";
+    /**
+     * 私钥,商户密钥
+     */
+    String merKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM0qCTZFdi1I59/Jeis+8KVhzSNhsRrKiOlHazIVyxNLzUQvFpWN5PlfVKlnJToorURTStfjAv01HD0Z4ZvMauuHhDT0bptiDln928Ld6SzX889X2nsCxl0Q+WzrkFsnT6gotvGnTeQGUgbBV3SQD3IUOwRwxoWYbrZqxtqHFxqRAgMBAAECgYEAvsHx9MMbAToDVmEXtXP8/lh0Cwy/RgDA0d30voni2pslTNtXbVCUcIUBy8y6oVvG1nt3YEmTsuiZy/nvehPT6GV1Gqj8T6lqWQ8KQKsDGlubvh23tzNM90me2TLZEPdNqC5CPRrwYrjght4BXgzu7s2+5FpeYCob1gVNi+w0Jz0CQQD0dXb9Oez+Ybnxb3rCghGWM6cxi8fsqk6MuKaleg53qfXrkgAVH78faeWzRaeSbVOh5+Z9kX5HUeynfM7E/f4nAkEA1tmnvJp4JQaouO1Trzbnkhowjea5daK/tDE8K0hIMHUjAw+c1QTteGOVGBFBHWPkUwkSCd2HKmk4URkp/snMhwJAO32+qF+Jclq8EqqLmHxo5UHKxX7793d2yD5Dp++tR6fgBiUwyfNA4tc1pEwmPLdIbBVwfUyEC70/N39jHoOlbwI/dX6SPJI9IgKCQp+HJEriWQP5iaCjy7E1JVXHkeP1lop4mzPukJAhTbUn1AGbmncGZmKPetWrFYZ1ReR9EtlJAkEA2kBmARWyOTt8fbikImuHr65M+BDgD+2fkuDb5+tqJljNmSqAIRRwcXj1Je6vkzlDJuSw3wlG6iliFtwe0cq7+w==";
+    /**
+     * 公钥
+     */
+    String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNKgk2RXYtSOffyXorPvClYc0jYbEayojpR2syFcsTS81ELxaVjeT5X1SpZyU6KK1EU0rX4wL9NRw9GeGbzGrrh4Q09G6bYg5Z/dvC3eks1/PPV9p7AsZdEPls65BbJ0+oKLbxp03kBlIGwVd0kA9yFDsEcMaFmG62asbahxcakQIDAQAB";
+    /**
+     * 用户编号
+     */
+    String merNo = "10001720";
+    /**
+     * 公账-专票,子商户商编
+     */
+    String publicAccountNo = "20001793";
+    /**
+     * 公账-普票,子商户商编
+     */
+    String commonInvoiceNo = "20001754";
+    /**
+     * 私账-无票,子商户商编
+     */
+    String privateAccountNo = "20001924";
+
+    /**
+     * 获取线上支付开关状态
+     *
+     * @return
+     */
+    @Override
+    public ResponseJson<Integer> getPayOnLineSwitch() {
+        Integer status = payOrderMapper.getPayOnLineSwitch();
+        return ResponseJson.success(status);
+    }
+
+    @Override
+    public ResponseJson<Map<String, Object>> checkoutCounter(Integer orderId) {
+        Map<String, Object> map = new HashMap<>();
+        OrderVo order = payOrderMapper.findOrder(orderId);
+        if (null == order) {
+            return ResponseJson.error("订单不存在", null);
+        }
+        map.put("order", order);
+        //支付记录
+        List<DiscernReceiptVo> discernReceiptList = payOrderMapper.getDiscernReceipt(order);
+        BigDecimal receiptAmount = BigDecimal.ZERO;
+        if (null != discernReceiptList && discernReceiptList.size() > 0) {
+            for (DiscernReceiptVo discernReceipt : discernReceiptList) {
+                if ("2".equals(discernReceipt.getPayWay())) {
+                    return ResponseJson.error("线下支付过只能走线下", null);
+                }
+                if ("3".equals(discernReceipt.getReceiptStatus())) {
+                    receiptAmount = receiptAmount.add(discernReceipt.getAssociateAmount());
+                }
+            }
+        }
+        order.setReceiptAmount(receiptAmount);
+        map.put("discernReceipt", discernReceiptList);
+        //商品数据
+        List<OrderProductVo> orderProductList = payOrderMapper.findAllOrderProduct(orderId);
+        orderProductList.forEach(o -> {
+            if (StringUtils.isBlank(o.getIncludedTax()) || StringUtils.isBlank(o.getInvoiceType()) || "2".equals(o.getIncludedTax())
+                    || ("2".equals(order.getInvoiceFlag()) && "0".equals(o.getIncludedTax()) && "3".equals(o.getInvoiceType()))) {
+                //订单选择开发票,商品属性:不含税-不能开票
+                order.setInvoiceStatus(true);
+            }
+        });
+        //过滤运费商品
+        orderProductList.removeIf(orderProductVo -> orderProductVo.getShopId() == 998);
+        map.put("orderProductList", orderProductList);
+        //机构信息
+        UserPo user = payOrderMapper.findUser(order.getUserID());
+        map.put("userName", user.getName());
+        return ResponseJson.success(map);
+    }
+
+    @Override
+    public synchronized ResponseJson<JSONObject> pay(PaymentDto payment) {
+        JSONObject result = null;
+        OrderVo order = payOrderMapper.findOrder(payment.getOrderId());
+        if (null == order) {
+            return ResponseJson.error("订单不存在", null);
+        }
+        if ("0".equals(order.getStatus()) || "33".equals(order.getStatus()) || "4".equals(order.getStatus()) || "5".equals(order.getStatus())
+                || "6".equals(order.getStatus()) || "7".equals(order.getStatus())) {
+            return ResponseJson.error("订单状态错误", null);
+        }
+        List<DiscernReceiptVo> discernReceiptList = payOrderMapper.getDiscernReceipt(order);
+        if (null != discernReceiptList && discernReceiptList.size() > 0) {
+            for (DiscernReceiptVo discernReceipt : discernReceiptList) {
+                if ("2".equals(discernReceipt.getPayWay())) {
+                    return ResponseJson.error("线下支付过只能走线下", null);
+                }
+            }
+        }
+        log.info("本次付款金额>>>>>" + payment.getPayAmount());
+        if (MathUtil.compare(MathUtil.mul(order.getPayableAmount(), 100), payment.getPayAmount()) < 0) {
+            return ResponseJson.error("付款金额错误", null);
+        }
+        //List<SplitAccountVo> splitBillDetail = null;
+        try {
+            // 时间戳
+            long time = System.currentTimeMillis() / 1000;
+            JSONObject json = getPayParameter(payment, time);
+            // 商户订单号
+            String orderId = order.getOrderNo() + "T" + time;
+            json.put("orderId", orderId);
+            //商品名称
+            String product = "采美订单" + order.getOrderNo();
+            json.put("product", product);
+            //支付类型
+            String payType = getPayType(payment);
+            String attach = order.getOrderId() + "," + payType;
+            json.put("attach", attach);
+            //分账详情
+            /*splitBillDetail = splitBillDetail(order, payment);
+            String parameters = ledgerParameters(splitBillDetail, order.getOrderID());
+            log.info("分账参数: " + parameters);
+            json.put("splitBillDetail", parameters);*/
+            String sign = PayUtils.buildSign(json, merKey);
+            json.put("sign", sign);
+            String data = PayUtils.buildDataPrivate(json, merKey);
+            result = PayUtils.httpGet("https://platform.mhxxkj.com/paygateway/mbpay/order/v1", merAccount, data);
+        } catch (Exception e) {
+            log.error("错误信息", e);
+            return ResponseJson.error("支付失败", null);
+        }
+        String code = result.getString("code");
+        if (!"000000".equals(code)) {
+            String msg = result.getString("msg");
+            log.info("第三方支付失败>>>>>>>msg:" + msg);
+            return ResponseJson.error(msg, null);
+        }
+
+        //保存分账信息
+        //saveSplitBillDetail(splitBillDetail, result);
+        return ResponseJson.success(result);
+    }
+
+    /**
+     * 整理第三方支付详情参数
+     */
+    private String ledgerParameters(List<SplitAccountVo> splitBillDetail, Integer orderId) {
+        List<Map<String, String>> maps = new ArrayList<>();
+        List<ShopOrderVo> shopOrderList = payOrderMapper.findShopOrder(orderId);
+        //供应商子商户总金额
+        for (ShopOrderVo shopOrder : shopOrderList) {
+            BigDecimal shopTotalAmount = BigDecimal.ZERO;
+            String subUserNo = "";
+            for (SplitAccountVo account : splitBillDetail) {
+                if ("4".equals(account.getType()) && shopOrder.getShopID().equals(account.getShopId())) {
+                    shopTotalAmount = MathUtil.add(shopTotalAmount, account.getSplitAccount());
+                    subUserNo = account.getSubUserNo();
+                }
+            }
+            addMaps(maps, shopTotalAmount, subUserNo);
+        }
+        //公账-专票总金额,私账-无票总金额,公账-普票总金额
+        BigDecimal totalAmount1 = BigDecimal.ZERO;
+        BigDecimal totalAmount2 = BigDecimal.ZERO;
+        BigDecimal totalAmount3 = BigDecimal.ZERO;
+        for (SplitAccountVo account : splitBillDetail) {
+            if ("1".equals(account.getType())) {
+                totalAmount1 = MathUtil.add(totalAmount1, account.getSplitAccount());
+            } else if ("2".equals(account.getType())) {
+                totalAmount2 = MathUtil.add(totalAmount2, account.getSplitAccount());
+            } else if ("3".equals(account.getType())) {
+                totalAmount3 = MathUtil.add(totalAmount3, account.getSplitAccount());
+            }
+        }
+        addMaps(maps, totalAmount1, publicAccountNo);
+        addMaps(maps, totalAmount2, privateAccountNo);
+        addMaps(maps, totalAmount3, commonInvoiceNo);
+        return JSON.toJSONString(maps);
+    }
+
+    private void addMaps(List<Map<String, String>> maps, BigDecimal shopTotalAmount, String subUserNo) {
+        if (MathUtil.compare(shopTotalAmount, 0) > 0) {
+            Map<String, String> map = new HashMap<>(3);
+            map.put("subUserNo", subUserNo);
+            map.put("splitBillType", "2");
+            map.put("splitBillValue", MathUtil.mul(shopTotalAmount, 100).toString());
+            maps.add(map);
+        }
+    }
+
+    /**
+     * 保存分账信息
+     */
+    private void saveSplitBillDetail(List<SplitAccountVo> splitBillDetail, JSONObject result) {
+        JSONObject data = result.getJSONObject("data");
+        String mbOrderId = data.getString("mbOrderId");
+        String orderRequestNo = data.getString("orderId");
+        for (SplitAccountVo splitAccount : splitBillDetail) {
+            splitAccount.setMbOrderId(mbOrderId);
+            splitAccount.setOrderRequestNo(orderRequestNo);
+            splitAccount.setPayStatus("0");
+            payOrderMapper.insertSplitAccount(splitAccount);
+        }
+    }
+
+    /**
+     * 设置第三方支付参数
+     */
+    private JSONObject getPayParameter(PaymentDto payment, long time) throws Exception {
+        String userType = "ENTERPRISE";
+        JSONObject json = new JSONObject();
+        json.put("merAccount", merAccount);
+        json.put("merNo", merNo);
+        json.put("time", time);
+        //支付金额
+        json.put("amount", payment.getPayAmount());
+        json.put("payWay", payment.getPayWay());
+        json.put("payType", payment.getPayType());
+        json.put("userIp", payment.getClientIp());
+        json.put("returnUrl", payment.getReturnUrl());
+        json.put("notifyUrl", payment.getNotifyUrl());
+        if (null != payment.getBankCode()) {
+            json.put("bankCode", payment.getBankCode());
+        }
+        json.put("userType", userType);
+        if (null != payment.getOrderId()) {
+            json.put("openId", payment.getOpenid());
+        }
+        return json;
+    }
+
+    /**
+     * 分账详情
+     */
+    private List<SplitAccountVo> splitBillDetail(OrderVo order, PaymentDto payment) {
+        List<SplitAccountVo> list = new ArrayList<>();
+        //本次支付金额,单位/元
+        BigDecimal payAmount = MathUtil.div(payment.getPayAmount(), 100);
+        //待分账总金额
+        BigDecimal splitAmount = payAmount;
+        //总手续费
+        BigDecimal procedureFee = BigDecimal.ZERO;
+        if ("UNIONPAY".equals(payment.getPayWay())) {
+            procedureFee = new BigDecimal(8);
+        } else {
+            //手续费
+            procedureFee = MathUtil.mul(payAmount, 0.0038, 2);
+            if (MathUtil.compare(procedureFee, 0) == 0) {
+                procedureFee = new BigDecimal("0.01");
+            }
+        }
+        splitAmount = MathUtil.sub(splitAmount, procedureFee);
+        List<OrderProductVo> orderProductList = payOrderMapper.findAllOrderProduct(order.getOrderId());
+        for (OrderProductVo orderProduct : orderProductList) {
+            BigDecimal costPrice = MathUtil.mul(orderProduct.getCostPrice(), orderProduct.getNum());
+            //不含税能开发票
+            if ("0".equals(orderProduct.getIncludedTax()) && !"3".equals(orderProduct.getInvoiceType())) {
+                //应付总税费
+                BigDecimal payableTax = MathUtil.mul(orderProduct.getSingleShouldPayTotalTax(), orderProduct.getNum());
+                costPrice = MathUtil.add(costPrice, payableTax);
+            }
+            //判断是否支付过
+            BigDecimal paidAmount = payOrderMapper.findPaidAmount(orderProduct.getOrderProductId());
+            if (paidAmount == null || MathUtil.compare(paidAmount, costPrice) < 0) {
+                if (paidAmount != null && MathUtil.compare(paidAmount, 0) > 0) {
+                    costPrice = MathUtil.sub(costPrice, paidAmount);
+                }
+                //待分账金额>=本次待分账金额
+                if (MathUtil.compare(splitAmount, costPrice) > -1) {
+                    splitAmount = MathUtil.sub(splitAmount, costPrice);
+                } else {
+                    costPrice = splitAmount;
+                    splitAmount = BigDecimal.ZERO;
+                }
+                String commercialCode = payOrderMapper.findCommercialCode(orderProduct.getShopId());
+                SplitAccountVo splitAccount = new SplitAccountVo();
+                splitAccount.setOrderId(order.getOrderId());
+                splitAccount.setOrderProductId(orderProduct.getOrderProductId());
+                splitAccount.setShopId(orderProduct.getShopId().intValue());
+                splitAccount.setSplitAccount(costPrice);
+                splitAccount.setProductType("1");
+                if (StringUtils.isNotBlank(commercialCode)) {
+                    //供应商拥有子商户号
+                    splitAccount.setType("4");
+                    splitAccount.setSubUserNo(commercialCode);
+                } else {
+                    if ("3".equals(orderProduct.getInvoiceType())) {
+                        //不能开票,则分账到私账-无票
+                        splitAccount.setType("2");
+                        splitAccount.setSubUserNo(privateAccountNo);
+                    } else if ("1".equals(orderProduct.getInvoiceType())) {
+                        //开增值税发票,则分账到公账-专票
+                        splitAccount.setType("1");
+                        splitAccount.setSubUserNo(publicAccountNo);
+                    } else if ("2".equals(orderProduct.getInvoiceType())) {
+                        //开普通发票,则分账到公账-普票
+                        splitAccount.setType("3");
+                        splitAccount.setSubUserNo(commonInvoiceNo);
+                    }
+                }
+                list.add(splitAccount);
+                if (MathUtil.compare(splitAmount, 0) == 0) {
+                    break;
+                }
+            }
+        }
+
+        //付供应商运费,是以供应商为单位的
+        if (MathUtil.compare(splitAmount, 0) > 0) {
+            List<ShopOrderVo> shopOrderList = payOrderMapper.findShopOrder(order.getOrderId());
+            for (ShopOrderVo shopOrder : shopOrderList) {
+                //运费
+                BigDecimal shopPostFee = shopOrder.getShopPostFee();
+                if (MathUtil.compare(shopPostFee, 0) > 0) {
+                    BigDecimal shipping = payOrderMapper.findShipping(order.getOrderId(), shopOrder.getShopID());
+                    shopPostFee = MathUtil.sub(shopPostFee, shipping);
+                    if (MathUtil.compare(splitAmount, shopPostFee) > -1) {
+                        splitAmount = MathUtil.sub(splitAmount, shipping);
+                    } else {
+                        shopPostFee = splitAmount;
+                        splitAmount = BigDecimal.ZERO;
+                    }
+                    String commercialCode = payOrderMapper.findCommercialCode(Long.valueOf(shopOrder.getShopID()));
+                    SplitAccountVo splitAccount = new SplitAccountVo();
+                    splitAccount.setOrderId(order.getOrderId());
+                    splitAccount.setShopId(shopOrder.getShopID());
+                    splitAccount.setSplitAccount(shopPostFee);
+                    splitAccount.setProductType("2");
+                    if (StringUtils.isNotBlank(commercialCode)) {
+                        //供应商拥有子商户号
+                        splitAccount.setType("4");
+                        splitAccount.setSubUserNo(commercialCode);
+                    } else {
+                        //私账
+                        splitAccount.setType("2");
+                        splitAccount.setSubUserNo(privateAccountNo);
+                    }
+                    list.add(splitAccount);
+                }
+            }
+        }
+
+        //佣金,私账
+        if (MathUtil.compare(splitAmount, 0) > 0) {
+            SplitAccountVo splitAccount = new SplitAccountVo();
+            splitAccount.setOrderId(order.getOrderId());
+            splitAccount.setSplitAccount(splitAmount);
+            splitAccount.setProductType("3");
+            splitAccount.setType("2");
+            splitAccount.setSubUserNo(privateAccountNo);
+            list.add(splitAccount);
+        }
+        return list;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String paymentCallback(String data) throws Exception {
+        //公钥解密
+        JSONObject json = PayUtils.decryptDataPublic(data, publicKey);
+        log.info("公钥解密>>>>>>" + json);
+        //公钥验签
+        String signaa = json.getString("sign");
+        json.remove("sign");
+        String signbb = PayUtils.buildSign(json, publicKey);
+        if (!signaa.equals(signbb)) {
+            return "验签失败";
+        }
+        //订单状态
+        String orderStatus = json.getString("orderStatus");
+        //附加数据,下单时若有传输则原样返回,下单时为空,则不返回该数据
+        String attach = json.getString("attach");
+        //平台唯一流水号
+        String mbOrderId = json.getString("mbOrderId");
+        //商户唯一订单号
+        String orderRequestNo = json.getString("orderId");
+        //订单金额,以元为单位
+        BigDecimal amount = json.getBigDecimal("amount");
+        log.info("订单状态>>>>>>" + orderStatus);
+        if ("FAILED".equals(orderStatus)) {
+            return "支付失败";
+        }
+        String[] split = attach.split(",");
+        //订单id
+        Integer orderId = Integer.valueOf(split[0]);
+        //支付类型
+        String payType = split[1];
+        OrderVo order = payOrderMapper.findOrder(orderId);
+        //支付记录
+        List<DiscernReceiptVo> discernReceiptList = payOrderMapper.getDiscernReceipt(order);
+        BigDecimal receiptAmount = BigDecimal.ZERO;
+        if (null != discernReceiptList && discernReceiptList.size() > 0) {
+            for (DiscernReceiptVo discernReceipt : discernReceiptList) {
+                receiptAmount = receiptAmount.add(discernReceipt.getAssociateAmount());
+            }
+        }
+        //已付金额+本次支付金额
+        receiptAmount = MathUtil.add(receiptAmount, amount);
+        log.info("已付金额+本次支付金额>>>>>>>" + receiptAmount);
+        if (MathUtil.compare(order.getPayableAmount(), receiptAmount) == 0) {
+            /*
+             * 订单全部支付
+             * 0待确认,11待收待发,12待收部发,13待收全发,21部收待发,22部收部发,23部收全发,
+             * 31已收待发,32已收部发,33已收全发,4交易完成,5订单完成,6已关闭,7交易全退
+             */
+            if ("11".equals(order.getStatus()) || "21".equals(order.getStatus())) {
+                order.setStatus("31");
+            } else if ("12".equals(order.getStatus()) || "22".equals(order.getStatus())) {
+                order.setStatus("32");
+            } else {
+                order.setStatus("33");
+            }
+            order.setPayFlag("1");
+            order.setOnlinePayFlag("0");
+            //(收款买家)收款状态:1待收款、2部分收款、3已收款
+            order.setReceiptStatus("3");
+            log.info("订单全部支付,修改订单状态>>>>>>" + order.getStatus());
+        } else {
+            //部分支付
+            if ("11".equals(order.getStatus()) || "21".equals(order.getStatus())) {
+                order.setStatus("21");
+            } else if ("12".equals(order.getStatus()) || "22".equals(order.getStatus())) {
+                order.setStatus("22");
+            } else {
+                order.setStatus("23");
+            }
+            order.setOnlinePayFlag("0");
+            //(收款买家)收款状态:1待收款、2部分收款、3已收款
+            order.setReceiptStatus("2");
+        }
+        //更新付款次数
+        order.setPaySuccessCounter(order.getPaySuccessCounter() + 1);
+        payOrderMapper.updateSelective(order);
+        //修改支付链接状态
+        OrderPayLinkVo orderPayLink = payOrderMapper.findOrderPayLink(order.getOrderId(), amount);
+        if (null != orderPayLink && ("12".equals(payType) || "17".equals(payType))) {
+            orderPayLink.setPayStatus("1");
+            payOrderMapper.updateOrderPayLink(orderPayLink);
+        }
+        //保存收款记录
+        String curDateStr = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date());
+        CmDiscernReceiptPo discernReceipt = new CmDiscernReceiptPo();
+        discernReceipt.setPayWay("1");
+        discernReceipt.setPayType(payType);
+        discernReceipt.setReceiptType("1");
+        discernReceipt.setReceiptStatus("3");
+        discernReceipt.setReceiptAmount(amount);
+        discernReceipt.setConfirmType("4");
+        discernReceipt.setRePayFlag("1");
+        discernReceipt.setFormData(json.toJSONString());
+        discernReceipt.setReceiptDate(curDateStr);
+        discernReceipt.setConfirmDate(curDateStr);
+        discernReceipt.setReviewDate(curDateStr);
+        discernReceipt.setUpdateDate(curDateStr);
+        discernReceipt.setDelFlag("0");
+        payOrderMapper.insertDiscernReceipt(discernReceipt);
+        CmReceiptOrderRelationPo relation = new CmReceiptOrderRelationPo();
+        relation.setReceiptID(discernReceipt.getId().intValue());
+        relation.setOrderID(order.getOrderId());
+        relation.setAssociateAmount(amount);
+        relation.setRelationType("2");
+        relation.setMbOrderId(mbOrderId);
+        relation.setOrderRequestNo(orderRequestNo);
+        relation.setDelFlag("0");
+        relation.setSplitStatus("0");
+        payOrderMapper.insertOrderRelation(relation);
+        log.info(">>>>>>>>>>>>>>>>>>>>>>>保存付款金额到收款记录," + amount);
+        //修改分账付款状态
+        //payOrderMapper.updateSplitAccountByPay(mbOrderId);
+        return "SUCCESS";
+    }
+
+    /**
+     * 支付状态转换
+     */
+    private String getPayType(PaymentDto payment) {
+        String payType = payment.getPayType();
+        if ("ALIPAY_H5".equals(payType)) {
+            //支付宝H5
+            return "14";
+        } else if ("JSAPI_WEIXIN".equals(payType)) {
+            //微信公众号支付
+            return "13";
+        } else if ("MINIAPP_WEIXIN".equals(payType)) {
+            //微信小程序支付
+            return "15";
+        } else if ("GATEWAY_UNIONPAY".equals(payType) && "ENTERPRISE".equals(payment.getUserType())) {
+            //企业网银
+            return "12";
+        } else {
+            //个人网银
+            return "17";
+        }
+    }
+
+    @Override
+    public ResponseJson<String> payLink(OrderPayLinkVo orderPayLink) {
+        if (null == orderPayLink) {
+            return ResponseJson.error("参数异常", null);
+        }
+        OrderVo order = payOrderMapper.findOrder(orderPayLink.getOrderId().intValue());
+        if (null == order) {
+            return ResponseJson.error("订单不存在", null);
+        }
+        // 时间戳
+        long time = System.currentTimeMillis() / 1000;
+        String linkLogo = RandomCodeGenerator.generateCodeString(4);
+        try {
+            linkLogo = Md5Util.MD5To16Bit(linkLogo + time);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        orderPayLink.setLinkLogo(linkLogo);
+        Date date = new Date();
+        GregorianCalendar calendar = new GregorianCalendar();
+        calendar.setTime(date);
+        calendar.add(Calendar.DATE, 1);
+        orderPayLink.setGenerateTime(date);
+        orderPayLink.setEffectiveTime(calendar.getTime());
+        orderPayLink.setPayStatus("0");
+        orderPayLink.setDelFlag("0");
+        OrderPayLinkVo orderPayLinkVo = payOrderMapper.findOrderPayLink(orderPayLink.getOrderId().intValue(), null);
+        if (null == orderPayLinkVo) {
+            payOrderMapper.insertOrderPayLink(orderPayLink);
+        } else {
+            orderPayLink.setId(orderPayLinkVo.getId());
+            payOrderMapper.updateOrderPayLink(orderPayLink);
+        }
+        String link = orderPayLink.getRedirectLink() + "?linkLogo=" + linkLogo;
+        return ResponseJson.success(link);
+    }
+
+    @Override
+    public void jumpPage(String linkLogo, String linkPage, ServerHttpResponse response) throws IOException {
+        String linkUrl = linkPage + "?linkLogo=" + linkLogo;
+        response.setStatusCode(HttpStatus.FOUND);
+        response.getHeaders().setLocation(URI.create(linkUrl));
+    }
+
+    @Override
+    public ResponseJson<Map<String, Object>> linkData(String linkLogo) {
+        Map<String, Object> map = new HashMap<>(3);
+        OrderPayLinkVo orderPayLink = payOrderMapper.getOrderPayLink(linkLogo);
+        //链接状态,0成功
+        int code = 0;
+        if (null == orderPayLink) {
+            //已重新生成链接
+            return ResponseJson.error("链接更新,请重新获取");
+        } else {
+            OrderVo order = payOrderMapper.findOrder(orderPayLink.getOrderId().intValue());
+            List<DiscernReceiptVo> discernReceiptList = payOrderMapper.getDiscernReceipt(order);
+            if (orderPayLink.getEffectiveTime().compareTo(new Date()) < 0) {
+                //链接失效
+                code = -3;
+            }
+            if (null != discernReceiptList && discernReceiptList.size() > 0) {
+                for (DiscernReceiptVo discernReceipt : discernReceiptList) {
+                    if ("2".equals(discernReceipt.getPayWay())) {
+                        // 已线下支付
+                        code = -2;
+                    }
+                }
+            }
+            if ("1".equals(orderPayLink.getPayStatus())) {
+                //链接已支付
+                code = 5;
+            }
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            String time = dateFormat.format(orderPayLink.getEffectiveTime());
+            map.put("orderPayLink", orderPayLink);
+            map.put("code", code);
+            map.put("time", time);
+        }
+        return ResponseJson.success(map);
+    }
+
+    @Override
+    public ResponseJson<String> payWhetherSuccess(Integer orderId, Integer paySuccessCounter) {
+        OrderVo order = payOrderMapper.findOrder(orderId);
+        if (order.getPaySuccessCounter().equals(paySuccessCounter)) {
+            return ResponseJson.error(-2, "支付失败", null);
+        } else if (order.getPaySuccessCounter() > paySuccessCounter) {
+            return ResponseJson.success("支付成功");
+        } else {
+            return ResponseJson.error("支付异常>>>>>>>" + order, null);
+        }
+    }
+
+    @Override
+    public ResponseJson<JSONObject> findOrderStatus(String mbOrderId) {
+        // 时间戳
+        long time = System.currentTimeMillis() / 1000;
+        JSONObject json = new JSONObject();
+        json.put("merAccount", merAccount);
+        json.put("mbOrderId", mbOrderId);
+        json.put("time", time);
+        String sign = PayUtils.buildSign(json, merKey);
+        json.put("sign", sign);
+        String data = "";
+        try {
+            data = PayUtils.buildDataPrivate(json, merKey);
+        } catch (Exception e) {
+            log.error("错误信息", e);
+        }
+        JSONObject result = PayUtils.httpGet("https://platform.mhxxkj.com/paygateway/mbpay/order/query/v1_1", merAccount, data);
+        String code = result.getString("code");
+        if (!"000000".equals(code)) {
+            String msg = result.getString("msg");
+            log.info("第三方查询订单失败>>>>>>>msg:" + msg);
+            return ResponseJson.error(msg, null);
+        }
+        return ResponseJson.success(result);
+    }
+}

+ 1 - 0
src/main/java/com/caimei/service/impl/ShoppingCartServiceImpl.java

@@ -130,6 +130,7 @@ public class ShoppingCartServiceImpl implements ShoppingCartService {
                 }
             });
             product.setMinBuyNumber(ladderPriceList.get(0).getBuyNum());
+            product.setLadderPriceList(ladderPriceList);
         } else {
             product.setActStatus(0);
             product.setLadderPriceFlag(0);

+ 128 - 0
src/main/java/com/caimei/util/AES.java

@@ -0,0 +1,128 @@
+package com.caimei.util;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+
+public class AES {
+    /**
+     * 加密
+     *
+     * @param content  需要加密的内容
+     * @param password 加密密码
+     * @return
+     */
+    public static byte[] encrypt(byte[] data, byte[] key) {
+        CheckUtils.notEmpty(data, "data");
+        CheckUtils.notEmpty(key, "key");
+        if (key.length < 16) {
+            throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
+        }
+        try {
+            SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
+            byte[] enCodeFormat = secretKey.getEncoded();
+            SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES");
+            Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
+            cipher.init(Cipher.ENCRYPT_MODE, seckey);// 初始化
+            byte[] result = cipher.doFinal(data);
+            return result; // 加密
+        } catch (Exception e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    /**
+     * 解密
+     *
+     * @param content  待解密内容
+     * @param password 解密密钥
+     * @return
+     */
+    public static byte[] decrypt(byte[] data, byte[] key) {
+        CheckUtils.notEmpty(data, "data");
+        CheckUtils.notEmpty(key, "key");
+        System.out.println(key.length);
+        if (key.length < 16) {
+            throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
+        }
+        try {
+            SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
+            byte[] enCodeFormat = secretKey.getEncoded();
+            SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES");
+            Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
+            cipher.init(Cipher.DECRYPT_MODE, seckey);// 初始化
+            byte[] result = cipher.doFinal(data);
+            return result; // 加密
+        } catch (Exception e) {
+            throw new RuntimeException("decrypt fail!", e);
+        }
+    }
+
+    public static String encryptToBase64(String data, String key) {
+        try {
+            byte[] valueByte = encrypt(data.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING), key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING));
+            return new String(Base64.encode(valueByte));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+
+    }
+
+    public static String decryptFromBase64(String data, String key) {
+        try {
+            byte[] originalData = Base64.decode(data.getBytes());
+            byte[] valueByte = decrypt(originalData, key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING));
+            return new String(valueByte, ConfigureEncryptAndDecrypt.CHAR_ENCODING);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("decrypt fail!", e);
+        }
+    }
+
+    public static String encryptWithKeyBase64(String data, String key) {
+        try {
+            byte[] valueByte = encrypt(data.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING), key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING));
+            return new String(Base64.encode(valueByte));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    public static String decryptWithKeyBase64(String data, String key) {
+        try {
+            byte[] originalData = Base64.decode(data.getBytes());
+            byte[] valueByte = decrypt(originalData, key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING));
+            return new String(valueByte, ConfigureEncryptAndDecrypt.CHAR_ENCODING);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("decrypt fail!", e);
+        }
+    }
+
+    public static byte[] genarateRandomKey() {
+        KeyGenerator keygen = null;
+        try {
+            keygen = KeyGenerator.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(" genarateRandomKey fail!", e);
+        }
+        SecureRandom random = new SecureRandom();
+        keygen.init(random);
+        Key key = keygen.generateKey();
+        return key.getEncoded();
+    }
+
+    public static String genarateRandomKeyWithBase64() {
+        return new String(Base64.encode(genarateRandomKey()));
+    }
+
+    public static void main(String[] args) {
+        String a = "123456";
+        String key = "1201jwe92123fdsd";
+        System.out.println(encryptToBase64(a, key));
+    }
+
+}

+ 601 - 0
src/main/java/com/caimei/util/Base64.java

@@ -0,0 +1,601 @@
+package com.caimei.util;
+
+import java.io.UnsupportedEncodingException;
+
+public class Base64 {
+    /**
+     * Chunk size per RFC 2045 section 6.8.
+     *
+     * <p>The {@value} character limit does not count the trailing CRLF, but counts
+     * all other characters, including any equal signs.</p>
+     *
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
+     */
+    static final int CHUNK_SIZE = 76;
+
+    /**
+     * Chunk separator per RFC 2045 section 2.1.
+     *
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
+     */
+    static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
+
+    /**
+     * The base length.
+     */
+    static final int BASELENGTH = 255;
+
+    /**
+     * Lookup length.
+     */
+    static final int LOOKUPLENGTH = 64;
+
+    /**
+     * Used to calculate the number of bits in a byte.
+     */
+    static final int EIGHTBIT = 8;
+
+    /**
+     * Used when encoding something which has fewer than 24 bits.
+     */
+    static final int SIXTEENBIT = 16;
+
+    /**
+     * Used to determine how many bits data contains.
+     */
+    static final int TWENTYFOURBITGROUP = 24;
+
+    /**
+     * Used to get the number of Quadruples.
+     */
+    static final int FOURBYTE = 4;
+
+    /**
+     * Used to test the sign of a byte.
+     */
+    static final int SIGN = -128;
+
+    /**
+     * Byte used to pad output.
+     */
+    static final byte PAD = (byte) '=';
+
+    // Create arrays to hold the base64 characters and a
+    // lookup for base64 chars
+    private static byte[] base64Alphabet = new byte[BASELENGTH];
+    private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
+
+    // Populating the lookup and character arrays
+    static {
+        for (int i = 0; i < BASELENGTH; i++) {
+            base64Alphabet[i] = (byte) -1;
+        }
+        for (int i = 'Z'; i >= 'A'; i--) {
+            base64Alphabet[i] = (byte) (i - 'A');
+        }
+        for (int i = 'z'; i >= 'a'; i--) {
+            base64Alphabet[i] = (byte) (i - 'a' + 26);
+        }
+        for (int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i - '0' + 52);
+        }
+
+        base64Alphabet['+'] = 62;
+        base64Alphabet['/'] = 63;
+
+        for (int i = 0; i <= 25; i++) {
+            lookUpBase64Alphabet[i] = (byte) ('A' + i);
+        }
+
+        for (int i = 26, j = 0; i <= 51; i++, j++) {
+            lookUpBase64Alphabet[i] = (byte) ('a' + j);
+        }
+
+        for (int i = 52, j = 0; i <= 61; i++, j++) {
+            lookUpBase64Alphabet[i] = (byte) ('0' + j);
+        }
+
+        lookUpBase64Alphabet[62] = (byte) '+';
+        lookUpBase64Alphabet[63] = (byte) '/';
+    }
+
+    private static boolean isBase64(byte octect) {
+        if (octect == PAD) {
+            return true;
+        } else if (base64Alphabet[octect] == -1) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Tests a given byte array to see if it contains
+     * only valid characters within the Base64 alphabet.
+     *
+     * @param arrayOctect byte array to test
+     * @return true if all bytes are valid characters in the Base64
+     * alphabet or if the byte array is empty; false, otherwise
+     */
+    public static boolean isArrayByteBase64(byte[] arrayOctect) {
+
+        arrayOctect = discardWhitespace(arrayOctect);
+
+        int length = arrayOctect.length;
+        if (length == 0) {
+            // shouldn't a 0 length array be valid base64 data?
+            // return false;
+            return true;
+        }
+        for (int i = 0; i < length; i++) {
+            if (!isBase64(arrayOctect[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm but
+     * does not chunk the output.
+     *
+     * @param binaryData binary data to encode
+     * @return Base64 characters
+     */
+    public static byte[] encodeBase64(byte[] binaryData) {
+        return encodeBase64(binaryData, false);
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm and chunks
+     * the encoded output into 76 character blocks
+     *
+     * @param binaryData binary data to encode
+     * @return Base64 characters chunked in 76 character blocks
+     */
+    public static byte[] encodeBase64Chunked(byte[] binaryData) {
+        return encodeBase64(binaryData, true);
+    }
+
+    /**
+     * Decodes a byte[] containing containing
+     * characters in the Base64 alphabet.
+     *
+     * @param pArray A byte array containing Base64 character data
+     * @return a byte array containing binary data
+     */
+    public static byte[] decode(byte[] pArray) {
+        return decodeBase64(pArray);
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm, optionally
+     * chunking the output into 76 character blocks.
+     *
+     * @param binaryData Array containing binary data to encode.
+     * @param isChunked  if isChunked is true this encoder will chunk
+     *                   the base64 output into 76 character blocks
+     * @return Base64-encoded data.
+     */
+    public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
+        int lengthDataBits = binaryData.length * EIGHTBIT;
+        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
+        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
+        byte encodedData[] = null;
+        int encodedDataLength = 0;
+        int nbrChunks = 0;
+
+        if (fewerThan24bits != 0) {
+            //data not divisible by 24 bit
+            encodedDataLength = (numberTriplets + 1) * 4;
+        } else {
+            // 16 or 8 bit
+            encodedDataLength = numberTriplets * 4;
+        }
+
+        // If the output is to be "chunked" into 76 character sections,
+        // for compliance with RFC 2045 MIME, then it is important to
+        // allow for extra length to account for the separator(s)
+        if (isChunked) {
+
+            nbrChunks =
+                    (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
+            encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
+        }
+
+        encodedData = new byte[encodedDataLength];
+
+        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        int i = 0;
+        int nextSeparatorIndex = CHUNK_SIZE;
+        int chunksSoFar = 0;
+
+        //log.debug("number of triplets = " + numberTriplets);
+        for (i = 0; i < numberTriplets; i++) {
+            dataIndex = i * 3;
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            b3 = binaryData[dataIndex + 2];
+
+            //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
+
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 =
+                    ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 =
+                    ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+            byte val3 =
+                    ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
+
+            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+            //log.debug( "val2 = " + val2 );
+            //log.debug( "k4   = " + (k<<4) );
+            //log.debug(  "vak  = " + (val2 | (k<<4)) );
+            encodedData[encodedIndex + 1] =
+                    lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex + 2] =
+                    lookUpBase64Alphabet[(l << 2) | val3];
+            encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
+
+            encodedIndex += 4;
+
+            // If we are chunking, let's put a chunk separator down.
+            if (isChunked) {
+                // this assumes that CHUNK_SIZE % 4 == 0
+                if (encodedIndex == nextSeparatorIndex) {
+                    System.arraycopy(
+                            CHUNK_SEPARATOR,
+                            0,
+                            encodedData,
+                            encodedIndex,
+                            CHUNK_SEPARATOR.length);
+                    chunksSoFar++;
+                    nextSeparatorIndex =
+                            (CHUNK_SIZE * (chunksSoFar + 1)) +
+                                    (chunksSoFar * CHUNK_SEPARATOR.length);
+                    encodedIndex += CHUNK_SEPARATOR.length;
+                }
+            }
+        }
+
+        // form integral number of 6-bit groups
+        dataIndex = i * 3;
+
+        if (fewerThan24bits == EIGHTBIT) {
+            b1 = binaryData[dataIndex];
+            k = (byte) (b1 & 0x03);
+            //log.debug("b1=" + b1);
+            //log.debug("b1<<2 = " + (b1>>2) );
+            byte val1 =
+                    ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
+            encodedData[encodedIndex + 2] = PAD;
+            encodedData[encodedIndex + 3] = PAD;
+        } else if (fewerThan24bits == SIXTEENBIT) {
+
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 =
+                    ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 =
+                    ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+
+            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex + 1] =
+                    lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
+            encodedData[encodedIndex + 3] = PAD;
+        }
+
+        if (isChunked) {
+            // we also add a separator to the end of the final chunk.
+            if (chunksSoFar < nbrChunks) {
+                System.arraycopy(
+                        CHUNK_SEPARATOR,
+                        0,
+                        encodedData,
+                        encodedDataLength - CHUNK_SEPARATOR.length,
+                        CHUNK_SEPARATOR.length);
+            }
+        }
+
+        return encodedData;
+    }
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param base64Data Byte array containing Base64 data
+     * @return Array containing decoded data.
+     */
+    public static byte[] decodeBase64(byte[] base64Data) {
+        // RFC 2045 requires that we discard ALL non-Base64 characters
+        base64Data = discardNonBase64(base64Data);
+
+        // handle the edge case, so we don't have to worry about it later
+        if (base64Data.length == 0) {
+            return new byte[0];
+        }
+
+        int numberQuadruple = base64Data.length / FOURBYTE;
+        byte decodedData[] = null;
+        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
+
+        // Throw away anything not in base64Data
+
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        {
+            // this sizes the output array properly - rlw
+            int lastData = base64Data.length;
+            // ignore the '=' padding
+            while (base64Data[lastData - 1] == PAD) {
+                if (--lastData == 0) {
+                    return new byte[0];
+                }
+            }
+            decodedData = new byte[lastData - numberQuadruple];
+        }
+
+        for (int i = 0; i < numberQuadruple; i++) {
+            dataIndex = i * 4;
+            marker0 = base64Data[dataIndex + 2];
+            marker1 = base64Data[dataIndex + 3];
+
+            b1 = base64Alphabet[base64Data[dataIndex]];
+            b2 = base64Alphabet[base64Data[dataIndex + 1]];
+
+            if (marker0 != PAD && marker1 != PAD) {
+                //No PAD e.g 3cQl
+                b3 = base64Alphabet[marker0];
+                b4 = base64Alphabet[marker1];
+
+                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                decodedData[encodedIndex + 1] =
+                        (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+                decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
+            } else if (marker0 == PAD) {
+                //Two PAD e.g. 3c[Pad][Pad]
+                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+            } else if (marker1 == PAD) {
+                //One PAD e.g. 3cQ[Pad]
+                b3 = base64Alphabet[marker0];
+
+                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                decodedData[encodedIndex + 1] =
+                        (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            }
+            encodedIndex += 3;
+        }
+        return decodedData;
+    }
+
+    /**
+     * Discards any whitespace from a base-64 encoded block.
+     *
+     * @param data The base-64 encoded data to discard the whitespace
+     *             from.
+     * @return The data, less whitespace (see RFC 2045).
+     */
+    static byte[] discardWhitespace(byte[] data) {
+        byte groomedData[] = new byte[data.length];
+        int bytesCopied = 0;
+
+        for (int i = 0; i < data.length; i++) {
+            switch (data[i]) {
+                case (byte) ' ':
+                case (byte) '\n':
+                case (byte) '\r':
+                case (byte) '\t':
+                    break;
+                default:
+                    groomedData[bytesCopied++] = data[i];
+            }
+        }
+
+        byte packedData[] = new byte[bytesCopied];
+
+        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+        return packedData;
+    }
+
+    /**
+     * Discards any characters outside of the base64 alphabet, per
+     * the requirements on page 25 of RFC 2045 - "Any characters
+     * outside of the base64 alphabet are to be ignored in base64
+     * encoded data."
+     *
+     * @param data The base-64 encoded data to groom
+     * @return The data, less non-base64 characters (see RFC 2045).
+     */
+    static byte[] discardNonBase64(byte[] data) {
+        byte groomedData[] = new byte[data.length];
+        int bytesCopied = 0;
+
+        for (int i = 0; i < data.length; i++) {
+            if (isBase64(data[i])) {
+                groomedData[bytesCopied++] = data[i];
+            }
+        }
+
+        byte packedData[] = new byte[bytesCopied];
+
+        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+        return packedData;
+    }
+
+    /**
+     * Encodes a byte[] containing binary data, into a byte[] containing
+     * characters in the Base64 alphabet.
+     *
+     * @param pArray a byte array containing binary data
+     * @return A byte array containing only Base64 character data
+     */
+    public static byte[] encode(byte[] pArray) {
+        return encodeBase64(pArray, false);
+    }
+
+    public static String encode(String str) throws UnsupportedEncodingException {
+        String baseStr = new String(encode(str.getBytes("UTF-8")));
+        String tempStr = Digest.digest(str).toUpperCase();
+        String result = tempStr + baseStr;
+        return new String(encode(result.getBytes("UTF-8")));
+    }
+
+    public static String decode(String cryptoStr) throws
+            UnsupportedEncodingException {
+        if (cryptoStr.length() < 40)
+            return "";
+        try {
+            String tempStr = new String(decode(cryptoStr.getBytes("UTF-8")));
+            String result = tempStr.substring(40, tempStr.length());
+            return new String(decode(result.getBytes("UTF-8")));
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            return "";
+        }
+    }
+
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param encoded string containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public static byte[] decode2(String encoded) {
+
+        if (encoded == null) {
+            return null;
+        }
+
+        char[] base64Data = encoded.toCharArray();
+        // remove white spaces
+        int len = removeWhiteSpace(base64Data);
+
+        if (len % FOURBYTE != 0) {
+            return null;//should be divisible by four
+        }
+
+        int numberQuadruple = (len / FOURBYTE);
+
+        if (numberQuadruple == 0) {
+            return new byte[0];
+        }
+
+        byte decodedData[] = null;
+        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
+        char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
+
+        int i = 0;
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        decodedData = new byte[(numberQuadruple) * 3];
+
+        for (; i < numberQuadruple - 1; i++) {
+
+            if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
+                    || !isData((d3 = base64Data[dataIndex++]))
+                    || !isData((d4 = base64Data[dataIndex++]))) {
+                return null;
+            }//if found "no data" just return null
+
+            b1 = base64Alphabet[d1];
+            b2 = base64Alphabet[d2];
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+        }
+
+        if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
+            return null;//if found "no data" just return null
+        }
+
+        b1 = base64Alphabet[d1];
+        b2 = base64Alphabet[d2];
+
+        d3 = base64Data[dataIndex++];
+        d4 = base64Data[dataIndex++];
+        if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters
+            if (isPad(d3) && isPad(d4)) {
+                if ((b2 & 0xf) != 0)//last 4 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 1];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                return tmp;
+            } else if (!isPad(d3) && isPad(d4)) {
+                b3 = base64Alphabet[d3];
+                if ((b3 & 0x3) != 0)//last 2 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 2];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+                tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+                return tmp;
+            } else {
+                return null;
+            }
+        } else { //No PAD e.g 3cQl
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+
+        }
+
+        return decodedData;
+    }
+
+    private static boolean isWhiteSpace(char octect) {
+        return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
+    }
+
+    private static boolean isData(char octect) {
+        return (octect < BASELENGTH && base64Alphabet[octect] != -1);
+    }
+
+    private static boolean isPad(char octect) {
+        return (octect == PAD);
+    }
+
+    /**
+     * remove WhiteSpace from MIME containing encoded Base64 data.
+     *
+     * @param data the byte array of base64 data (with WS)
+     * @return the new length
+     */
+    private static int removeWhiteSpace(char[] data) {
+        if (data == null) {
+            return 0;
+        }
+
+        // count characters that's not whitespace
+        int newSize = 0;
+        int len = data.length;
+        for (int i = 0; i < len; i++) {
+            if (!isWhiteSpace(data[i])) {
+                data[newSize++] = data[i];
+            }
+        }
+        return newSize;
+    }
+}

+ 36 - 0
src/main/java/com/caimei/util/CheckUtils.java

@@ -0,0 +1,36 @@
+package com.caimei.util;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+
+
+public class CheckUtils {
+
+	public static final String COMMON_FIELD = "flowID,initiator,";
+
+
+	/**
+	 * 验证对象是否为NULL,空字符串,空数组,空的Collection或Map(只有空格的字符串也认为是空串)
+	 * @param obj 被验证的对象
+	 * @param message 异常信息
+	 */
+	@SuppressWarnings("rawtypes")
+	public static void notEmpty(Object obj, String message) {
+		if (obj == null){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+		if (obj instanceof String && obj.toString().trim().length()==0){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+		if (obj.getClass().isArray() && Array.getLength(obj)==0){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+		if (obj instanceof Collection && ((Collection)obj).isEmpty()){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+		if (obj instanceof Map && ((Map)obj).isEmpty()){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+	}
+}

+ 7 - 0
src/main/java/com/caimei/util/ConfigureEncryptAndDecrypt.java

@@ -0,0 +1,7 @@
+package com.caimei.util;
+
+public class ConfigureEncryptAndDecrypt {
+	public static final String CHAR_ENCODING = "UTF-8";
+	public static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";
+	public static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
+}

+ 373 - 0
src/main/java/com/caimei/util/ConvertUtils.java

@@ -0,0 +1,373 @@
+package com.caimei.util;
+
+import sun.misc.BASE64Encoder;
+
+import java.io.UnsupportedEncodingException;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.text.DecimalFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public abstract class ConvertUtils {
+
+    private static final DecimalFormat simpleFormat = new DecimalFormat("####");
+
+    public static final boolean objectToBoolean(Object o){
+        return o != null ? Boolean.valueOf(o.toString()).booleanValue() : false;
+    }
+
+    public static final int objectToInt(Object o){
+        if(o instanceof Number)
+            return ((Number)o).intValue();
+        try{
+            if(o == null)
+                return -1;
+            else
+                return Integer.parseInt(o.toString());
+        }catch(NumberFormatException e){
+            return -1;
+        }
+    }
+
+    public static final short objectToShort(Object o){
+        if(o instanceof Number)
+            return ((Number)o).shortValue();
+        try{
+            if(o == null)
+                return -1;
+            else
+                return Short.parseShort(o.toString());
+        }catch(NumberFormatException e){
+            return -1;
+        }
+    }
+
+    public static final double objectToDouble(Object o){
+        if(o instanceof Number)
+            return ((Number)o).doubleValue();
+        try{
+            if(o == null)
+                return -1D;
+            else
+                return Double.parseDouble(o.toString());
+        }catch(NumberFormatException e){
+            return -1D;
+        }
+    }
+
+    public static final long objectToLong(Object o)
+    {
+        if(o instanceof Number)
+            return ((Number)o).longValue();
+        try{
+            if(o == null)
+                return -1L;
+            else
+                return Long.parseLong(o.toString());
+        }catch(NumberFormatException e){
+            return -1L;
+        }
+    }
+
+    public static final String objectToString(Object obj, DecimalFormat fmt)
+    {
+        fmt.setDecimalSeparatorAlwaysShown(false);
+        if(obj instanceof Double)
+            return fmt.format(((Double)obj).doubleValue());
+        if(obj instanceof Long)
+            return fmt.format(((Long)obj).longValue());
+        else
+            return obj.toString();
+    }
+
+    public static final Object getObjectValue(String value)
+    {
+        try{
+            return Long.valueOf(value);
+        }catch(NumberFormatException e) {}
+
+        try{
+            return Double.valueOf(value);
+        }catch(NumberFormatException e){
+            return value;
+        }
+    }
+
+    public static String longToSimpleString(long value){
+        return simpleFormat.format(value);
+    }
+
+    public static String asHex(byte hash[]){
+        return toHex(hash);
+    }
+
+    public static String toHex(byte input[]){
+        if(input == null)
+            return null;
+        StringBuffer output = new StringBuffer(input.length * 2);
+        for(int i = 0; i < input.length; i++){
+            int current = input[i] & 0xff;
+            if(current < 16)
+                output.append("0");
+            output.append(Integer.toString(current, 16));
+        }
+
+        return output.toString();
+    }
+
+    public static byte[] fromHex(String input){
+        if(input == null)
+            return null;
+        byte output[] = new byte[input.length() / 2];
+        for(int i = 0; i < output.length; i++)
+            output[i] = (byte)Integer.parseInt(input.substring(i * 2, (i + 1) * 2), 16);
+
+        return output;
+    }
+
+    public static String stringToHexString(String input, String encoding)
+        throws UnsupportedEncodingException{
+        return input != null ? toHex(input.getBytes(encoding)) : null;
+    }
+
+    public static String stringToHexString(String input){
+        try{
+            return stringToHexString(input, "UTF-8");
+        }catch(UnsupportedEncodingException e){
+            throw new IllegalStateException("UTF-8 encoding is not supported by JVM");
+        }
+    }
+
+    public static String hexStringToString(String input, String encoding)
+        throws UnsupportedEncodingException{
+        return input != null ? new String(fromHex(input), encoding) : null;
+    }
+
+    public static String hexStringToString(String input){
+        try{
+            return hexStringToString(input, "UTF-8");
+        }catch(UnsupportedEncodingException e){
+            throw new IllegalStateException("UTF-8 encoding is not supported by JVM");
+        }
+    }
+
+    public static String timeZoneToCode(TimeZone tz){
+
+        return timeZoneToString(tz);
+    }
+
+    public static TimeZone codeToTimeZone(String tzString){
+
+        return stringToTimeZone(tzString);
+    }
+
+    public static String timeZoneToString(TimeZone tz){
+
+        return tz != null ? tz.getID() : "";
+    }
+
+    public static TimeZone stringToTimeZone(String tzString){
+
+        return TimeZone.getTimeZone(tzString != null ? tzString : "");
+    }
+
+    public static String localeToCode(Locale aLocale){
+
+        return localeToString(aLocale);
+    }
+
+    public static Locale codeToLocale(String locString){
+
+        return stringToLocale(locString);
+    }
+
+    public static String localeToString(Locale loc){
+
+        return loc != null ? loc.toString() : "";
+    }
+
+    public static Locale stringToLocale(String locString){
+
+        locString = locString != null ? locString.trim() : "";
+        if(locString.equals(""))
+            return new Locale("", "", "");
+        int pos = locString.indexOf(95);
+        if(pos == -1)
+            return new Locale(locString, "", "");
+        String language = locString.substring(0, pos);
+        locString = locString.substring(pos + 1);
+        pos = locString.indexOf(95);
+        if(pos == -1){
+            return new Locale(language, locString, "");
+        }else{
+            String country = locString.substring(0, pos);
+            locString = locString.substring(pos + 1);
+            return new Locale(language, country, locString);
+        }
+    }
+
+    public static Date dateToSQLDate(java.util.Date d){
+
+        return d != null ? new Date(d.getTime()) : null;
+    }
+
+    public static Time dateToSQLTime(java.util.Date d){
+
+        return d != null ? new Time(d.getTime()) : null;
+    }
+
+    public static Timestamp dateToSQLTimestamp(java.util.Date d){
+
+        return d != null ? new Timestamp(d.getTime()) : null;
+    }
+
+    public static java.util.Date sqlTimestampToDate(Timestamp t){
+
+        return t != null ? new java.util.Date(Math.round((double)t.getTime() + (double)t.getNanos() / 1000000D)) : null;
+    }
+
+    public static Timestamp getCurrentDate(){
+
+        Calendar c = Calendar.getInstance();
+        c.set(c.get(1), c.get(2), c.get(5), 0, 0, 0);
+        Timestamp t = new Timestamp(c.getTime().getTime());
+        t.setNanos(0);
+        return t;
+    }
+
+    public static java.util.Date getDate(int y, int m, int d, boolean inclusive)
+    {
+        java.util.Date dt = null;
+        Calendar c = Calendar.getInstance();
+        c.clear();
+        if(c.getActualMinimum(1) <= y && y <= c.getActualMaximum(1))
+        {
+            c.set(1, y);
+            if(c.getActualMinimum(2) <= m && m <= c.getActualMaximum(2))
+            {
+                c.set(2, m);
+                if(c.getActualMinimum(5) <= d && d <= c.getActualMaximum(5))
+                    c.set(5, d);
+            }
+            if(inclusive)
+            {
+                c.add(5, 1);
+                c.add(14, -1);
+            }
+            dt = c.getTime();
+        }
+        return dt;
+    }
+
+    public static java.util.Date getDateStart(java.util.Date d)
+    {
+
+         Calendar c = new GregorianCalendar();
+         c.clear();
+         Calendar co = new GregorianCalendar();
+         co.setTime(d);
+         c.set(Calendar.DAY_OF_MONTH,co.get(Calendar.DAY_OF_MONTH));
+         c.set(Calendar.MONTH,co.get(Calendar.MONTH));
+         c.set(Calendar.YEAR,co.get(Calendar.YEAR));
+         //c.add(Calendar.DAY_OF_MONTH,1);
+         //c.add(Calendar.MILLISECOND,-1);
+         return c.getTime();
+    }
+
+    public static java.util.Date getDateEnd(java.util.Date d)
+    {
+         Calendar c = Calendar.getInstance();
+         c.clear();
+         Calendar co = Calendar.getInstance();
+         co.setTime(d);
+         c.set(Calendar.DAY_OF_MONTH,co.get(Calendar.DAY_OF_MONTH));
+         c.set(Calendar.MONTH,co.get(Calendar.MONTH));
+         c.set(Calendar.YEAR,co.get(Calendar.YEAR));
+         c.add(Calendar.DAY_OF_MONTH,1);
+         c.add(Calendar.MILLISECOND,-1);
+         return c.getTime();
+    }
+
+    public static double roundNumber(double rowNumber, int roundingPoint)
+    {
+        double base = Math.pow(10D, roundingPoint);
+        return (double)Math.round(rowNumber * base) / base;
+    }
+    public static Object getObject(String type,String value) throws Exception{
+
+    	type=type.toLowerCase();
+    	if("boolean".equals(type))
+            return Boolean.valueOf(value);
+        if("byte".equals(type))
+            return Byte.valueOf(value);
+        if("short".equals(type))
+            return Short.valueOf(value);
+        if("char".equals(type))
+            if(value.length() != 1)
+                throw new NumberFormatException("Argument is not a character!");
+            else
+                return Character.valueOf(value.toCharArray()[0]);
+        if("int".equals(type))
+            return Integer.valueOf(value);
+        if("long".equals(type))
+            return Long.valueOf(value);
+        if("float".equals(type))
+            return Float.valueOf(value);
+        if("double".equals(type))
+            return Double.valueOf(value);
+        if("string".equals(type))
+            return value;
+		else{
+	        Object objs[]=new String[]{value};
+			return Class.forName(type).getConstructor(new Class[] {
+				    String.class
+				}).newInstance(objs);
+		}
+    }
+    private ConvertUtils(){}
+
+    /**
+	 * 图片的十六进制字符串转为base64形式的字符串(去掉回车、换行字符)
+	 * @param src
+	 * @return
+	 */
+	public static String hexStr2Base64Str(String src){
+		byte[] bytes = hex2byte(src);
+		String strs = new BASE64Encoder().encodeBuffer(bytes);
+		StringBuilder sb = new StringBuilder(strs.length());
+		for(int i=0;i< strs.length();i++){
+			if(strs.charAt(i) != '\r' && strs.charAt(i) != '\n'){
+				sb.append(strs.charAt(i));
+			}
+		}
+		return sb.toString();
+	}
+	
+	/**
+	 * 将十六进制字符串转为字节数组
+	 * @param s
+	 * @return
+	 */
+	public static byte[] hex2byte(String s){
+		byte[] src = s.toLowerCase().getBytes();
+        byte[] ret = new byte[src.length / 2];
+        for (int i = 0; i < src.length; i += 2) {
+            byte hi = src[i];
+            byte low = src[i + 1];
+            hi = (byte) ((hi >= 'a' && hi <= 'f') ? 0x0a + (hi - 'a')
+                    : hi - '0');
+            low = (byte) ((low >= 'a' && low <= 'f') ? 0x0a + (low - 'a')
+                    : low - '0');
+            ret[i / 2] = (byte) (hi << 4 | low);
+        }
+        return ret;
+	}
+//    public static void main(String[] args)
+//    {
+//    	System.out.println(getDateStart(new java.util.Date()));
+//    }
+}

+ 164 - 0
src/main/java/com/caimei/util/Digest.java

@@ -0,0 +1,164 @@
+package com.caimei.util;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+public class Digest {
+    public static final String ENCODE = "UTF-8";
+
+    public static String signMD5(String aValue, String encoding) {
+        try {
+            byte[] input = aValue.getBytes(encoding);
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            return ConvertUtils.toHex(md.digest(input));
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String hmacSign(String aValue) {
+        try {
+            byte[] input = aValue.getBytes();
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            return ConvertUtils.toHex(md.digest(input));
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String hmacSign(String aValue, String aKey) {
+        return hmacSign(aValue, aKey, ENCODE);
+    }
+
+    public static String hmacSign(String aValue, String aKey, String encoding) {
+        byte k_ipad[] = new byte[64];
+        byte k_opad[] = new byte[64];
+        byte keyb[];
+        byte value[];
+        try {
+            keyb = aKey.getBytes(encoding);
+            value = aValue.getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            keyb = aKey.getBytes();
+            value = aValue.getBytes();
+        }
+        Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
+        Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
+        for (int i = 0; i < keyb.length; i++) {
+            k_ipad[i] = (byte) (keyb[i] ^ 0x36);
+            k_opad[i] = (byte) (keyb[i] ^ 0x5c);
+        }
+
+        MessageDigest md = null;
+        try {
+            md = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+        md.update(k_ipad);
+        md.update(value);
+        byte dg[] = md.digest();
+        md.reset();
+        md.update(k_opad);
+        md.update(dg, 0, 16);
+        dg = md.digest();
+        return ConvertUtils.toHex(dg);
+    }
+
+    public static String hmacSHASign(String aValue, String aKey, String encoding) {
+        byte k_ipad[] = new byte[64];
+        byte k_opad[] = new byte[64];
+        byte keyb[];
+        byte value[];
+        try {
+            keyb = aKey.getBytes(encoding);
+            value = aValue.getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            keyb = aKey.getBytes();
+            value = aValue.getBytes();
+        }
+        Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
+        Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
+        for (int i = 0; i < keyb.length; i++) {
+            k_ipad[i] = (byte) (keyb[i] ^ 0x36);
+            k_opad[i] = (byte) (keyb[i] ^ 0x5c);
+        }
+
+        MessageDigest md = null;
+        try {
+            md = MessageDigest.getInstance("SHA");
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+        md.update(k_ipad);
+        md.update(value);
+        byte dg[] = md.digest();
+        md.reset();
+        md.update(k_opad);
+        md.update(dg, 0, 20);
+        dg = md.digest();
+        return ConvertUtils.toHex(dg);
+    }
+
+    public static String digest(String aValue) {
+        return digest(aValue, ENCODE);
+
+    }
+
+    public static String digest(String aValue, String encoding) {
+        aValue = aValue.trim();
+        byte value[];
+        try {
+            value = aValue.getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            value = aValue.getBytes();
+        }
+        MessageDigest md = null;
+        try {
+            md = MessageDigest.getInstance("SHA");
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+        return ConvertUtils.toHex(md.digest(value));
+    }
+
+
+    public static String digest(String aValue, String alg, String encoding) {
+        aValue = aValue.trim();
+        byte value[];
+        try {
+            value = aValue.getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            value = aValue.getBytes();
+        }
+        MessageDigest md = null;
+        try {
+            md = MessageDigest.getInstance(alg);
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+        return ConvertUtils.toHex(md.digest(value));
+    }
+
+    public static String udpSign(String aValue) {
+        try {
+            byte[] input = aValue.getBytes("UTF-8");
+            MessageDigest md = MessageDigest.getInstance("SHA1");
+            return new String(Base64.encode(md.digest(input)), ENCODE);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+}

+ 85 - 0
src/main/java/com/caimei/util/HashUtil.java

@@ -0,0 +1,85 @@
+
+package com.caimei.util;
+
+import java.security.MessageDigest;
+
+public class HashUtil {
+
+    private static final java.security.SecureRandom random = new java.security.SecureRandom();
+    private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
+    private static final char[] CHAR_ARRAY = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
+
+    public static String md5(String srcStr) {
+        return hash("MD5", srcStr);
+    }
+
+    public static String sha1(String srcStr) {
+        return hash("SHA-1", srcStr);
+    }
+
+    public static String sha256(String srcStr) {
+        return hash("SHA-256", srcStr);
+    }
+
+    public static String sha384(String srcStr) {
+        return hash("SHA-384", srcStr);
+    }
+
+    public static String sha512(String srcStr) {
+        return hash("SHA-512", srcStr);
+    }
+
+    public static String hash(String algorithm, String srcStr) {
+        try {
+            MessageDigest md = MessageDigest.getInstance(algorithm);
+            byte[] bytes = md.digest(srcStr.getBytes("utf-8"));
+            return toHex(bytes);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String toHex(byte[] bytes) {
+        StringBuilder ret = new StringBuilder(bytes.length * 2);
+        for (int i = 0; i < bytes.length; i++) {
+            ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
+            ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
+        }
+        return ret.toString();
+    }
+
+    /**
+     * md5 128bit 16bytes
+     * sha1 160bit 20bytes
+     * sha256 256bit 32bytes
+     * sha384 384bit 48bytes
+     * sha512 512bit 64bytes
+     */
+    public static String generateSalt(int saltLength) {
+        StringBuilder salt = new StringBuilder(saltLength);
+        for (int i = 0; i < saltLength; i++) {
+            salt.append(CHAR_ARRAY[random.nextInt(CHAR_ARRAY.length)]);
+        }
+        return salt.toString();
+    }
+
+    public static String generateSaltForSha256() {
+        return generateSalt(32);
+    }
+
+    public static String generateSaltForSha512() {
+        return generateSalt(64);
+    }
+
+    public static boolean slowEquals(byte[] a, byte[] b) {
+        if (a == null || b == null) {
+            return false;
+        }
+
+        int diff = a.length ^ b.length;
+        for (int i = 0; i < a.length && i < b.length; i++) {
+            diff |= a[i] ^ b[i];
+        }
+        return diff == 0;
+    }
+}

+ 167 - 0
src/main/java/com/caimei/util/HttpClient4Utils.java

@@ -0,0 +1,167 @@
+package com.caimei.util;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * httpClient4 工具类
+ *
+ * @author: yingjie.wang
+ * @since : 2015-09-23 13:12
+ */
+
+public class HttpClient4Utils {
+
+    //设置默认超时时间为60s
+    public static final int DEFAULT_TIME_OUT = 60 * 1000;
+
+    //http请求
+    public static String sendHttpRequest(String url, Map<String, String> paramMap, String charset, boolean isPost) {
+        return sendHttpRequest(url, paramMap, charset, isPost, DEFAULT_TIME_OUT);
+    }
+
+    //http请求
+    public static String sendHttpRequest(String url, Map<String, String> paramMap, String charset, boolean isPost, int timeout) {
+        if (isPost) {
+            return httpPost(url, paramMap, charset, timeout);
+        }
+
+        return httpGet(url, paramMap, charset, timeout);
+    }
+
+    //post请求
+    public static String httpPost(String url, Map<String, String> params, String charset, int timeout) {
+
+        if (url == null || url.equals("")) {
+            return null;
+        }
+
+        String result = null;
+
+        //超时设置
+        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(timeout).setSocketTimeout(timeout).build();
+
+        //参数组装
+        List<NameValuePair> pairs = new ArrayList<NameValuePair>();
+        for (Entry<String, String> entry : params.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            pairs.add(new BasicNameValuePair(key, formatStr(value)));
+        }
+
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        HttpPost httpPost = null;
+        String responseBody = null;
+        CloseableHttpResponse response = null;
+
+        try {
+            httpPost = new HttpPost(url);
+            httpPost.setConfig(requestConfig);
+            httpPost.setEntity(new UrlEncodedFormEntity(pairs));
+            response = httpClient.execute(httpPost);
+
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                httpPost.abort();
+                throw new RuntimeException("HttpClient,error status code :" + statusCode);
+            }
+
+            HttpEntity entity = response.getEntity();
+            responseBody = EntityUtils.toString(entity, charset);
+            result = responseBody;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                // 关闭连接,释放资源
+                if (response != null) {
+                    response.close();
+                }
+                if (httpClient != null) {
+                    httpClient.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return result;
+    }
+
+    //get请求
+    public static String httpGet(String url, Map<String, String> params, String charset, int timeout) {
+
+        if (url == null || url.equals("")) {
+            return null;
+        }
+
+        String result = null;
+
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        HttpGet httpGet = null;
+        String responseBody = null;
+        CloseableHttpResponse response = null;
+
+        try {
+
+            if (params != null && !params.isEmpty()) {
+                List<NameValuePair> pairs = new ArrayList<NameValuePair>();
+                for (Entry<String, String> entry : params.entrySet()) {
+                    String key = entry.getKey();
+                    String value = entry.getValue();
+                    pairs.add(new BasicNameValuePair(key, formatStr(value)));
+                }
+                url = url + "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset));
+            }
+
+            httpGet = new HttpGet(url);
+            response = httpClient.execute(httpGet);
+
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                httpGet.abort();
+                throw new RuntimeException("HttpClient,error status code :" + statusCode);
+            }
+
+            HttpEntity entity = response.getEntity();
+            responseBody = EntityUtils.toString(entity, charset);
+            result = responseBody;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                // 关闭连接,释放资源
+                if (response != null) {
+                    response.close();
+                }
+                if (httpClient != null) {
+                    httpClient.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return result;
+    }
+
+    public static String formatStr(String text) {
+        return (text == null ? "" : text.trim());
+    }
+
+}

+ 23 - 0
src/main/java/com/caimei/util/KeyGenerator.java

@@ -0,0 +1,23 @@
+package com.caimei.util;
+
+
+import com.caimei.model.enumerate.KeyFormat;
+
+import java.security.NoSuchAlgorithmException;
+
+/*
+2015-01-23
+*/
+public class KeyGenerator {
+
+    static public KeyPair generateKeyPair(KeyFormat format)
+            throws NoSuchAlgorithmException {
+        KeyPair keyPair = new KeyPair(format);
+
+        return keyPair;
+    }
+
+    static public KeyPair generateKeyPair() throws NoSuchAlgorithmException {
+        return generateKeyPair(KeyFormat.ASN);
+    }
+}

+ 133 - 0
src/main/java/com/caimei/util/KeyPair.java

@@ -0,0 +1,133 @@
+package com.caimei.util;
+
+import com.caimei.model.enumerate.KeyFormat;
+import sun.misc.BASE64Encoder;
+
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+
+/*
+2015-01-23
+*/
+public class KeyPair {
+    private java.security.KeyPair _rsa;
+    private KeyFormat _format;
+    private String _private;
+    private String _public;
+
+    public KeyFormat getFormat() {
+        return this._format;
+    }
+
+    KeyPair(KeyFormat format) throws NoSuchAlgorithmException {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(1024);
+        this._rsa = kpg.genKeyPair();
+        ;
+        this._format = format;
+    }
+
+    private KeyPair(java.security.KeyPair rsa, KeyFormat format) {
+        this._rsa = rsa;
+        this._format = format;
+    }
+
+    public String getPrivateKey() {
+        if (this._private == null) {
+            switch (this._format) {
+                case ASN:
+                    this._private = this._toASNPrivateKey();
+                    break;
+                case XML:
+                    this._private = this._toXMLPrivateKey();
+                    break;
+                case PEM:
+                    this._private = this._toPEMPrivateKey();
+                    break;
+                default:
+                    this._private = this._toXMLPrivateKey();
+                    break;
+            }
+        }
+        return this._private;
+    }
+
+    public String getPublicKey() {
+        if (this._public == null) {
+            switch (this._format) {
+                case ASN:
+                    this._public = this._toASNPublicKey();
+                    break;
+                case XML:
+                    this._public = this._toXMLPublicKey();
+                    break;
+                case PEM:
+                    this._public = this._toPEMPublicKey();
+                    break;
+                default:
+                    this._public = this._toXMLPublicKey();
+                    break;
+            }
+        }
+        return this._public;
+    }
+
+    public KeyPair toASNKeyPair() {
+        return new KeyPair(this._rsa, KeyFormat.ASN);
+    }
+
+    public KeyPair toXMLKeyPair() {
+        return new KeyPair(this._rsa, KeyFormat.XML);
+    }
+
+    public KeyPair toPEMKeyPair() {
+        return new KeyPair(this._rsa, KeyFormat.PEM);
+    }
+
+
+    private String _toASNPublicKey() {
+        return (new BASE64Encoder()).encodeBuffer(this._rsa.getPublic().getEncoded());
+    }
+
+    private String _toASNPrivateKey() {
+        return (new BASE64Encoder()).encodeBuffer(this._rsa.getPrivate().getEncoded());
+    }
+
+    private String _toXMLPublicKey() {
+        return XmlKeyBuilder.publicKeyToXML(this._rsa.getPublic());
+    }
+
+    private String _toXMLPrivateKey() {
+        return XmlKeyBuilder.privateKeyToXML(this._rsa.getPrivate());
+    }
+
+    private String _toPEMPublicKey() {
+        String publicKey = this._toASNPublicKey();
+        StringBuilder sb = new StringBuilder();
+        sb.append("-----BEGIN PUBLIC KEY-----\r\n");
+        int i = 0;
+        while (i + 64 < publicKey.length()) {
+            sb.append(publicKey.substring(i, i + 64) + "\r\n");
+            i += 64;
+        }
+        sb.append(publicKey.substring(i, publicKey.length()) + "\r\n");
+        sb.append("-----END PUBLIC KEY-----\r\n");
+
+        return sb.toString();
+    }
+
+    private String _toPEMPrivateKey() {
+        String privateKey = this._toASNPrivateKey();
+        StringBuilder sb = new StringBuilder();
+        sb.append("-----BEGIN PRIVATE KEY-----\r\n");
+        int i = 0;
+        while (i + 64 < privateKey.length()) {
+            sb.append(privateKey.substring(i, i + 64) + "\r\n");
+            i += 64;
+        }
+        sb.append(privateKey.substring(i, privateKey.length()) + "\r\n");
+        sb.append("-----END PRIVATE KEY-----\r\n");
+
+        return sb.toString();
+    }
+}

+ 206 - 0
src/main/java/com/caimei/util/KeyWorker.java

@@ -0,0 +1,206 @@
+package com.caimei.util;
+
+
+import com.caimei.model.enumerate.KeyFormat;
+import org.xml.sax.SAXException;
+import sun.misc.BASE64Decoder;
+import sun.misc.BASE64Encoder;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.*;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/*
+2015-01-23
+*/
+public class KeyWorker {
+
+    private String _key;
+    private KeyFormat _format;
+    private Cipher _decryptProvider;
+    private Cipher _encryptProvider;
+
+    private static final int MAX_ENCRYPT_BLOCK = 117;
+
+    private static final int MAX_DECRYPT_BLOCK = 128;
+
+    public KeyWorker(String key) {
+        this(key, KeyFormat.ASN);
+    }
+
+    public KeyWorker(String key, KeyFormat format) {
+        this._key = key;
+        this._format = format;
+    }
+
+    public String encrypt(String data) throws IllegalBlockSizeException,
+            BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
+            NoSuchPaddingException, InvalidKeySpecException, IOException, SAXException, ParserConfigurationException {
+        this._makesureEncryptProvider();
+        byte[] bytes = data.getBytes("UTF-8");
+        int inputLen = bytes.length;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int offSet = 0;
+        byte[] cache;
+        int i = 0;
+        while (inputLen - offSet > 0) {
+            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
+                cache = _encryptProvider.doFinal(bytes, offSet, MAX_ENCRYPT_BLOCK);
+            } else {
+                cache = _encryptProvider.doFinal(bytes, offSet, inputLen - offSet);
+            }
+            out.write(cache, 0, cache.length);
+            i++;
+            offSet = i * MAX_ENCRYPT_BLOCK;
+        }
+        bytes = out.toByteArray();
+        out.close();
+        return new BASE64Encoder().encode(bytes);
+    }
+
+    public String decrypt(String data) throws IOException,
+            IllegalBlockSizeException, BadPaddingException,
+            InvalidKeyException, NoSuchAlgorithmException,
+            NoSuchPaddingException, InvalidKeySpecException, SAXException, ParserConfigurationException {
+        this._makesureDecryptProvider();
+        byte[] bytes = new BASE64Decoder().decodeBuffer(data);
+        int inputLen = bytes.length;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int offSet = 0;
+        byte[] cache;
+        int i = 0;
+        while (inputLen - offSet > 0) {
+            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
+                cache = _decryptProvider.doFinal(bytes, offSet, MAX_DECRYPT_BLOCK);
+            } else {
+                cache = _decryptProvider.doFinal(bytes, offSet, inputLen - offSet);
+            }
+            out.write(cache, 0, cache.length);
+            i++;
+            offSet = i * MAX_DECRYPT_BLOCK;
+        }
+        bytes = out.toByteArray();
+        out.close();
+        return new String(bytes, "UTF-8");
+    }
+
+    private void _makesureDecryptProvider() throws NoSuchAlgorithmException,
+            NoSuchPaddingException, IOException, InvalidKeySpecException,
+            InvalidKeyException, SAXException, ParserConfigurationException {
+        if (this._decryptProvider != null)
+            return;
+
+        Cipher deCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+        switch (this._format) {
+            case XML: {
+                Boolean isPrivate = this._key.indexOf("<P>") > -1;
+                if (isPrivate) {
+                    PrivateKey privateKey = XmlKeyBuilder
+                            .xmlToPrivateKey(this._key);
+                    deCipher.init(Cipher.DECRYPT_MODE, privateKey);
+                } else {
+                    PublicKey publicKey = XmlKeyBuilder.xmlToPublicKey(this._key);
+                    deCipher.init(Cipher.DECRYPT_MODE, publicKey);
+                }
+            }
+            break;
+            case PEM: {
+                this._key = this._key.replace("-----BEGIN PUBLIC KEY-----", "")
+                        .replace("-----END PUBLIC KEY-----", "")
+                        .replace("-----BEGIN PRIVATE KEY-----", "")
+                        .replace("-----END PRIVATE KEY-----", "")
+                        .replaceAll("\r\n", "");
+            }
+            case ASN:
+            default: {
+                Boolean isPrivate = this._key.length() > 500;
+                if (isPrivate) {
+                    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(
+                            new BASE64Decoder().decodeBuffer(this._key));
+
+                    KeyFactory factory = KeyFactory.getInstance("RSA");
+                    RSAPrivateKey privateKey = (RSAPrivateKey) factory
+                            .generatePrivate(spec);
+                    deCipher.init(Cipher.DECRYPT_MODE, privateKey);
+                } else {
+                    X509EncodedKeySpec spec = new X509EncodedKeySpec(
+                            new BASE64Decoder().decodeBuffer(this._key));
+
+                    KeyFactory factory = KeyFactory.getInstance("RSA");
+                    RSAPublicKey publicKey = (RSAPublicKey) factory
+                            .generatePublic(spec);
+                    deCipher.init(Cipher.DECRYPT_MODE, publicKey);
+                }
+            }
+            break;
+        }
+
+        this._decryptProvider = deCipher;
+    }
+
+    private void _makesureEncryptProvider() throws NoSuchAlgorithmException,
+            NoSuchPaddingException, IOException, InvalidKeySpecException,
+            InvalidKeyException, SAXException, ParserConfigurationException {
+        if (this._encryptProvider != null)
+            return;
+
+        Cipher enCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+        switch (this._format) {
+            case XML: {
+                Boolean isPrivate = this._key.indexOf("<P>") > -1;
+                if (isPrivate) {
+                    PrivateKey privateKey = XmlKeyBuilder
+                            .xmlToPrivateKey(this._key);
+                    enCipher.init(Cipher.ENCRYPT_MODE, privateKey);
+                } else {
+                    PublicKey publicKey = XmlKeyBuilder.xmlToPublicKey(this._key);
+                    enCipher.init(Cipher.ENCRYPT_MODE, publicKey);
+                }
+            }
+            break;
+            case PEM: {
+                this._key = this._key.replace("-----BEGIN PUBLIC KEY-----", "")
+                        .replace("-----END PUBLIC KEY-----", "")
+                        .replace("-----BEGIN PRIVATE KEY-----", "")
+                        .replace("-----END PRIVATE KEY-----", "")
+                        .replaceAll("\r\n", "");
+            }
+            case ASN:
+            default: {
+                Boolean isPrivate = this._key.length() > 500;
+                if (isPrivate) {
+                    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(
+                            new BASE64Decoder().decodeBuffer(this._key));
+
+                    KeyFactory factory = KeyFactory.getInstance("RSA");
+                    RSAPrivateKey privateKey = (RSAPrivateKey) factory
+                            .generatePrivate(spec);
+                    enCipher.init(Cipher.ENCRYPT_MODE, privateKey);
+
+                } else {
+                    X509EncodedKeySpec spec = new X509EncodedKeySpec(
+                            new BASE64Decoder().decodeBuffer(this._key));
+
+                    KeyFactory factory = KeyFactory.getInstance("RSA");
+                    RSAPublicKey publicKey = (RSAPublicKey) factory
+                            .generatePublic(spec);
+                    enCipher.init(Cipher.ENCRYPT_MODE, publicKey);
+                }
+            }
+            break;
+        }
+
+        this._encryptProvider = enCipher;
+    }
+
+}

+ 89 - 0
src/main/java/com/caimei/util/Md5Util.java

@@ -0,0 +1,89 @@
+package com.caimei.util;
+
+import java.security.MessageDigest;
+import java.util.UUID;
+
+/**
+ * md5加密工具类
+ *
+ * @author : Charles
+ * @date : 2020/1/17
+ */
+public class Md5Util {
+    /**
+     * md5加密
+     *
+     * @param s:待加密字符串
+     * @return 加密后16进制字符串
+     */
+    public static String md5(String s) {
+        try {
+            //实例化MessageDigest的MD5算法对象
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            //通过digest方法返回哈希计算后的字节数组
+            byte[] bytes = md.digest(s.getBytes("utf-8"));
+            //将字节数组转换为16进制字符串并返回
+            return toHex(bytes);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 获取随即盐
+     *
+     * @return
+     */
+    public static String salt() {
+        //利用UUID生成随机盐
+        UUID uuid = UUID.randomUUID();
+        //返回a2c64597-232f-4782-ab2d-9dfeb9d76932
+        String[] arr = uuid.toString().split("-");
+        return arr[0];
+    }
+
+    /**
+     * 字节数组转换为16进制字符串
+     *
+     * @param bytes数组
+     * @return 16进制字符串
+     */
+    private static String toHex(byte[] bytes) {
+        final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
+        StringBuilder ret = new StringBuilder(bytes.length * 2);
+        for (int i = 0; i < bytes.length; i++) {
+            ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
+            ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
+        }
+        return ret.toString();
+    }
+
+    /**
+     * <p>Description: 16位的MD5值</p>
+     * <p>Company: caimei365</p>
+     *
+     * @param s
+     * @return
+     * @throws Exception
+     * @author dmeng
+     * @date 2015年12月17日 下午5:28:49
+     */
+    public static String MD5To16Bit(String s) throws Exception {
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        md.update(s.getBytes());
+        byte b[] = md.digest();
+        int i;
+        StringBuffer buf = new StringBuffer("");
+        for (int offset = 0; offset < b.length; offset++) {
+            i = b[offset];
+            if (i < 0)
+                i += 256;
+            if (i < 16)
+                buf.append("0");
+            buf.append(Integer.toHexString(i));
+        }
+        return buf.toString().substring(8, 24);
+    }
+
+}
+

+ 141 - 0
src/main/java/com/caimei/util/PayUtils.java

@@ -0,0 +1,141 @@
+package com.caimei.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+/**
+ * 工具类
+ */
+
+public class PayUtils {
+    //编码格式UTF-8
+    public static final String CHARSET = "UTF-8";
+    private static JSONObject decrypt;
+
+
+    //生成RSA签名:sign
+    public static String buildSign(JSONObject json, String merKey) {
+        StringBuffer buffer = new StringBuffer();
+        TreeMap<String, Object> treeMap = new TreeMap<String, Object>();
+        for (Entry<String, Object> entry : json.entrySet()) {
+            if (entry.getValue() != null) {
+                treeMap.put(entry.getKey(), entry.getValue());
+            }
+        }
+        for (Entry<String, Object> entry : treeMap.entrySet()) {
+            if (entry.getValue() != null) {
+                buffer.append(entry.getValue());
+            }
+        }
+        buffer.append(merKey.replaceAll("(\r\n|\r|\n|\n\r)", ""));
+        System.out.println(buffer.toString());
+        return HashUtil.md5(buffer.toString()).toUpperCase();
+    }
+
+    public static String buildDataPrivate(JSONObject json, String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        return RSAUtil.privateEncrypt(json.toJSONString(), privateKey);
+    }
+
+    public static String buildDataPublic(JSONObject json, String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        return RSAUtil.publicEncrypt(json.toJSONString(), publicKey);
+    }
+
+    public static JSONObject decryptDataPrivate(String data, String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        JSONObject result = null;
+        String jsonStr = RSAUtil.privateDecrypt(data.trim(), privateKey);
+        result = JSON.parseObject(jsonStr);
+        return result;
+    }
+
+    public static JSONObject decryptDataPublic(String data, String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        JSONObject result = null;
+        String jsonStr = RSAUtil.publicDecrypt(data.trim(), publicKey);
+        result = JSON.parseObject(jsonStr);
+        return result;
+    }
+
+    //生成密文:data
+    public static String buildData(JSONObject json, String merKey) {
+        return AES.encryptWithKeyBase64(json.toJSONString(), merKey);
+    }
+
+    //解密data,获得明文参数
+    public static JSONObject decrypt(String data, String merKey) {
+        JSONObject result = null;
+        //2.使用AESKey解开data,取得明文参数;解密后格式为json
+        String jsonStr = AES.decryptWithKeyBase64(data.trim(), merKey.trim());
+        result = JSON.parseObject(jsonStr);
+        return result;
+    }
+
+    // sign验签
+    public static boolean checkSign(JSONObject json, String merKey) {
+        //获取明文参数中的sign。
+        String signPay = StringUtils.trimToEmpty(json.getString("sign"));
+        //将明文参数中sign之外的其他参数,拼接成字符串
+        StringBuffer buffer = new StringBuffer();
+        TreeMap<String, Object> treeMap = new TreeMap<String, Object>();
+        for (Entry<String, Object> entry : json.entrySet()) {
+            if (entry.getValue() != null) {
+                treeMap.put(entry.getKey(), entry.getValue());
+            }
+        }
+        for (Entry<String, Object> entry : treeMap.entrySet()) {
+            String key = formatStr(entry.getKey());
+            String value = formatStr(entry.getValue());
+            if ("sign".equals(key)) {
+                continue;
+            }
+            buffer.append(value);
+        }
+        buffer.append(merKey.replaceAll("(\r\n|\r|\n|\n\r)", ""));
+        //result为true时表明验签通过
+        String checkSign = HashUtil.md5(buffer.toString()).toUpperCase();
+        return signPay.equals(checkSign);
+    }
+
+    //一键支付post请求
+    public static JSONObject httpPost(String url, String merAccount, String data) {
+        //请求参数为如下:merAccount、data
+        Map<String, String> paramMap = new HashMap<String, String>();
+        paramMap.put("data", data);
+        paramMap.put("merAccount", merAccount);
+        String responseBody = HttpClient4Utils.sendHttpRequest(url, paramMap, CHARSET, true);
+        return JSON.parseObject(responseBody);
+    }
+
+    //get请求
+    public static JSONObject httpGet(String url, String merAccount, String data) {
+        //请求参数为如下:merAccount、data
+        Map<String, String> paramMap = new HashMap<String, String>();
+        paramMap.put("data", data);
+        paramMap.put("merAccount", merAccount);
+        String responseBody = HttpClient4Utils.sendHttpRequest(url, paramMap, CHARSET, false);
+        return JSON.parseObject(responseBody);
+    }
+
+    //get请求
+    public static JSONObject httpGet(String url, String merAccount, String appid, String data) {
+        //请求参数为如下:merAccount、data
+        Map<String, String> paramMap = new HashMap<String, String>();
+        paramMap.put("data", data);
+        paramMap.put("appid", appid);
+        paramMap.put("merAccount", merAccount);
+        String responseBody = HttpClient4Utils.sendHttpRequest(url, paramMap, CHARSET, false);
+        return JSON.parseObject(responseBody);
+    }
+
+    //字符串格式化
+    public static String formatStr(Object text) {
+        return (text == null) ? "" : (text + "").trim();
+    }
+
+}

+ 106 - 0
src/main/java/com/caimei/util/RSAUtil.java

@@ -0,0 +1,106 @@
+package com.caimei.util;
+
+
+import com.caimei.model.enumerate.KeyFormat;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class RSAUtil {
+
+    public static Map<String, String> createKeys() throws NoSuchAlgorithmException {
+        //为RSA算法创建一个KeyPairGenerator对象
+        KeyPair keyPair = KeyGenerator.generateKeyPair();
+        KeyPair asnKeyPair = keyPair.toASNKeyPair();
+        //得到公钥
+        String publicKey = asnKeyPair.getPublicKey();
+        String privateKey = asnKeyPair.getPrivateKey();
+        Map<String, String> keyPairMap = new HashMap<String, String>();
+        keyPairMap.put("publicKey", publicKey);
+        keyPairMap.put("privateKey", privateKey);
+
+        return keyPairMap;
+    }
+
+    /**
+     * 公钥加密
+     *
+     * @param data
+     * @param publicKey
+     * @return
+     */
+    public static String publicEncrypt(String data, String publicKey) {
+        try {
+            KeyWorker publicWorker = new KeyWorker(publicKey, KeyFormat.ASN);
+            return publicWorker.encrypt(data);
+        } catch (Exception e) {
+            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
+        }
+    }
+
+    /**
+     * 私钥解密
+     *
+     * @param data
+     * @param privateKey
+     * @return
+     */
+
+    public static String privateDecrypt(String data, String privateKey) {
+        try {
+            KeyWorker privateWorker = new KeyWorker(privateKey, KeyFormat.ASN);
+            return privateWorker.decrypt(data);
+        } catch (Exception e) {
+            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
+        }
+    }
+
+    /**
+     * 私钥加密
+     *
+     * @param data
+     * @param privateKey
+     * @return
+     */
+
+    public static String privateEncrypt(String data, String privateKey) {
+        try {
+            KeyWorker privateWorker = new KeyWorker(privateKey, KeyFormat.ASN);
+            return privateWorker.encrypt(data);
+        } catch (Exception e) {
+            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
+        }
+    }
+
+    /**
+     * 公钥解密
+     *
+     * @param data
+     * @param publicKey
+     * @return
+     */
+
+    public static String publicDecrypt(String data, String publicKey) {
+        try {
+            KeyWorker publicWorker = new KeyWorker(publicKey, KeyFormat.ASN);
+            return publicWorker.decrypt(data);
+        } catch (Exception e) {
+            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
+        }
+    }
+
+    public static void main(String[] args) throws NoSuchAlgorithmException {
+        Map<String, String> keys = createKeys();
+        String publicKey = keys.get("publicKey");
+        String privateKey = keys.get("privateKey");
+        System.out.println("private:" + privateKey);
+        System.out.println("public:" + publicKey);
+        String conent = "sdsfalfasdl2对方是否";
+        String en = publicEncrypt(conent, publicKey);
+        System.out.println("公钥加密:" + en);
+        String de = privateDecrypt(en, privateKey);
+        System.out.println("私钥解密:" + de);
+    }
+
+}

+ 98 - 0
src/main/java/com/caimei/util/RandomCodeGenerator.java

@@ -0,0 +1,98 @@
+package com.caimei.util;
+
+import java.util.Random;
+
+public class RandomCodeGenerator {
+
+    // TODO 与WWW的RandomCodeGenerator是重复的,后续需要删除www的RandomCodeGenerator
+    private static char codeSequence[] = {
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
+            'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+            'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7',
+            '8', '9'
+    };
+
+    private static char intSequence[] = {
+            '2', '3', '4', '5', '6', '7',
+            '8', '9'
+    };
+
+    private static char stringSequence[] = {
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
+            'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+            'W', 'X', 'Y', 'Z'
+    };
+
+    private static char newStringSequence[] = {
+            'A', 'a', 'B', 'b', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'H', 'h',
+            'L', 'N', 'n', 'Q', 'q', 'R', 'r', 'T', 't', 'Y', 'y'
+    };
+//	C,i,j,k,M,O,P,S,U,V,W,X,Z,l
+
+    public static String generateCode(int length) {
+        StringBuffer sb = new StringBuffer();
+        Random random = new Random();
+        for (int i = 0; i < codeSequence.length && i < length; ++i) {
+            sb.append(codeSequence[random.nextInt(codeSequence.length)]);
+        }
+
+        return sb.toString();
+    }
+
+    public static String generateCodeInt(int length) {
+        StringBuffer sb = new StringBuffer();
+        Random random = new Random();
+        for (int i = 0; i < intSequence.length && i < length; ++i) {
+            sb.append(intSequence[random.nextInt(intSequence.length)]);
+        }
+
+        return sb.toString();
+    }
+
+    public static String generateCodeString(int length) {
+        StringBuffer sb = new StringBuffer();
+        Random random = new Random();
+        for (int i = 0; i < stringSequence.length && i < length; ++i) {
+            sb.append(stringSequence[random.nextInt(stringSequence.length)]);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 随机生成指定位数字母与数字组成的字符串
+     *
+     * @param length 长度
+     * @return
+     */
+	/*public static String generateRandomCode(int length) {
+		// 允许字母与数字的字符串
+		return RandomStringUtils.random(length, true, true);
+	}*/
+    public static String getRandomCharAndNumr(int length) {
+        String val = "";
+        Random random = new Random();
+        for (int i = 0; i < length; i++) {
+            // 输出字母还是数字
+            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
+            // 字符串
+            if ("char".equalsIgnoreCase(charOrNum)) {
+                // 取得大写字母还是小写字母
+                int choice = random.nextInt(2) % 2 == 0 ? 65 : 97;
+                val += (char) (choice + random.nextInt(26));
+            } else if ("num".equalsIgnoreCase(charOrNum)) { // 数字
+                val += String.valueOf(random.nextInt(10));
+            }
+        }
+        return val;
+    }
+
+    public static String generateAccount(int length) {
+        StringBuffer sb = new StringBuffer();
+        Random random = new Random();
+        for (int i = 0; i < newStringSequence.length && i < length; ++i) {
+            sb.append(newStringSequence[random.nextInt(newStringSequence.length)]);
+        }
+        return sb.toString();
+    }
+}

+ 174 - 0
src/main/java/com/caimei/util/XmlKeyBuilder.java

@@ -0,0 +1,174 @@
+package com.caimei.util;
+
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import sun.misc.BASE64Decoder;
+import sun.misc.BASE64Encoder;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+
+/*
+ Jeffrey Walton
+ http://www.codeproject.com/Articles/25487/Cryptographic-Interoperability-Keys
+ */
+public class XmlKeyBuilder {
+    public static String publicKeyToXML(PublicKey key) {
+        if (!RSAPublicKey.class.isInstance(key)) {
+            return null;
+        }
+        RSAPublicKey pubKey = (RSAPublicKey) key;
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<RSAKeyValue>");
+        sb.append("<Modulus>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(pubKey.getModulus()
+                        .toByteArray()))).append("</Modulus>");
+        sb.append("<Exponent>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(pubKey.getPublicExponent()
+                        .toByteArray()))).append("</Exponent>");
+        sb.append("</RSAKeyValue>");
+        return sb.toString();
+    }
+
+    public static String privateKeyToXML(PrivateKey key) {
+        if (!RSAPrivateCrtKey.class.isInstance(key)) {
+            return null;
+        }
+        RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) key;
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<RSAKeyValue>");
+        sb.append("<Modulus>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getModulus()
+                        .toByteArray()))).append("</Modulus>");
+        sb.append("<Exponent>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPublicExponent()
+                        .toByteArray()))).append("</Exponent>");
+        sb.append("<P>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrimeP()
+                        .toByteArray()))).append("</P>");
+        sb.append("<Q>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrimeQ()
+                        .toByteArray()))).append("</Q>");
+        sb.append("<DP>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrimeExponentP()
+                        .toByteArray()))).append("</DP>");
+        sb.append("<DQ>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrimeExponentQ()
+                        .toByteArray()))).append("</DQ>");
+        sb.append("<InverseQ>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getCrtCoefficient()
+                        .toByteArray()))).append("</InverseQ>");
+        sb.append("<D>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrivateExponent()
+                        .toByteArray()))).append("</D>");
+        sb.append("</RSAKeyValue>");
+        return sb.toString();
+    }
+
+    public static PublicKey xmlToPublicKey(String key)
+            throws ParserConfigurationException, SAXException, IOException {
+        key = key.replaceAll("\r", "").replaceAll("\n", "");
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        Document doc = builder.parse(new InputSource(new ByteArrayInputStream(
+                key.getBytes("utf-8"))));
+        String n = doc.getDocumentElement().getElementsByTagName("Modulus")
+                .item(0).getNodeValue();
+        String e = doc.getDocumentElement().getElementsByTagName("Exponent")
+                .item(0).getNodeValue();
+        BigInteger modulus = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(n));
+        BigInteger publicExponent = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(e));
+
+        RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus,
+                publicExponent);
+
+        KeyFactory keyf;
+        try {
+            keyf = KeyFactory.getInstance("RSA");
+            return keyf.generatePublic(rsaPubKey);
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+
+    public static PrivateKey xmlToPrivateKey(String key) throws IOException,
+            SAXException, ParserConfigurationException {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        Document doc = builder.parse(new InputSource(new ByteArrayInputStream(
+                key.getBytes("utf-8"))));
+        String n = doc.getDocumentElement().getElementsByTagName("Modulus")
+                .item(0).getNodeValue();
+        String e = doc.getDocumentElement().getElementsByTagName("Exponent")
+                .item(0).getNodeValue();
+        String d = doc.getDocumentElement().getElementsByTagName("D").item(0)
+                .getNodeValue();
+        String p = doc.getDocumentElement().getElementsByTagName("P").item(0)
+                .getNodeValue();
+        String q = doc.getDocumentElement().getElementsByTagName("Q").item(0)
+                .getNodeValue();
+        String dp = doc.getDocumentElement().getElementsByTagName("DP").item(0)
+                .getNodeValue();
+        String dq = doc.getDocumentElement().getElementsByTagName("DQ").item(0)
+                .getNodeValue();
+        String inverseQ = doc.getDocumentElement()
+                .getElementsByTagName("InverseQ").item(0).getNodeValue();
+
+        key = key.replaceAll("\r", "").replaceAll("\n", "");
+        BigInteger modulus = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(n));
+        BigInteger publicExponent = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(e));
+        BigInteger privateExponent = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(d));
+        BigInteger primeP = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(p));
+        BigInteger primeQ = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(q));
+        BigInteger primeExponentP = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(dp));
+        BigInteger primeExponentQ = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(dq));
+        BigInteger crtCoefficient = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(inverseQ));
+
+        RSAPrivateCrtKeySpec rsaPriKey = new RSAPrivateCrtKeySpec(modulus,
+                publicExponent, privateExponent, primeP, primeQ,
+                primeExponentP, primeExponentQ, crtCoefficient);
+
+        KeyFactory keyf;
+        try {
+            keyf = KeyFactory.getInstance("RSA");
+            return keyf.generatePrivate(rsaPriKey);
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+
+    static byte[] TrimLeadingZero(byte[] values) {
+        if ((0x00 == values[0]) && (values.length > 1)) {
+            byte[] r = null;
+            r = new byte[values.length - 1];
+            System.arraycopy(values, 1, r, 0, r.length);
+            return r;
+        }
+
+        return values;
+    }
+}

+ 0 - 2
src/main/resources/config/beta/application-beta.yml

@@ -56,5 +56,3 @@ swagger:
 # 新旧www服务域名
 caimei:
   oldapi: https://www-b.caimei365.com
-  newapi: https://spi-b.caimei365.com
-  imageDomain: https://img-b.caimei365.com

+ 10 - 2
src/main/resources/config/dev/application-dev.yml

@@ -58,9 +58,17 @@ swagger:
 wx:
   AppId: wxca7172d7a20bdf7a
   AppSecret: d7f853a64b73d01ef93f3829852a790e
+  crmAppId: wxea43a0f9ebce9e66
+  crmAppSecret: 1c3cd60908e72dd280840bee9e15f7f6
 
 # 新旧www服务域名
 caimei:
   oldapi: http://localhost:8100
-  newapi: http://localhost:8008
-  imageDomain: https://img-b.caimei365.com
+  #支付异步回调地址
+  notifyUrl: https://spi-b.caimei365.com/PayOrder/paymentCallback
+  #支付链接重定向地址
+  redirectLink: https://spi-b.caimei365.com/PayOrder/jumpPage
+  #链接页面
+  linkPage: https://www-b.caimei365.com/pay/caimei-pay.html
+  #延时分账异步回调地址
+  delayedSplittingUrl: https://spi-b.caimei365.com/PayOrder/delayedSplittingCallback

+ 0 - 2
src/main/resources/config/prod/application-prod.yml

@@ -56,6 +56,4 @@ swagger:
 # 新旧www服务域名
 caimei:
   oldapi: https://www.caimei365.com
-  newapi: https://spi.caimei365.com
-  imageDomain: https://img.caimei365.com
 

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

@@ -0,0 +1,118 @@
+<?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.caimei.mapper.OrderMapper">
+    <select id="findOrderList" resultType="com.caimei.model.vo.OrderVo">
+        select
+          orderID AS orderId,
+          organizeID AS organizeId,
+          userID AS userId,
+          status,
+          shopOrderIDs AS shopOrderIds,
+          receiptStatus,
+          payStatus,
+          sendOutStatus
+        from cm_order
+        where delFlag = '0'
+        AND userID = #{userId}
+        AND organizeID = #{organizeId}
+        <if test="orderState == 1 ">
+            AND status = '0'
+        </if>
+        <if test="orderState == 2 ">
+            AND status IN(11,12,13,21,22,23)
+        </if>
+        <if test="orderState == 3 ">
+            AND status IN(11,12,21,22,31,32)
+        </if>
+        <if test="orderState == 4 ">
+            AND status IN(12,13,22,23,32,33)
+        </if>
+        <if test="orderState == 5 ">
+            AND refundType IN(1,2)
+        </if>
+        ORDER BY orderTime DESC
+    </select>
+
+    <select id="findLogistics" resultType="com.caimei.model.vo.LogisticsBatchVo">
+        SELECT * FROM cm_logistics_batch WHERE orderID= #{orderId}
+    </select>
+
+    <select id="findAllShopOrder" resultType="com.caimei.model.vo.ShopOrderVo">
+        SELECT
+        cso.shopOrderID AS shopOrderId,
+        s.name AS shopName,
+        s.logo AS shopLogo
+        FROM
+        cm_shop_order cso
+        LEFT JOIN shop s ON cso.shopID = s.shopID
+        WHERE
+        cso.orderID = #{orderId}
+        AND cso.delFlag = '0'
+    </select>
+
+    <select id="findOrderProduct" resultType="com.caimei.model.vo.OrderProductVo">
+        SELECT
+          cop.orderProductID AS orderProductId,
+          p.mainImage AS productImage,
+          p.productCategory as productCategory
+        FROM
+          cm_order_product cop
+          LEFT JOIN product p ON cop.productID = p.productID
+        WHERE
+          shopOrderID = #{shopOrderId}
+    </select>
+
+    <select id="findOrderPromotionsById" resultType="com.caimei.model.po.PromotionsPo">
+        SELECT
+          id,
+          name,
+          description,
+          orderId,
+          type,
+          mode,
+          touchPrice,
+          reducedPrice,
+          beginTime,
+          endTime,
+          status
+        FROM
+          cm_promotions_order
+        WHERE
+          id = #{orderPromotionsId}
+    </select>
+
+    <select id="findDiscernReceipt" resultType="com.caimei.model.vo.DiscernReceiptVo">
+        SELECT
+          cdr.*,cror.associateAmount
+        FROM
+          cm_receipt_order_relation cror
+          LEFT JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+        WHERE
+          cror.orderID = #{orderID}
+          AND cror.relationType = '2'
+          AND cror.delFlag = '0'
+          AND cdr.delFlag = '0'
+          AND cdr.receiptStatus = '3'
+          AND cdr.payType != '16'
+        ORDER BY
+          cdr.receiptDate DESC
+    </select>
+
+    <select id="findOfflinePayment" resultType="com.caimei.model.vo.DiscernReceiptVo">
+        SELECT
+          cdr.*,cror.associateAmount
+        FROM
+          cm_receipt_order_relation cror
+          LEFT JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+        WHERE
+          cror.orderID = #{orderId}
+          AND cror.relationType = '2'
+          AND cror.delFlag = '0'
+          AND cdr.delFlag = '0'
+          AND cdr.receiptStatus IN(2,3)
+          AND cdr.payWay = '2'
+        LIMIT 1
+    </select>
+</mapper>

+ 441 - 0
src/main/resources/mapper/PayOrderMapper.xml

@@ -0,0 +1,441 @@
+<?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.caimei.mapper.PayOrderMapper">
+    <select id="findOrder" resultType="com.caimei.model.vo.OrderVo">
+        SELECT
+          *
+        FROM
+          cm_order
+        WHERE
+          orderID = #{orderID}
+          AND delFlag = '0'
+    </select>
+
+    <select id="getDiscernReceipt" resultType="com.caimei.model.vo.DiscernReceiptVo">
+        SELECT
+          cdr.*,
+          cror.associateAmount
+        FROM
+          cm_receipt_order_relation cror
+          LEFT JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+        WHERE
+          (
+            cror.orderID = #{orderID}
+            AND cror.relationType = '2'
+          OR
+            cror.orderID = #{shopOrderIDs}
+            AND cror.relationType = '1'
+          )
+          AND cror.delFlag = '0'
+          AND cdr.delFlag = '0'
+          AND cdr.payType != '16'
+          AND cdr.receiptStatus IN(2,3)
+        ORDER BY
+          cdr.receiptDate DESC
+    </select>
+
+    <update id="updateSelective">
+        update cm_order
+        <set>
+            <if test="orderNo != null">
+                orderNo = #{orderNo,jdbcType=VARCHAR},
+            </if>
+            <if test="userID != null">
+                userID = #{userID,jdbcType=BIGINT},
+            </if>
+            <if test="buyUserID != null">
+                buyUserID = #{buyUserID,jdbcType=INTEGER},
+            </if>
+            <if test="shopOrderIDs != null">
+                shopOrderIDs = #{shopOrderIDs,jdbcType=VARCHAR},
+            </if>
+            <if test="orderSubmitType != null">
+                orderSubmitType = #{orderSubmitType,jdbcType=INTEGER},
+            </if>
+            <if test="orderType != null">
+                orderType = #{orderType,jdbcType=INTEGER},
+            </if>
+            <if test="hasActProduct != null">
+                hasActProduct = #{hasActProduct,jdbcType=CHAR},
+            </if>
+            <if test="autoCloseTimeMills != null">
+                autoCloseTimeMills = #{autoCloseTimeMills,jdbcType=DECIMAL},
+            </if>
+            <if test="status != null">
+                `status` = #{status,jdbcType=CHAR},
+            </if>
+            <if test="receiptStatus != null">
+                receiptStatus = #{receiptStatus,jdbcType=CHAR},
+            </if>
+            <if test="payStatus != null">
+                payStatus = #{payStatus,jdbcType=CHAR},
+            </if>
+            <if test="sendOutStatus != null">
+                sendOutStatus = #{sendOutStatus,jdbcType=CHAR},
+            </if>
+            <if test="refundType != null">
+                refundType = #{refundType,jdbcType=CHAR},
+            </if>
+            <if test="paySuccessCounter != null">
+                paySuccessCounter = #{paySuccessCounter},
+            </if>
+            <if test="payFlag != null">
+                payFlag = #{payFlag,jdbcType=CHAR},
+            </if>
+            <if test="onlinePayFlag != null">
+                onlinePayFlag = #{onlinePayFlag,jdbcType=CHAR},
+            </if>
+            <if test="productTotalFee != null">
+                productTotalFee = #{productTotalFee,jdbcType=DECIMAL},
+            </if>
+            <if test="orderTotalFee != null">
+                orderTotalFee = #{orderTotalFee,jdbcType=DECIMAL},
+            </if>
+            <if test="payTotalFee != null">
+                payTotalFee = #{payTotalFee,jdbcType=DECIMAL},
+            </if>
+            <if test="payableAmount != null">
+                payableAmount = #{payableAmount,jdbcType=DECIMAL},
+            </if>
+            <if test="balancePayFee != null">
+                balancePayFee = #{balancePayFee,jdbcType=DECIMAL},
+            </if>
+            <if test="preferential != null">
+                preferential = #{preferential,jdbcType=DECIMAL},
+            </if>
+            <if test="discountFee != null">
+                discountFee = #{discountFee,jdbcType=DECIMAL},
+            </if>
+            <if test="spID != null">
+                spID = #{spID,jdbcType=BIGINT},
+            </if>
+            <if test="mainSpID != null">
+                mainSpID = #{mainSpID,jdbcType=BIGINT},
+            </if>
+            <if test="note != null">
+                note = #{note,jdbcType=VARCHAR},
+            </if>
+            <if test="clubID != null">
+                clubID = #{clubID,jdbcType=BIGINT},
+            </if>
+            <if test="clubScanTime != null">
+                clubScanTime = #{clubScanTime,jdbcType=VARCHAR},
+            </if>
+            <if test="payWay != null">
+                payWay = #{payWay,jdbcType=VARCHAR},
+            </if>
+            <if test="orderSource != null">
+                orderSource = #{orderSource,jdbcType=CHAR},
+            </if>
+            <if test="closeTime != null">
+                closeTime = #{closeTime,jdbcType=VARCHAR},
+            </if>
+            <if test="confirmTime != null">
+                confirmTime = #{confirmTime,jdbcType=VARCHAR},
+            </if>
+            <if test="payTime != null">
+                payTime = #{payTime,jdbcType=VARCHAR},
+            </if>
+            <if test="orderTime != null">
+                orderTime = #{orderTime,jdbcType=VARCHAR},
+            </if>
+            <if test="productCount != null">
+                productCount = #{productCount,jdbcType=INTEGER},
+            </if>
+            <if test="presentCount != null">
+                presentCount = #{presentCount,jdbcType=INTEGER},
+            </if>
+            <if test="cooFreeFlag != null">
+                cooFreeFlag = #{cooFreeFlag,jdbcType=CHAR},
+            </if>
+            <if test="cooFreeRate != null">
+                cooFreeRate = #{cooFreeRate,jdbcType=INTEGER},
+            </if>
+            <if test="cooFreeAmount != null">
+                cooFreeAmount = #{cooFreeAmount,jdbcType=DECIMAL},
+            </if>
+            <if test="invoiceFlag != null">
+                invoiceFlag = #{invoiceFlag,jdbcType=CHAR},
+            </if>
+            <if test="confirmFlag != null">
+                confirmFlag = #{confirmFlag,jdbcType=CHAR},
+            </if>
+            <if test="clauseID != null">
+                clauseID = #{clauseID,jdbcType=BIGINT},
+            </if>
+            <if test="clauseContent != null">
+                clauseContent = #{clauseContent,jdbcType=VARCHAR},
+            </if>
+            <if test="clauseName != null">
+                clauseName = #{clauseName,jdbcType=VARCHAR},
+            </if>
+            <if test="updateDate != null">
+                updateDate = #{updateDate,jdbcType=VARCHAR},
+            </if>
+            <if test="freePostFlag != null">
+                freePostFlag = #{freePostFlag,jdbcType=CHAR},
+            </if>
+            <if test="freight != null">
+                freight = #{freight,jdbcType=DECIMAL},
+            </if>
+            <if test="delFlag != null">
+                delFlag = #{delFlag,jdbcType=CHAR},
+            </if>
+            <if test="freePostageTicketID != null">
+                freePostageTicketID = #{freePostageTicketID,jdbcType=INTEGER},
+            </if>
+            <if test="splitFlag != null">
+                splitFlag = #{splitFlag,jdbcType=CHAR},
+            </if>
+            <if test="closeReason != null">
+                closeReason = #{closeReason,jdbcType=VARCHAR},
+            </if>
+            <if test="postageOrderFlag != null">
+                postageOrderFlag = #{postageOrderFlag,jdbcType=CHAR},
+            </if>
+        </set>
+        where orderID = #{orderID,jdbcType=BIGINT}
+    </update>
+
+    <insert id="insertDiscernReceipt" keyColumn="id" keyProperty="id" useGeneratedKeys="true" parameterType="com.caimei.model.po.CmDiscernReceiptPo">
+        INSERT INTO `cm_discern_receipt` (
+          `payWay`, `payType`, `receiptType`,
+          `receiptStatus`, `smsContent`, `smsMd5Code`,
+          `orderFlag`, `receiptAmount`, `handlingFee`,
+          `confirmType`, `confirmUserPermissionID`,
+          `reviewUserPermissionID`, `cancelUserPermissionID`,
+          `transactionNum`, `bankID`, `bankCode`,
+          `kuaiQianPayTypeID`, `kuaiQianPayerID`,
+          `rePayFlag`, `actualAmount`, `formData`,
+          `problem`, `noOrderReason`, `reviewReason`,
+          `cancelReason`, `receiptDate`, `confirmDate`,
+          `reviewDate`, `cancelDate`, `updateDate`,
+          `delFlag`
+        )
+        VALUES
+          (
+            #{payWay}, #{payType}, #{receiptType},
+            #{receiptStatus}, #{smsContent}, #{smsMd5Code},
+            #{orderFlag}, #{receiptAmount}, #{handlingFee},
+            #{confirmType}, #{confirmUserPermissionID},
+            #{reviewUserPermissionID}, #{cancelUserPermissionID},
+            #{transactionNum}, #{bankID}, #{bankCode},
+            #{kuaiQianPayTypeID}, #{kuaiQianPayerID},
+            #{rePayFlag}, #{actualAmount}, #{formData},
+            #{problem}, #{noOrderReason}, #{reviewReason},
+            #{cancelReason}, #{receiptDate}, #{confirmDate},
+            #{reviewDate}, #{cancelDate}, #{updateDate},
+            #{delFlag}
+          )
+    </insert>
+
+    <insert id="insertOrderRelation" keyColumn="id" keyProperty="id" useGeneratedKeys="true" parameterType="com.caimei.model.po.CmReceiptOrderRelationPo">
+        INSERT INTO `cm_receipt_order_relation` (
+          `relationType`, `receiptID`, `associateAmount`,
+          `orderID`, `delFlag`, mbOrderId, orderRequestNo,
+          splitStatus
+        )
+        VALUES
+          (
+            #{relationType}, #{receiptID}, #{associateAmount},
+            #{orderID}, #{delFlag},#{mbOrderId},#{orderRequestNo},
+            #{splitStatus}
+          )
+    </insert>
+
+    <select id="findOrderPayLink" resultType="com.caimei.model.vo.OrderPayLinkVo">
+        SELECT
+        *
+        FROM
+        cm_order_pay_link
+        WHERE
+        orderId = #{orderId}
+        <if test="amount != null">
+            AND unpaidAmount = #{amount}
+        </if>
+        AND delFlag = '0'
+        AND payStatus = '0'
+        ORDER BY id DESC LIMIT 1
+    </select>
+
+    <insert id="insertOrderPayLink">
+        insert into cm_order_pay_link
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="orderId != null">
+                orderId,
+            </if>
+            <if test="linkLogo != null">
+                linkLogo,
+            </if>
+            <if test="unpaidAmount != null">
+                unpaidAmount,
+            </if>
+            <if test="generateTime != null">
+                generateTime,
+            </if>
+            <if test="effectiveTime != null">
+                effectiveTime,
+            </if>
+            <if test="payStatus != null">
+                payStatus,
+            </if>
+            <if test="payType != null">
+                payType,
+            </if>
+            <if test="delFlag != null">
+                delFlag,
+            </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="orderId != null">
+                #{orderId,jdbcType=BIGINT},
+            </if>
+            <if test="linkLogo != null">
+                #{linkLogo,jdbcType=VARCHAR},
+            </if>
+            <if test="unpaidAmount != null">
+                #{unpaidAmount,jdbcType=DECIMAL},
+            </if>
+            <if test="generateTime != null">
+                #{generateTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="effectiveTime != null">
+                #{effectiveTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="payStatus != null">
+                #{payStatus,jdbcType=CHAR},
+            </if>
+            <if test="payType != null">
+                #{payType},
+            </if>
+            <if test="delFlag != null">
+                #{delFlag,jdbcType=CHAR},
+            </if>
+        </trim>
+    </insert>
+
+    <update id="updateOrderPayLink">
+        update cm_order_pay_link
+        <set>
+            <if test="orderId != null">
+                orderId = #{orderId,jdbcType=BIGINT},
+            </if>
+            <if test="linkLogo != null">
+                linkLogo = #{linkLogo,jdbcType=VARCHAR},
+            </if>
+            <if test="unpaidAmount != null">
+                unpaidAmount = #{unpaidAmount,jdbcType=DECIMAL},
+            </if>
+            <if test="generateTime != null">
+                generateTime = #{generateTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="effectiveTime != null">
+                effectiveTime = #{effectiveTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="payStatus != null">
+                payStatus = #{payStatus,jdbcType=CHAR},
+            </if>
+            <if test="payType != null">
+                payType = #{payType},
+            </if>
+            <if test="delFlag != null">
+                delFlag = #{delFlag,jdbcType=CHAR},
+            </if>
+        </set>
+        where id = #{id,jdbcType=INTEGER}
+    </update>
+
+    <select id="getOrderPayLink" resultType="com.caimei.model.vo.OrderPayLinkVo">
+        SELECT
+          *
+        FROM
+          cm_order_pay_link
+        WHERE
+          linkLogo = #{linkLogo}
+          AND delFlag = '0'
+    </select>
+
+    <select id="findAllOrderProduct" resultType="com.caimei.model.vo.OrderProductVo">
+        SELECT * FROM cm_order_product WHERE orderID = #{orderId} ORDER BY shopID ASC, productID ASC
+    </select>
+
+    <select id="findUser" resultType="com.caimei.model.po.UserPo">
+        SELECT
+          *
+        FROM
+          USER
+        WHERE
+          userID = #{userID}
+    </select>
+    <select id="getPayOnLineSwitch" resultType="java.lang.Integer">
+        select status from cm_pay_online_switch where id=1
+    </select>
+
+    <insert id="insertSplitAccount" keyColumn="id" keyProperty="id" parameterType="com.caimei.model.vo.SplitAccountVo" useGeneratedKeys="true">
+        INSERT INTO `cm_split_account` (
+          `orderId`, `productId`, `orderProductId`,
+          `shopId`, `type`, `subUserNo`, `splitAccount`,
+          `mbOrderId`, `orderRequestNo`,
+          `payStatus`, `productType`, splitTime
+        )
+        VALUES
+          (
+            #{orderId}, #{productId}, #{orderProductId},
+            #{shopId}, #{type}, #{subUserNo}, #{splitAccount},
+            #{mbOrderId}, #{orderRequestNo},
+            #{payStatus}, #{productType}, NOW()
+          );
+    </insert>
+
+    <select id="findCommercialCode" resultType="string">
+        SELECT commercialCode FROM shop WHERE shopID = #{shopId}
+    </select>
+
+    <select id="findPaidAmount" resultType="java.math.BigDecimal">
+        SELECT
+          SUM(splitAccount)
+        FROM
+          cm_split_account
+        WHERE
+          orderProductId = #{orderProductId}
+          AND payStatus = 1
+          AND productType = 1
+    </select>
+
+    <select id="findShopOrder" resultType="com.caimei.model.vo.ShopOrderVo">
+        SELECT * FROM cm_shop_order WHERE orderID = #{orderId}
+    </select>
+
+    <select id="findShipping" resultType="java.math.BigDecimal">
+        SELECT
+          SUM(splitAccount)
+        FROM
+          cm_split_account
+        WHERE
+          orderId = #{orderId}
+          AND shopId = #{shopId}
+          AND productType = 2
+          AND payStatus = 1
+    </select>
+
+    <update id="updateShopOrderByPayStatus">
+        UPDATE
+          cm_shop_order
+        SET
+          payStatus = #{payStatus},
+          payedShopAmount = #{paidShop}
+        WHERE
+          shopOrderID = #{shopOrderId}
+    </update>
+
+    <update id="updateOrderByPayStatus">
+        UPDATE
+          cm_order
+        SET
+          payStatus = #{payStatus}
+        WHERE
+          orderID = #{orderId}
+    </update>
+</mapper>