Browse Source

Merge remote-tracking branch 'origin/developerC' into developerC

zhijiezhao 3 years ago
parent
commit
460c87e77e
35 changed files with 5971 additions and 75 deletions
  1. 53 1
      backup.sql
  2. 75 55
      src/main/java/com/caimei365/order/components/WeChatService.java
  3. 405 0
      src/main/java/com/caimei365/order/controller/ReceiptApi.java
  4. 8 0
      src/main/java/com/caimei365/order/mapper/BaseMapper.java
  5. 5 0
      src/main/java/com/caimei365/order/mapper/OrderCommonMapper.java
  6. 213 0
      src/main/java/com/caimei365/order/mapper/ReceiptMapper.java
  7. 67 0
      src/main/java/com/caimei365/order/model/bo/ReceiptParamsBo.java
  8. 52 0
      src/main/java/com/caimei365/order/model/dto/ReceiptAssociateDto.java
  9. 46 0
      src/main/java/com/caimei365/order/model/dto/ReceiptAuthDto.java
  10. 79 0
      src/main/java/com/caimei365/order/model/dto/ReceiptDto.java
  11. 79 0
      src/main/java/com/caimei365/order/model/dto/ReceiptOperateDto.java
  12. 14 1
      src/main/java/com/caimei365/order/model/enums/OrderStatus.java
  13. 47 0
      src/main/java/com/caimei365/order/model/enums/ReceiptStatus.java
  14. 42 0
      src/main/java/com/caimei365/order/model/enums/ReceiptType.java
  15. 3 1
      src/main/java/com/caimei365/order/model/enums/ReceivablesType.java
  16. 4 0
      src/main/java/com/caimei365/order/model/po/BalanceRecordPo.java
  17. 4 0
      src/main/java/com/caimei365/order/model/po/OrderReceiptRelationPo.java
  18. 41 0
      src/main/java/com/caimei365/order/model/po/RebateRecordPo.java
  19. 126 0
      src/main/java/com/caimei365/order/model/po/ReceiptPo.java
  20. 52 0
      src/main/java/com/caimei365/order/model/po/RefundShopPo.java
  21. 49 0
      src/main/java/com/caimei365/order/model/po/RefundShopRecordPo.java
  22. 48 1
      src/main/java/com/caimei365/order/model/vo/OrderVo.java
  23. 27 0
      src/main/java/com/caimei365/order/model/vo/ReceiptTypeVo.java
  24. 63 0
      src/main/java/com/caimei365/order/model/vo/ReceiptUserVo.java
  25. 149 0
      src/main/java/com/caimei365/order/model/vo/ReceiptVo.java
  26. 25 0
      src/main/java/com/caimei365/order/model/vo/ShopOrderVo.java
  27. 215 0
      src/main/java/com/caimei365/order/service/ReceiptService.java
  28. 2132 0
      src/main/java/com/caimei365/order/service/impl/ReceiptServiceImpl.java
  29. 38 0
      src/main/java/com/caimei365/order/task/ReceiptMsgTask.java
  30. 785 0
      src/main/java/com/caimei365/order/utils/DateUtils.java
  31. 75 3
      src/main/java/com/caimei365/order/utils/RequestUtil.java
  32. 14 8
      src/main/resources/mapper/BaseMapper.xml
  33. 54 1
      src/main/resources/mapper/OrderCommonMapper.xml
  34. 1 4
      src/main/resources/mapper/PayOrderMapper.xml
  35. 881 0
      src/main/resources/mapper/ReceiptMapper.xml

+ 53 - 1
backup.sql

@@ -76,4 +76,56 @@ ALTER TABLE `caimei`.`cm_order_product`
     ADD COLUMN `svipPriceFlag` INT NULL DEFAULT 0 COMMENT '超级会员优惠价格标识:0不是,1是' AFTER `heUserId`,
     ADD COLUMN `svipPriceType` INT NULL DEFAULT NULL COMMENT '超级会员优惠价类型:1折扣价,2直接优惠价' AFTER `svipPriceFlag`,
     ADD COLUMN `svipDiscount` DECIMAL(20,2) NULL DEFAULT '0.00' COMMENT '超级会员折扣' AFTER `svipPriceFlag`,
-    ADD COLUMN `svipReduction` DECIMAL(20,2) NULL DEFAULT 0.00 COMMENT '超级会员优惠' AFTER `svipDiscount`;
+    ADD COLUMN `svipReduction` DECIMAL(20,2) NULL DEFAULT 0.00 COMMENT '超级会员优惠' AFTER `svipDiscount`;
+
+-- ===========================CRM收款工具重构================================
+-- 收款用户表
+DROP TABLE IF EXISTS `cm_receipt_user`;
+CREATE TABLE `cm_receipt_user` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '收款用户ID',
+  `mobile` VARCHAR(11) NULL COMMENT '收款用户手机号',
+  `password` VARCHAR(255) NULL COMMENT '收款用户密码',
+  `openid` varchar(255) NULL COMMENT '收款用户微信openid',
+  `unionId` varchar(255) NULL COMMENT '收款用户微信unionId',
+  `user_type` char(2) NULL COMMENT '收款用户类型:1协销人员,2客服,3财务,4超级管理员',
+  `name` VARCHAR(100) NOT NULL COMMENT '姓名',
+  `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
+  `update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
+  `del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标记:0未删,其他删除',
+  PRIMARY KEY (`id`)
+) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 COMMENT='采美收款用户表';
+ -- 收款权限表
+DROP TABLE IF EXISTS `cm_receipt_permission`;
+CREATE TABLE `cm_receipt_permission` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '收款权限ID',
+  `permission` VARCHAR(100) DEFAULT NULL COMMENT '收款权限描述',
+  PRIMARY KEY (`id`)
+) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 COMMENT='采美收款权限表';
+ -- 采美收款用户权限关联表
+DROP TABLE IF EXISTS `cm_receipt_user_permission`;
+CREATE TABLE `cm_receipt_user_permission` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT,
+  `user_id` BIGINT DEFAULT NULL COMMENT '收款用户ID',
+  `permission_id` BIGINT DEFAULT NULL COMMENT '收款权限ID',
+  PRIMARY KEY (`id`)
+) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 COMMENT='采美收款用户权限关联表';
+ -- 收款类型表
+DROP TABLE IF EXISTS `cm_receipt_type`;
+CREATE TABLE `cm_receipt_type` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '收款类型ID',
+  `type` VARCHAR(100) NOT NULL COMMENT '收款类型',
+  PRIMARY KEY (`id`)
+) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采美收款类型表';
+-- 初始数据
+INSERT  INTO `cm_receipt_permission`(`id`,`permission`) VALUES (1,'收款列表访问'),(2,'收款详情访问'),(3,'收款录入'),(4,'收款关联订单'),(5,'收款关联返佣'),(6,'收款关联供应商退款'),(7,'收款审核');
+INSERT  INTO `cm_receipt_type`(`id`,`type`) VALUES (1,'建设银行-7297'),(2,'广发银行-0115'),(3,'中信银行-7172'),(4,'中信银行-0897'),(5,'中信银行-0897-财付通'),(6,'中信银行-0897-支付宝'),(7,'线上-支付宝'),(8,'线上-微信支付'),(9,'线上-快钱支付'),(10,'口头返佣'),(11,'广发银行-5461'),(12,'企业网银'),(13,'微信支付'),(14,'支付宝'),(15,'微信支付'),(16,'余额抵扣'),(18,'建设银行-1854'),(19,'建设银行-6256');
+
+
+
+
+
+
+
+
+
+

+ 75 - 55
src/main/java/com/caimei365/order/components/WeChatService.java

@@ -40,6 +40,10 @@ public class WeChatService {
     private String miniAppId;
     @Value("${wx.mini-app-secret}")
     private String miniAppSecret;
+    @Value("${wx.receipt-app-id}")
+    private String receiptAppId;
+    @Value("${wx.receipt-app-secret}")
+    private String receiptAppSecret;
     @Value("${wx.crm-app-id}")
     private String crmAppId;
     @Value("${wx.crm-app-secret}")
@@ -48,62 +52,12 @@ public class WeChatService {
     private String heHeAppId;
     @Value("${wx.hehe-app-secret}")
     private String heHeAppSecret;
+    /**
+     * 收款通知模板Id
+     */
+    @Value("${wx.message-template.receipt}")
+    private String messageTemplateReceipt;
 
-    public void setRedirectUri(String redirectUri) {
-        redirectUri = redirectUri;
-    }
-
-    public void setAppId(String appId) {
-        appId = appId;
-    }
-
-    public void setAppSecret(String appSecret) {
-        appSecret = appSecret;
-    }
-
-    public void setMiniAppId(String miniAppId) {
-        miniAppId = miniAppId;
-    }
-
-    public void setMiniAppSecret(String miniAppSecret) {
-        miniAppSecret = miniAppSecret;
-    }
-
-    public void setCrmAppId(String crmAppId) {
-        crmAppId = crmAppId;
-    }
-
-    public void setCrmAppSecret(String crmAppSecret) {
-        crmAppSecret = crmAppSecret;
-    }
-
-    public String getRedirectUri() {
-        return redirectUri;
-    }
-
-    public String getAppId() {
-        return appId;
-    }
-
-    public String getAppSecret() {
-        return appSecret;
-    }
-
-    public String getMiniAppId() {
-        return miniAppId;
-    }
-
-    public String getMiniAppSecret() {
-        return miniAppSecret;
-    }
-
-    public String getCrmAppId() {
-        return crmAppId;
-    }
-
-    public String getCrmAppSecret() {
-        return crmAppSecret;
-    }
 
     public static class Keys {
         public static final String OPEN_ID = "openid";
@@ -173,6 +127,12 @@ public class WeChatService {
             log.info("采美小程序appId: ---" + miniAppId);
             // 小程序appSecret
             requestUrlParam.put("secret", miniAppSecret);
+        } else if (mode == 2) {
+            // 收款工具小程序appId
+            requestUrlParam.put("appid", receiptAppId);
+            log.info("收款工具小程序appId: ---" + receiptAppId);
+            // 收款工具小程序appSecret
+            requestUrlParam.put("secret", receiptAppSecret);
         } else {
             // 呵呵商城小程序appId
             requestUrlParam.put("appid", heHeAppId);
@@ -295,4 +255,64 @@ public class WeChatService {
         return map;
     }
 
+    /**
+     * 推送收款微信模板消息
+     *
+     * 模板内容:
+     * {{first.DATA}}
+     * 交易金额:{{keyword1.DATA}}
+     * 交易时间:{{keyword2.DATA}}
+     * {{remark.DATA}}
+     * 在发送时,需要将内容中的参数({{.DATA}}内为参数)赋值替换为需要的信息
+     *  @param openid 被推送人的openid
+     * @param title 标题
+     * @param money 金额
+     * @param time 日期时间
+     * @param remarkText 备注文本:收款类型+确认时间
+     * @param pagePath 小程序跳转链接
+     */
+    public void sendTemplateMsg(String openid, String title, Double money, String time, String remarkText, String pagePath) {
+        JSONObject first = new JSONObject();
+        first.put("value", title);
+        first.put("color", "#000000");
+        JSONObject keyword1 = new JSONObject();
+        keyword1.put("value", money.toString());
+        keyword1.put("color", "#FF0000");
+        JSONObject keyword2 = new JSONObject();
+        keyword2.put("value", time);
+        keyword2.put("color", "#c4c400");
+        JSONObject remark = new JSONObject();
+        keyword2.put("value", remarkText);
+        keyword2.put("color", "#c4c400");
+
+        JSONObject data = new JSONObject();
+        data.put("first", first);
+        data.put("keyword1", keyword1);
+        data.put("keyword2", keyword2);
+        data.put("remark", remark);
+
+        JSONObject miniProgram = new JSONObject();
+        miniProgram.put("appid", receiptAppId);
+        miniProgram.put("pagepath", pagePath);
+
+        JSONObject json = new JSONObject();
+        json.put("template_id", messageTemplateReceipt);
+        json.put("topcolor", "#FF0000");
+        json.put("touser", openid);
+        json.put("url", "https://www.caimei365.com/");
+        json.put("miniprogram", miniProgram);
+        json.put("data", data);
+        // json 字符串
+        String jsonString = json.toJSONString();
+        log.info(">>>>>>>>推送微信模板消息:" + jsonString);
+        try {
+            // 获取access_token
+            String accessToken = getAccessToken();
+            String requestUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+accessToken;
+            // 发送请求
+            RequestUtil.httpRequest(requestUrl, "POST", jsonString);
+        } catch (Exception e) {
+            log.error("推送微信模板消息失败:", e);
+        }
+    }
 }

+ 405 - 0
src/main/java/com/caimei365/order/controller/ReceiptApi.java

@@ -0,0 +1,405 @@
+package com.caimei365.order.controller;
+
+import com.caimei365.order.model.ResponseJson;
+import com.caimei365.order.model.dto.ReceiptAuthDto;
+import com.caimei365.order.model.dto.ReceiptDto;
+import com.caimei365.order.model.dto.ReceiptOperateDto;
+import com.caimei365.order.model.po.ReceiptPo;
+import com.caimei365.order.model.vo.*;
+import com.caimei365.order.service.ReceiptService;
+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 lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpHeaders;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 线下收款接口
+ *
+ * @author : Charles
+ * @date : 2022/2/11
+ */
+@Api(tags = "线下收款API")
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/order/receipt")
+public class ReceiptApi {
+    private final ReceiptService receiptService;
+
+    /**
+     * 线下收款用户微信授权
+     *
+     * @param receiptAuthDto {
+     *                       code          微信授权code
+     *                       encryptedData 微信加密数据
+     *                       iv            加密算法的初始向量
+     *                       }
+     */
+    @ApiOperation("线下收款用户微信授权(小程序)")
+    @PostMapping("/auth")
+    public ResponseJson<ReceiptUserVo> receiptAuthorization(ReceiptAuthDto receiptAuthDto, @RequestHeader HttpHeaders headers) {
+        String code = receiptAuthDto.getCode();
+        String encryptedData = receiptAuthDto.getEncryptedData();
+        String iv = receiptAuthDto.getIv();
+        if (!StringUtils.hasLength(code)) {
+            return ResponseJson.error("没有获取到微信授权code", null);
+        }
+        return receiptService.receiptAuthorization(code, encryptedData, iv, headers);
+    }
+
+    /**
+     * 线下收款用户密码登录
+     *
+     * @param receiptAuthDto {
+     *                       code          微信授权code
+     *                       encryptedData 微信加密数据
+     *                       iv            加密算法的初始向量
+     *                       mobile        手机号
+     *                       password      密码
+     *                       }
+     */
+    @ApiOperation("线下收款用户密码登录")
+    @PostMapping("/password")
+    public ResponseJson<ReceiptUserVo> passwordAuthorization(ReceiptAuthDto receiptAuthDto, @RequestHeader HttpHeaders headers) {
+        String mobile = receiptAuthDto.getMobile();
+        String password = receiptAuthDto.getPassword();
+        String code = receiptAuthDto.getCode();
+        String encryptedData = receiptAuthDto.getEncryptedData();
+        String iv = receiptAuthDto.getIv();
+        if (!StringUtils.hasLength(code)) {
+            return ResponseJson.error("没有获取到微信授权code", null);
+        }
+        if (StringUtils.hasLength(mobile) && StringUtils.hasLength(password)) {
+            return receiptService.passwordAuthorization(mobile, password, code, encryptedData, iv, headers);
+        } else {
+            return ResponseJson.error("手机号或密码不能为空", null);
+        }
+    }
+
+    /**
+     * 获取收款类型
+     */
+    @ApiOperation("获取收款类型")
+    @GetMapping("/type")
+    public ResponseJson<List<ReceiptTypeVo>> getReceiptType() {
+        return receiptService.getReceiptType();
+    }
+
+    /**
+     * 识别收款短信
+     *
+     * @param receiptDto {
+     *                   smsContent          短信类容
+     *                   }
+     */
+    @ApiOperation("识别收款短信")
+    @PostMapping("/read/sms")
+    public ResponseJson<ReceiptVo> readSmsMessage(ReceiptDto receiptDto, @RequestHeader HttpHeaders headers) {
+        String smsContent = receiptDto.getSmsContent();
+        if (StringUtils.hasLength(smsContent)) {
+            return receiptService.readSmsMessage(smsContent, headers);
+        } else {
+            return ResponseJson.error("收款短信不能为空", null);
+        }
+    }
+
+    /**
+     * 保存收款信息
+     * crm:/api/user/receivables/save
+     */
+    @ApiOperation("保存收款信息")
+    @PostMapping("/save")
+    public ResponseJson<ReceiptPo> saveReceipt(ReceiptDto receiptDto, @RequestHeader HttpHeaders headers) {
+        return receiptService.saveReceipt(receiptDto, headers);
+    }
+
+    /**
+     * 操作收款信息(作废,设为返佣/普通/供应商退款/非订单款)
+     * crm:/api/user/receivables/save
+     *
+     * @param receiptOperateDto {
+     *                   id:收款Id
+     *                   receiptType: 1订单款,2非订单款,3返佣款 4订单款或者非订单款(因财务阶段无法区分订单非订单), 5供应商退款
+     *                   noOrderReason: 非订单款说明
+     *                   delFlag: 作废标记: 0否,其余是
+     *                   }
+     */
+    @ApiOperation("操作收款信息(作废,设为返佣/普通/供应商退款/非订单款)")
+    @PostMapping("/operate")
+    public ResponseJson<ReceiptPo> operateReceipt(ReceiptOperateDto receiptOperateDto, @RequestHeader HttpHeaders headers) {
+        if (null == receiptOperateDto.getId() || receiptOperateDto.getId() <= 0) {
+            return ResponseJson.error("收款Id不能为空", null);
+        }
+        return receiptService.operateReceipt(receiptOperateDto, headers);
+    }
+
+    /**
+     * 获取收款列表
+     * crm:/api/user/receivables/list
+     * 返佣款/api/user/rebate/list  或 (receiptType==3:/api/user/receivables/list)
+     */
+    @ApiOperation("获取收款列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(required = false, name = "receiptType", value = "收款款项类型:1订单款,2非订单款,3返佣款 4订单款或者非订单款(因财务阶段无法区分订单非订单), 5供应商退款"),
+            @ApiImplicitParam(required = false, name = "receiptStatus", value = "收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【线上支付成功为审核通过】"),
+            @ApiImplicitParam(required = false, name = "smsContent", value = "收款短信"),
+            @ApiImplicitParam(required = false, name = "startDate", value = "筛选开始时间"),
+            @ApiImplicitParam(required = false, name = "endDate", value = "筛选结束时间"),
+            @ApiImplicitParam(required = false, name = "pageNum", value = "页码"),
+            @ApiImplicitParam(required = false, name = "pageSize", value = "每页数量")
+    })
+    @GetMapping("/list")
+    public ResponseJson<PageInfo<ReceiptVo>> getReceiptList(Integer receiptType, Integer receiptStatus, String smsContent, String startDate, String endDate,
+                                                            @RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
+                                                            @RequestParam(value = "pageSize", defaultValue = "10") int pageSize,
+                                                            @RequestHeader HttpHeaders headers) {
+        return receiptService.getReceiptList(receiptType, receiptStatus, smsContent, startDate, endDate, pageNum, pageSize, headers);
+    }
+
+    /**
+     * 收款详情(订单/非订单)
+     * crm:/api/user/receivables/get
+     */
+    @ApiOperation("收款详情(订单/非订单)")
+    @ApiImplicitParam(required = true, name = "id", value = "款项Id")
+    @GetMapping("/detail")
+    public ResponseJson<ReceiptVo> getReceiptDetail(Integer id, @RequestHeader HttpHeaders headers) {
+        return receiptService.getReceiptDetail(id, headers);
+    }
+
+    /**
+     * 收款详情的款项类型
+     */
+    @ApiOperation("收款详情的款项类型")
+    @ApiImplicitParam(required = true, name = "id", value = "款项Id")
+    @GetMapping("/detail/type")
+    public ResponseJson<Map<String, Integer>> getReceiptDetailType(Integer id, @RequestHeader HttpHeaders headers) {
+        return receiptService.getReceiptDetailType(id, headers);
+    }
+
+    /**
+     * 收款详情(供应商退款)
+     * crm:/api/user/shop/shopRefund
+     */
+    @ApiOperation("收款详情(供应商退款)")
+    @ApiImplicitParam(required = true, name = "id", value = "款项Id")
+    @GetMapping("/refund")
+    public ResponseJson<ReceiptVo> getReceiptRefundDetail(Integer id, @RequestHeader HttpHeaders headers) {
+        return receiptService.getReceiptRefundDetail(id, headers);
+    }
+
+    /**
+     * 收款详情(返佣款)
+     * crm:/api/user/rebate/rebateDetail
+     */
+    @ApiOperation("收款详情(返佣款)")
+    @ApiImplicitParam(required = true, name = "id", value = "款项Id")
+    @GetMapping("/rebate")
+    public ResponseJson<ReceiptVo> getReceiptRebateDetail(Integer id, @RequestHeader HttpHeaders headers) {
+        return receiptService.getReceiptRebateDetail(id, headers);
+    }
+
+    /**
+     * 收款详情-订单列表
+     * crm:/api/user/receivables/getOrderList
+     * crm:/api/user/receivables/getMoneyOrderList
+     */
+    @ApiOperation("收款详情-订单列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(required = false, name = "id", value = "收款ID"),
+            @ApiImplicitParam(required = false, name = "type", value = "类型:0商品订单(默认),1订金订单"),
+            @ApiImplicitParam(required = false, name = "organizeId", value = "组织ID"),
+            @ApiImplicitParam(required = false, name = "orderReceiptStatus", value = "订单收款状态:1待收款,2部分收款,3已收款"),
+            @ApiImplicitParam(required = false, name = "keyword", value = "搜索关键词(客户名称/订单号)"),
+            @ApiImplicitParam(required = false, name = "pageNum", value = "页码"),
+            @ApiImplicitParam(required = false, name = "pageSize", value = "每页数量")
+    })
+    @GetMapping("/detail/orders")
+    public ResponseJson<PageInfo<OrderVo>> getReceiptOrderList(Integer id, Integer type, Integer organizeId, String orderReceiptStatus, String keyword,
+                                                               @RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
+                                                               @RequestParam(value = "pageSize", defaultValue = "10") int pageSize,
+                                                               @RequestHeader HttpHeaders headers) {
+        return receiptService.getReceiptOrderList(id, type, organizeId, orderReceiptStatus, keyword, pageNum, pageSize, headers);
+    }
+
+    /**
+     * 收款详情-订单详情
+     * crm:/api/user/receivables/orderDetail
+     */
+    @ApiOperation("收款详情-订单详情")
+    @ApiImplicitParam(required = false, name = "orderId", value = "订单ID")
+    @GetMapping("/detail/orders/info")
+    public ResponseJson<OrderVo> getReceiptOrderInfo(Integer orderId, @RequestHeader HttpHeaders headers) {
+        return receiptService.getReceiptOrderInfo(orderId, headers);
+    }
+
+    /**
+     * 供应商退款-子订单列表
+     * crm:/api/user/shop/refundShopOrder
+     */
+    @ApiOperation("供应商退款-子订单列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(required = false, name = "id", value = "收款ID"),
+            @ApiImplicitParam(required = false, name = "confirmedType", value = "0待确认,2已确认子订单"),
+            @ApiImplicitParam(required = false, name = "shopName", value = "供应商名称"),
+            @ApiImplicitParam(required = false, name = "keyword", value = "搜索关键词(客户名称/订单号)"),
+            @ApiImplicitParam(required = false, name = "pageNum", value = "页码"),
+            @ApiImplicitParam(required = false, name = "pageSize", value = "每页数量")
+    })
+    @GetMapping("/refund/orders")
+    public ResponseJson<PageInfo<ShopOrderVo>> getRefundShopOrderList(Integer id, Integer confirmedType, String shopName, String keyword,
+                                                                      @RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
+                                                                      @RequestParam(value = "pageSize", defaultValue = "10") int pageSize,
+                                                                      @RequestHeader HttpHeaders headers) {
+        return receiptService.getRefundShopOrderList(id, confirmedType, shopName, keyword, pageNum, pageSize, headers);
+    }
+
+    /**
+     * 供应商退款-订单详情
+     * crm:/api/user/rebate/orderInfo
+     */
+    @ApiOperation("供应商退款-订单详情")
+    @ApiImplicitParam(required = false, name = "orderId", value = "订单ID")
+    @GetMapping("/refund/orders/info")
+    public ResponseJson<OrderVo> getRefundShopOrderInfo(Integer orderId, @RequestHeader HttpHeaders headers) {
+        return receiptService.getRefundShopOrderInfo(orderId, headers);
+    }
+
+    /**
+     * 返佣款-子订单列表
+     * crm:/api/user/rebate/rebateShopOrder
+     */
+    @ApiOperation("返佣款-子订单列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(required = false, name = "id", value = "返佣收款ID"),
+            @ApiImplicitParam(required = false, name = "shopName", value = "供应商名称"),
+            @ApiImplicitParam(required = false, name = "pageNum", value = "页码"),
+            @ApiImplicitParam(required = false, name = "pageSize", value = "每页数量")
+    })
+    @GetMapping("/rebate/orders")
+    public ResponseJson<PageInfo<ShopOrderVo>> getRebateShopOrderList(Integer id, String shopName,
+                                                                      @RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
+                                                                      @RequestParam(value = "pageSize", defaultValue = "10") int pageSize,
+                                                                      @RequestHeader HttpHeaders headers) {
+        return receiptService.getRebateShopOrderList(id, shopName, pageNum, pageSize, headers);
+    }
+
+    /**
+     * 确认收款-关联收款信息
+     * crm:/api/user/receivables/confirmReceipt
+     *
+     * @param receiptOperateDto {
+     *                            id: 收款Id
+     *                            orderIds: 关联订单IDS
+     *                            confirmType: 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联
+     *                            noOrderReason: 非订单款说明
+     *                            }
+     */
+    @ApiOperation("确认收款-关联收款信息")
+    @PostMapping("/confirm")
+    public ResponseJson<Void> receiptAssociate(ReceiptOperateDto receiptOperateDto, @RequestHeader HttpHeaders headers) {
+        if (null == receiptOperateDto.getId() || receiptOperateDto.getId() <= 0) {
+            return ResponseJson.error("收款Id不能为空", null);
+        }
+        if (!StringUtils.hasLength(receiptOperateDto.getOrderIds())) {
+            return ResponseJson.error("关联订单IDS不能为空", null);
+        }
+        return receiptService.receiptAssociate(receiptOperateDto, headers);
+    }
+
+    /**
+     * 确认收款-关联供应商退款
+     * crm:/api/user/shop/relation
+     *
+     * @param receiptOperateDto {
+     *                            id:          收款Id
+     *                            shopOrderId: 关联供应商退款子订单ID
+     *                            }
+     */
+    @ApiOperation("确认收款-关联供应商退款")
+    @PostMapping("/confirm/refund")
+    public ResponseJson<Void> receiptAssociateRefund(ReceiptOperateDto receiptOperateDto, @RequestHeader HttpHeaders headers) {
+        if (null == receiptOperateDto.getId() || receiptOperateDto.getId() <= 0) {
+            return ResponseJson.error("收款Id不能为空", null);
+        }
+        if (null == receiptOperateDto.getShopOrderId() || receiptOperateDto.getShopOrderId() <= 0) {
+            return ResponseJson.error("关联供应商退款子订单ID不能为空", null);
+        }
+        return receiptService.receiptAssociateRefund(receiptOperateDto, headers);
+    }
+
+    /**
+     * 确认收款-关联返佣款
+     * crm:/api/user/rebate/relation
+     *
+     * @param receiptOperateDto {
+     *                            id:           收款Id
+     *                            shopOrderIds: 关联返佣子订单IDs
+     *                            rebateRemarks:关联返佣备注
+     *                            }
+     */
+    @ApiOperation("确认收款-关联返佣款")
+    @PostMapping("/confirm/rebate")
+    public ResponseJson<Void> receiptAssociateRebate(ReceiptOperateDto receiptOperateDto, @RequestHeader HttpHeaders headers) {
+        if (null == receiptOperateDto.getId() || receiptOperateDto.getId() <= 0) {
+            return ResponseJson.error("收款Id不能为空", null);
+        }
+        if (!StringUtils.hasLength(receiptOperateDto.getShopOrderIds())) {
+            return ResponseJson.error("关联返佣子订单IDs不能为空", null);
+        }
+        return receiptService.receiptAssociateRebate(receiptOperateDto, headers);
+    }
+
+    /**
+     * 确认收款-关联口头佣金
+     * crm:/api/user/rebate/verballyReceipt
+     *
+     * @param receiptOperateDto {
+     *                            verbalAmount: 口头返佣佣金
+     *                            shopOrderId: 关联返佣子订单ID
+     *                            rebateRemarks:关联返佣备注
+     *                            }
+     */
+    @ApiOperation("确认收款-关联口头佣金")
+    @PostMapping("/confirm/rebate/verbal")
+    public ResponseJson<Integer> associateVerbalRebate(ReceiptOperateDto receiptOperateDto, @RequestHeader HttpHeaders headers) {
+        if (null == receiptOperateDto.getVerbalAmount()) {
+            return ResponseJson.error("口头返佣佣金参数异常", null);
+        }
+        if (null == receiptOperateDto.getShopOrderId()) {
+            return ResponseJson.error("关联返佣子订单ID不能为空", null);
+        }
+        return receiptService.associateVerbalRebate(receiptOperateDto, headers);
+    }
+
+    /**
+     * 审核收款信息
+     * crm:/api/user/receivables/checkReceipt
+     *
+     * @param receiptOperateDto {
+     *                          id            收款Id
+     *                          receiptStatus 收款状态
+     *                          reviewReason  审核不通过原因
+     * }
+     */
+    @ApiOperation("审核收款信息")
+    @PostMapping("/audit")
+    public ResponseJson<Void> receiptAudit(ReceiptOperateDto receiptOperateDto, @RequestHeader HttpHeaders headers) {
+        if (null == receiptOperateDto.getId() || receiptOperateDto.getId() <= 0) {
+            return ResponseJson.error("收款Id不能为空", null);
+        }
+        if (null == receiptOperateDto.getReceiptStatus() || receiptOperateDto.getReceiptStatus() <= 0) {
+            return ResponseJson.error("收款状态不能为空", null);
+        }
+        return receiptService.receiptAudit(receiptOperateDto.getId(), receiptOperateDto.getReceiptStatus(), receiptOperateDto.getReviewReason(), headers);
+    }
+
+}
+

+ 8 - 0
src/main/java/com/caimei365/order/mapper/BaseMapper.java

@@ -161,6 +161,14 @@ public interface BaseMapper {
      * @param userId 机构用户Id
      */
     void updateUserBeans(Integer userId, int userBeans);
+    /**
+     * 抵扣后更新用户余额
+     *
+     * @param userMoney     账户余额
+     * @param ableUserMoney 账户实际可用余额
+     * @param userId        机构用户Id
+     */
+    void updateUserMoney(Double userMoney, Double ableUserMoney, Integer userId);
     /**
      * 保存余额到余额收支记录
      */

+ 5 - 0
src/main/java/com/caimei365/order/mapper/OrderCommonMapper.java

@@ -107,6 +107,11 @@ public interface OrderCommonMapper {
      * @param orderId 订单Id
      */
     OrderVo getOrderByOrderId(Integer orderId);
+    /**
+     * 根据子订单Id获取订单
+     * @param shopOrderId 订单Id
+     */
+    OrderVo getOrderByShopOrderId(Integer shopOrderId);
     /**
      * 根据订单Id获取子订单列表
      */

+ 213 - 0
src/main/java/com/caimei365/order/mapper/ReceiptMapper.java

@@ -0,0 +1,213 @@
+package com.caimei365.order.mapper;
+
+import com.caimei365.order.model.bo.ReceiptParamsBo;
+import com.caimei365.order.model.po.*;
+import com.caimei365.order.model.vo.*;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/11
+ */
+@Mapper
+public interface ReceiptMapper {
+    /**
+     * 保存收款记录
+     */
+    void insertReceipt(ReceiptPo receiptPo);
+    /**
+     * 更新收款记录
+     */
+    void updateReceipt(ReceiptPo receiptPo);
+    /**
+     * 根据openid获取收款用户
+     */
+    ReceiptUserVo getReceiptUserByOpenid(String openId);
+    /**
+     * 根据收款用户Id获取姓名
+     */
+    String getReceiptUserNameById(Integer id);
+    /**
+     * 根据mobile获取收款用户
+     */
+    ReceiptUserVo getReceiptUserByMobile(String mobile);
+    /**
+     * 根据mobile更新openid
+     */
+    void updateOpenidByMobile(String openid, String unionId, String mobile);
+    /**
+     * 获取用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+     */
+    List<Integer> getPermissionsByUserId(Integer userId);
+    /**
+     * 获取收款类型列表
+     */
+    List<ReceiptTypeVo> getReceiptType();
+    /**
+     * 获取收款类型
+     */
+    ReceiptTypeVo getReceiptTypeById(Integer id);
+    String getReceiptTypeStrById(Integer id);
+    /**
+     * 短信内容Md5加密串 获取收款记录
+     */
+    List<ReceiptVo> getReceiptBySmsMd5Code(String smsMd5Code);
+    /**
+     * 获取收款类型
+     */
+    Integer getReceivablesTypeId(String type);
+    /**
+     * 获取订单已支付金额(待审核状态的收款也算)
+     */
+    Double getPaidAmountByOrderId(Integer orderId);
+    /**
+     * 获取订单待审核金额
+     */
+    Double getUncheckAmount(Integer orderId);
+    /**
+     * 获取订单已退金额
+     */
+    Double getReturnedAmount(Integer orderId);
+    /**
+     * 获取主订单下面的所有的确认收货商品数量
+     */
+    Integer getReceiptNumByOrderId(Integer orderId);
+    /**
+     * 获取该主订单下面所有已完成状态退货退款商品数量
+     */
+    Integer getReturnAndCancelNum(Integer orderId);
+    /**
+     * 获取协销Id
+     */
+    Integer getServiceProviderId(String unionId);
+    /**
+     * 获取协销下的机构userIds
+     */
+    List<Integer> getClubUserIdBySpId(Integer spId);
+    /**
+     * 获取收款列表
+     */
+    List<ReceiptVo> getReceiptList(ReceiptParamsBo receiptParamsBo);
+    /**
+     * 获取收款详情
+     */
+    ReceiptVo getReceiptDetail(Integer id);
+    /**
+     * 获取收款关联订单
+     */
+    List<OrderVo> getReceiptOrders(Integer receiptId);
+    /**
+     * 获取收款商品订单列表
+     */
+    List<OrderVo> getOrderListByParams(ReceiptParamsBo receiptParamsBo);
+    List<OrderVo> getOrderListByOrderIds(@Param("orderIdS") String[] orderIdS);
+    /**
+     * 存在退款中的记录
+     */
+    Integer countHaveReturning(Integer orderId);
+    /**
+     * 存在收款中的记录
+     */
+    Integer countHaveReceipting(Integer orderId);
+    /**
+     * 线上支付收款Id
+     */
+    Integer getOnlineReceiptId(Integer orderId);
+    /**
+     * 获取已退款金额
+     */
+    List<ReturnedPurchaseVo> getRefundFeeByOrderId(Integer orderId);
+    /**
+     * 已关联子订单
+     */
+    List<ShopOrderVo> getShopOrderListByReceiptId(Integer receiptId);
+    /**
+     * 搜索子订单进行关联
+     */
+    List<ShopOrderVo> getRefundShopOrders(Integer confirmedType, String shopName, String userName, Integer orderId);
+    /**
+     * 返佣款-子订单列表
+     */
+    List<ShopOrderVo> getRebateShopOrders(String shopName);
+    /**
+     * 获取子订单信息
+     */
+    ShopOrderVo getShopOrderById(Integer shopOrderId);
+    /**
+     * 返佣订单付款总金额
+     */
+    double countAllPayAssociateAmount(Integer orderId);
+    /**
+     * 需要支付金额
+     */
+    double countNeedPayAmount(Integer orderId);
+    /**
+     * 删除之前与这个收款所有的关系
+     */
+    void deleteReceiptRelation(Integer receiptId);
+    /**
+     * 子订单付款类型列表
+     */
+    List<Integer> getRebatePayTypeList(Integer shopOrderId);
+    /**
+     * 根据用户类型获取用户openid列表
+     */
+    List<String> getOpenidListByPermission(List<Integer> permissions);
+    /**
+     * 统计收款金额
+     */
+    double countAssociateAmountById(Integer receiptId);
+    /**
+     * 总退款金额
+     */
+    double getRefundShopAmount(Integer shopOrderId);
+    /**
+     * 保存供应商退款
+     */
+    void insertRefundShop(RefundShopPo refundShopPo);
+    /**
+     * 保存供应商退款记录
+     */
+    void insertRefundShopRecord(RefundShopRecordPo refundShopRecord);
+    /**
+     * 供应商Id列表
+     */
+    List<Integer> getShopIdList(@Param("shopOrderIdArr") String[] shopOrderIdArr);
+    /**
+     * 查询这个子订单的主订单下面的所有子订单ID
+     */
+    List<Integer> getSubShopOrderList(String shopOrderId);
+    /**
+     * 查询当前子订单列表中已经返佣了的子订单
+     */
+    List<Integer> getSubShopOrderRebate(@Param("shopOrderIdList") List<Integer> shopOrderIdList);
+    /**
+     * 修改(付款供应商)付款状态
+     * @param shopOrderId 子订单Id
+     * @param payStatus 付款状态
+     */
+    void updateShopOrderPayStatus(String shopOrderId, Integer payStatus);
+    /**
+     * 收款项和订单关系
+     */
+    List<OrderReceiptRelationPo> getOrderReceiptRelationList(Integer receiptId);
+    /**
+     * 查询待审核数量
+     */
+    int getPendingAuditCount(Integer orderId);
+
+    /**
+     * 添加 返佣欠款记录
+     */
+    void insertRebateRecord(RebateRecordPo crr);
+
+    /**
+     * 更新供应商欠款金额
+     */
+    void updateShopRebateAmount(Integer shopId, Double amount);
+}

+ 67 - 0
src/main/java/com/caimei365/order/model/bo/ReceiptParamsBo.java

@@ -0,0 +1,67 @@
+package com.caimei365.order.model.bo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/16
+ */
+@Data
+public class ReceiptParamsBo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 款项id
+     */
+    private Integer id;
+    /**
+     * 收款款项类型:1订单款,2非订单款,3返佣款
+     */
+    private Integer receiptType;
+    /**
+     * 短信内容Md5加密串
+     */
+    private String smsMd5Code;
+    /**
+     * 用户id集合
+     */
+    private List<Integer> userIds;
+    /**
+     * 收款状态列表:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【线上支付成功为审核通过】
+     */
+    private String[] receiptStatusArr;
+    /**
+     * 筛选开始时间
+     */
+    private String startDate;
+    /**
+     * 筛选结束时间
+     */
+    private String endDate;
+    /**
+     * 订单Id
+     */
+    private Integer orderId;
+    private Integer excludeOrderId;
+    /**
+     * 订单类型:0商品订单(默认),1订金订单
+     */
+    private Integer orderType;
+    /**
+     * 订单状态集合
+     */
+    private List<Integer> orderStatusList;
+    /**
+     * 客户名称
+     */
+    private String userName;
+    /**
+     * 组织Id
+     */
+    private Integer organizeId;
+
+}

+ 52 - 0
src/main/java/com/caimei365/order/model/dto/ReceiptAssociateDto.java

@@ -0,0 +1,52 @@
+package com.caimei365.order.model.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/21
+ */
+@Data
+public class ReceiptAssociateDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 收款Id
+     */
+    @ApiModelProperty("收款Id")
+    private Integer id;
+    /**
+     * 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联
+     */
+    @ApiModelProperty("确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联")
+    private Integer confirmType;
+    /**
+     * 关联订单IDS
+     */
+    @ApiModelProperty("关联订单IDS")
+    private String orderIds;
+    /**
+     * 非订单款说明(适用协销确认的时候区分订单和非订单款)
+     */
+    @ApiModelProperty("非订单款说明(适用协销确认的时候区分订单和非订单款)")
+    private String noOrderReason;
+    /**
+     * 关联供应商退款子订单ID
+     */
+    @ApiModelProperty("关联供应商退款子订单ID")
+    private Integer shopOrderId;
+    /**
+     * 关联供应商退款子订单ID
+     */
+    @ApiModelProperty("关联返佣子订单IDs")
+    private String shopOrderIds;
+    /**
+     * 返佣关联备注
+     */
+    @ApiModelProperty("关联返佣备注")
+    private String rebateRemarks;
+}

+ 46 - 0
src/main/java/com/caimei365/order/model/dto/ReceiptAuthDto.java

@@ -0,0 +1,46 @@
+package com.caimei365.order.model.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/11
+ */
+@ApiModel("线下收款微信授权")
+@Data
+public class ReceiptAuthDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 微信授权code
+     */
+    @NotNull
+    @ApiModelProperty("微信授权code")
+    private String code;
+    /**
+     * 微信加密数据
+     */
+    @ApiModelProperty("微信加密数据")
+    private String encryptedData;
+    /**
+     * 加密算法的初始向量
+     */
+    @ApiModelProperty("加密算法的初始向量")
+    private String iv;
+    /**
+     * 授权人手机号
+     */
+    @ApiModelProperty("授权人手机号")
+    private String mobile;
+    /**
+     * 密码
+     */
+    @ApiModelProperty("密码")
+    private String password;
+}

+ 79 - 0
src/main/java/com/caimei365/order/model/dto/ReceiptDto.java

@@ -0,0 +1,79 @@
+package com.caimei365.order.model.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/11
+ */
+@ApiModel("款项")
+@Data
+public class ReceiptDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 收款Id
+     */
+    @ApiModelProperty("收款Id")
+    private Integer id;
+    /**
+     * 收款短信
+     */
+    @ApiModelProperty("收款短信")
+    private String smsContent;
+    /**
+     * 短信内容Md5加密串(适用于二次短信匹配查询)
+     */
+    @ApiModelProperty("短信内容Md5加密串")
+    private String smsMd5Code;
+    /**
+     * 用户付款方式:1线上,2线下,3余额抵扣
+     */
+    @ApiModelProperty("付款方式")
+    private Integer 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网银
+     */
+    @ApiModelProperty("付款类型")
+    private Integer payType;
+    /**
+     * 收款款项类型
+     */
+    @ApiModelProperty("收款款项类型:1订单款,2非订单款,3返佣款 4订单款或者非订单款(因财务阶段无法区分订单非订单), 5供应商退款")
+    private Integer receiptType;
+    /**
+     * 收款状态
+     */
+    @ApiModelProperty("收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【线上支付成功为审核通过】")
+    private Integer receiptStatus;
+    /**
+     * 收款时间
+     */
+    @ApiModelProperty("收款时间")
+    private String receiptDate;
+    /**
+     * 收款金额(线上一次性付款和支付金额一致)
+     */
+    @ApiModelProperty("收款金额")
+    private Double receiptAmount;
+    /**
+     * 手续费
+     */
+    @ApiModelProperty("手续费")
+    private Double handlingFee;
+    /**
+     * 订单标识
+     */
+    @ApiModelProperty("订单标识")
+    private Integer orderFlag;
+    /**
+     * 非订单款说明(适用协销确认的时候区分订单和非订单款)
+     */
+    @ApiModelProperty("非订单款说明")
+    private String noOrderReason;
+}

+ 79 - 0
src/main/java/com/caimei365/order/model/dto/ReceiptOperateDto.java

@@ -0,0 +1,79 @@
+package com.caimei365.order.model.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/25
+ */
+@ApiModel("款项操作")
+@Data
+public class ReceiptOperateDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 收款Id
+     */
+    @ApiModelProperty("收款Id")
+    private Integer id;
+    /**
+     * 作废标记
+     */
+    @ApiModelProperty("作废标记: 0否,其余是")
+    private Integer delFlag;
+    /**
+     * 非订单款说明(适用协销确认的时候区分订单和非订单款)
+     */
+    @ApiModelProperty("非订单款说明")
+    private String noOrderReason;
+    /**
+     * 收款款项类型
+     */
+    @ApiModelProperty("收款款项类型:1订单款,2非订单款,3返佣款 4订单款或者非订单款(因财务阶段无法区分订单非订单), 5供应商退款")
+    private Integer receiptType;
+    /**
+     * 收款状态
+     */
+    @ApiModelProperty("收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【线上支付成功为审核通过】")
+    private Integer receiptStatus;
+    /**
+     * 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联
+     */
+    @ApiModelProperty("确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联")
+    private Integer confirmType;
+    /**
+     * 关联订单IDS
+     */
+    @ApiModelProperty("关联订单IDS")
+    private String orderIds;
+    /**
+     * 关联供应商退款子订单ID
+     */
+    @ApiModelProperty("关联供应商退款子订单ID")
+    private Integer shopOrderId;
+    /**
+     * 关联供应商退款子订单ID
+     */
+    @ApiModelProperty("关联返佣子订单IDs")
+    private String shopOrderIds;
+    /**
+     * 返佣关联备注
+     */
+    @ApiModelProperty("关联返佣备注")
+    private String rebateRemarks;
+    /**
+     * 审核不通过原因
+     */
+    @ApiModelProperty("审核不通过原因")
+    private String reviewReason;
+    /**
+     * 口头返佣佣金
+     */
+    @ApiModelProperty("口头返佣佣金")
+    private Double verbalAmount;
+}

+ 14 - 1
src/main/java/com/caimei365/order/model/enums/OrderStatus.java

@@ -23,7 +23,11 @@ public enum OrderStatus {
     TRANSACTION_COMPLETED(4, "交易完成"),
     ORDER_COMPLETED(5, "订单完成"),
     FULL_RETURNED(7, "交易全退"),
-    CLOSED(6, "已关闭");
+    CLOSED(6, "已关闭"),
+    // ---数据库不存在的状态
+    WAIT_PAY(1,"待支付"),
+    TRANSACTION(2,"交易中"),
+    REFUND_RETURN(8,"退货退款");
 
     private final int code;
     private final String desc;
@@ -41,4 +45,13 @@ public enum OrderStatus {
         this.desc = desc;
     }
 
+    public static String getOrderStatusText(int code){
+        for(OrderStatus type : OrderStatus.values()){
+            if(type.getCode() == code){
+                return type.desc;
+            }
+        }
+        return "";
+    }
+
 }

+ 47 - 0
src/main/java/com/caimei365/order/model/enums/ReceiptStatus.java

@@ -0,0 +1,47 @@
+package com.caimei365.order.model.enums;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/17
+ */
+public enum ReceiptStatus {
+    /**
+     * 收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【订单款项状态:12345】【非订单款项状态:125】【返佣款状态:125】【线上支付成功为审核通过】
+     */
+    DAIQUEREN(1, "待确认"),
+    YIQUEREN(2, "已确认(待审核)"),
+    SHENGHETONGGUO(3, "审核通过"),
+    SHENGHEWEITONGGUO(4, "审核未通过"),
+    SHOUKUANCHEXIAO(5, "收款撤销");
+
+    private final int code;
+    private final String desc;
+
+    public static String getTypeDesc(Integer code, Integer receiptType) {
+        for (ReceiptStatus type : ReceiptStatus.values()) {
+            if (type.getCode() == code) {
+                boolean flag = code == YIQUEREN.getCode() && (receiptType == ReceiptType.FEIDINGDAN.getCode() || receiptType == ReceiptType.FANYONG.getCode() || receiptType == ReceiptType.SHOPREFUND.getCode());
+                if (flag) {
+                    return "已确认";
+                }
+                return type.desc;
+            }
+        }
+        return "";
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    ReceiptStatus(int code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+}

+ 42 - 0
src/main/java/com/caimei365/order/model/enums/ReceiptType.java

@@ -0,0 +1,42 @@
+package com.caimei365.order.model.enums;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/17
+ */
+public enum ReceiptType {
+    /**
+     * 收款款项类型:1订单款,2非订单款,3返佣款
+     */
+    DINGDAN(1, "订单款"),
+    FEIDINGDAN(2, "非订单款"),
+    FANYONG(3, "返佣款"),
+    SHOPREFUND(5, "供应商退款");
+
+    private final int code;
+    private final String desc;
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    ReceiptType(int code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public static String getTypeDesc(int code) {
+        for (ReceiptType type : ReceiptType.values()) {
+            if (type.getCode() == code) {
+                return type.desc;
+            }
+        }
+        return "";
+    }
+}

+ 3 - 1
src/main/java/com/caimei365/order/model/enums/ReceivablesType.java

@@ -25,7 +25,9 @@ public enum ReceivablesType {
     PCWX(13, "微信支付"),
     ZHIFUBAO(14, "支付宝"),
     MINIWX(15, "微信支付"),
-    YUEDIKO(16, "余额抵扣");
+    YUEDIKO(16, "余额抵扣"),
+    JIANSHE_1(18,"建设银行-1854"),
+    JIANSHE_2(19,"建设银行-6256");
 
     private final int code;
     private final String desc;

+ 4 - 0
src/main/java/com/caimei365/order/model/po/BalanceRecordPo.java

@@ -42,6 +42,10 @@ public class BalanceRecordPo implements Serializable {
      * 主订单ID(适用余额类型1,3[类型为3多次退款存在相同记录ID则需通过退款ID唯一区分])
      */
     private Integer orderId;
+    /**
+     * 收款ID(适用余额类型2)
+     */
+    private Integer receiptId;
     /**
      * 备注
      */

+ 4 - 0
src/main/java/com/caimei365/order/model/po/OrderReceiptRelationPo.java

@@ -29,6 +29,10 @@ public class OrderReceiptRelationPo implements Serializable {
      * 关联金额:1普通收款(线下):收款具体对该应母订单的收金额、2线上支付:付款金额就等于该金额、3返佣收款:默认为0
      */
     private Double associateAmount;
+    /**
+     * 关联方式: 1手动 2自动
+     */
+    private Integer associationType;
     /**
      * 订单Id(relationType值为1是为子订单ID,为2时为主订单ID)
      */

+ 41 - 0
src/main/java/com/caimei365/order/model/po/RebateRecordPo.java

@@ -0,0 +1,41 @@
+package com.caimei365.order.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/28
+ */
+@Data
+public class RebateRecordPo implements Serializable {
+    private static final long serialVersionUId = 1L;
+    private Integer id;
+    /**
+     * 记录类型:1供应商欠款录入(口头返佣)、2应付供应商抵扣(不打款欠款抵扣)(付款抵扣), 3供应商退款给采美退款到余额
+     */
+    private Integer type;
+    /**
+     * 供应商ID
+     */
+    private Integer shopId;
+    /**
+     * 识别款项Id(对应cm_discern_receipt表)供应商欠款录入(口头返佣)的时候有值
+     */
+    private Integer receiptId;
+    /**
+     * 子订单ID(多个子订单使用,分隔
+     */
+    private String shopOrderId;
+    /**
+     * 金额(可能是欠款录入的口头返佣,可能是付款抵扣金额,具体看type值)
+     */
+    private Double rebateAmount;
+    /**
+     * 操作时间
+     */
+    private String operateTime;
+}

+ 126 - 0
src/main/java/com/caimei365/order/model/po/ReceiptPo.java

@@ -0,0 +1,126 @@
+package com.caimei365.order.model.po;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/15
+ */
+@Data
+public class ReceiptPo implements Serializable {
+    private static final long serialVersionUId = 1L;
+    /**
+     * 收款Id
+     */
+    private Integer id;
+    /**
+     * 收款短信
+     */
+    private String smsContent;
+    /**
+     * 短信内容Md5加密串(适用于二次短信匹配查询)
+     */
+    private String smsMd5Code;
+    /**
+     * 用户付款方式:1线上,2线下,3余额抵扣
+     */
+    private Integer 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 Integer payType;
+    /**
+     * 收款款项类型:1订单款,2非订单款,3返佣款 4订单款或者非订单款(因财务阶段无法区分订单非订单), 5供应商退款
+     */
+    private Integer receiptType;
+    /**
+     * 收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【线上支付成功为审核通过】
+     */
+    private Integer receiptStatus;
+    /**
+     * 收款时间
+     */
+    private String receiptDate;
+    /**
+     * 收款金额(线上一次性付款和支付金额一致)
+     */
+    private Double receiptAmount;
+    /**
+     * 手续费
+     */
+    private Double handlingFee;
+    /**
+     * 订单标识
+     */
+    private Integer orderFlag;
+    /**
+     * 作废标记
+     */
+    private Integer delFlag;
+    /**
+     * 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联
+     */
+    private Integer confirmType;
+    /**
+     * 确认时间
+     */
+    private String confirmDate;
+    /**
+     * 确认人权限Id(对应cm_receipt_user表)
+     */
+    private Integer confirmUserPermissionId;
+    /**
+     * 收款人权限Id(对应cm_receipt_user表)
+     */
+    private Integer receiptUserPermissionId;
+    /**
+     * 审核人权限Id(对应cm_receipt_user表)
+     */
+    private Integer reviewUserPermissionId;
+    /**
+     * 撤销人权限Id(对应cm_receipt_user表)
+     */
+    private Integer cancelUserPermissionId;
+    /**
+     * 审核时间
+     */
+    private String reviewDate;
+    /**
+     * 撤销时间
+     */
+    private String cancelDate;
+    /**
+     * 撤销原因
+     */
+    private String cancelReason;
+    /**
+     * 更新操作时间
+     */
+    private String updateDate;
+    /**
+     * 自动审核收款标记(0手动分享去确认审核,1收款直接确认并审核[适用星范带有正确订单标识的收款])
+     */
+    private Integer autoAuditFlag;
+    /**
+     * 当前识别短信是否已经识别存在系统。0新短信可以识别,1已经存在识别短信。
+     */
+    private Integer smsContentPresenceFlag;
+    /**
+     * 非订单款说明(适用协销确认的时候区分订单和非订单款)
+     */
+    private String noOrderReason;
+    /**
+     * 审核不通过原因
+     */
+    private String reviewReason;
+    /**
+     * 返佣关联备注
+     */
+    private String rebateRemarks;
+
+}

+ 52 - 0
src/main/java/com/caimei365/order/model/po/RefundShopPo.java

@@ -0,0 +1,52 @@
+package com.caimei365.order.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/24
+ */
+@Data
+public class RefundShopPo implements Serializable {
+    private static final long serialVersionUId = 1L;
+    /**
+     * id
+     */
+    private Integer id;
+    /**
+     * 供应商Id
+     */
+    private Integer shopId;
+    /**
+     * 操作人ID
+     */
+    private Integer operator;
+    /**
+     * 操作时间
+     */
+    private String operateTime;
+    /**
+     * 退款金额(线下转账)
+     */
+    private Double refundAmount;
+    /**
+     * 退款余额金额(余额账号)
+     */
+    private Double refundBalanceAmount;
+    /**
+     * 退款方式:1线下转账,2余额账号,3线下和余额同时退,4收款CRM(供应商退款)
+     */
+    private Integer refundWay;
+    /**
+     * 线下转账具体银行
+     */
+    private Integer refundType;
+    /**
+     * 备注
+     */
+    private String remark;
+}

+ 49 - 0
src/main/java/com/caimei365/order/model/po/RefundShopRecordPo.java

@@ -0,0 +1,49 @@
+package com.caimei365.order.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/24
+ */
+@Data
+public class RefundShopRecordPo implements Serializable {
+    private static final long serialVersionUId = 1L;
+    /**
+     * id
+     */
+    private Integer id;
+    /**
+     * 退款表ID
+     */
+    private Integer refundShopId;
+    /**
+     * 供应商Id
+     */
+    private Integer shopId;
+    /**
+     * 子订单ID
+     */
+    private Integer shopOrderId;
+    /**
+     * 子订单编号
+     */
+    private String shopOrderNo;
+    /**
+     * 退款金额(线下转账)
+     */
+    private Double refundAmount;
+    /**
+     * 线下转账具体银行
+     */
+    private Integer refundType;
+    /**
+     * 付款时间
+     */
+    private String refundTime;
+
+}

+ 48 - 1
src/main/java/com/caimei365/order/model/vo/OrderVo.java

@@ -38,6 +38,10 @@ public class OrderVo implements Serializable {
      * 机构Id
      */
     private Integer clubId;
+    /**
+     * 组织Id
+     */
+    private Integer organizeId;
     /**
      * 用户身份:1协销 2会员机构 3供应商 4普通机构
      */
@@ -267,5 +271,48 @@ public class OrderVo implements Serializable {
      * 是否有商品发票属性的限制,为true时只能线下支付
      */
     private boolean invoiceStatus = false;
-
+    /**
+     * 客户名称
+     */
+    private String userName;
+    /**
+     * 收款详情是否可以查看
+     */
+    private Boolean authority = false;
+    /**
+     * 存在退款中的记录 (0:否,1:是)
+     */
+    private String haveReturning;
+    /**
+     * 存在收款中的记录 (0:否,1:是)
+     */
+    private String haveReceipting;
+    /**
+     * 剩余应收金额
+     */
+    private Double surplusAmount;
+    /**
+     * 已支付金额
+     */
+    private Double paidAmount;
+    /**
+     * 待审金额
+     */
+    private Double unCheckAmount;
+    /**
+     * 退货退款价值(抵扣经理折扣后)
+     */
+    private Double returnValue;
+    /**
+     * 退款总额(给买家)
+     */
+    private Double refundFee;
+    /**
+     * 订单状态备注
+     */
+    private String statusName;
+    /**
+     * 总税费
+     */
+    private Double totalAddedValueTax;
 }

+ 27 - 0
src/main/java/com/caimei365/order/model/vo/ReceiptTypeVo.java

@@ -0,0 +1,27 @@
+package com.caimei365.order.model.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/16
+ */
+@Data
+public class ReceiptTypeVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 收款类型Id
+     */
+    @ApiModelProperty("收款类型Id")
+    private Integer id;
+    /**
+     * 收款类型
+     */
+    @ApiModelProperty("收款类型名称")
+    private String type;
+}

+ 63 - 0
src/main/java/com/caimei365/order/model/vo/ReceiptUserVo.java

@@ -0,0 +1,63 @@
+package com.caimei365.order.model.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/11
+ */
+@Data
+public class ReceiptUserVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 收款用户Id
+     */
+    @ApiModelProperty("收款用户Id")
+    private Integer id;
+    /**
+     * 用户类型:1协销人员,2客服,3财务,4超级管理员
+     */
+    @ApiModelProperty("用户类型:1协销人员,2客服,3财务,4超级管理员")
+    private Integer userType;
+    /**
+     * 名称
+     */
+    @ApiModelProperty("名称")
+    private String name;
+    /**
+     * 授权人手机号
+     */
+    @ApiModelProperty("授权人手机号")
+    private String mobile;
+    /**
+     * 微信openid
+     */
+    @ApiModelProperty("微信openid")
+    private String openid;
+    /**
+     * 密码
+     */
+    @ApiModelProperty("密码")
+    private String password;
+    /**
+     * 用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+     */
+    @ApiModelProperty("用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核")
+    private Integer permission;
+    /**
+     * 用户权限列表: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+     */
+    @ApiModelProperty("用户权限列表: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核")
+    private List<Integer> permissions;
+    /**
+     * 微信unionId
+     */
+    @ApiModelProperty("微信unionId")
+    private String unionId;
+}

+ 149 - 0
src/main/java/com/caimei365/order/model/vo/ReceiptVo.java

@@ -0,0 +1,149 @@
+package com.caimei365.order.model.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/15
+ */
+@Data
+public class ReceiptVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 款项识别表id
+     */
+    private Integer id;
+    /**
+     * 用户付款方式:1线上,2线下,3余额抵扣
+     */
+    private Integer 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 Integer payType;
+    private String payTypeText;
+    /**
+     * 收款款项类型:1订单款,2非订单款,3返佣款
+     */
+    private Integer receiptType;
+    private String receiptTypeText;
+    /**
+     * 收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【订单款项状态:12345】【非订单款项状态:125】【返佣款状态:125】【线上支付成功为审核通过】
+     */
+    private Integer receiptStatus;
+    private String receiptStatusText;
+    /**
+     * 收款金额(线上一次性付款和支付金额一致)
+     */
+    private Double receiptAmount;
+    /**
+     * 收款时间
+     */
+    private String receiptDate;
+    /**
+     * 确认时间
+     */
+    private String confirmDate;
+    /**
+     * 审核时间
+     */
+    private String reviewDate;
+    /**
+     * 撤销时间
+     */
+    private String cancelDate;
+    /**
+     * 撤销原因
+     */
+    private String cancelReason;
+    /**
+     * 审核不通过原因
+     */
+    private String reviewReason;
+    /**
+     * 非订单款说明
+     */
+    private String noOrderReason;
+    /**
+     * 单次收款金额
+     */
+    private Double associateAmount;
+    /**
+     * 短信内容
+     */
+    private String smsContent;
+    /**
+     * 短信内容Md5加密串(适用于二次短信匹配查询)
+     */
+    private String smsMd5Code;
+    /**
+     * 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联
+     */
+    private Integer confirmType;
+    /**
+     * 是否存在手续费
+     */
+    private Boolean haveFee;
+    /**
+     * 手续费
+     */
+    private Double handlingFee;
+    /**
+     * 订单标识
+     */
+    private String orderFlag;
+    /**
+     * 当前识别短信是否已经识别存在系统。0新短信可以识别,1已经存在识别短信。
+     */
+    private String smsContentPresenceFlag;
+    /**
+     * 自动审核收款标记(0手动分享去确认审核,1收款直接确认并审核[适用星范带有正确订单标识的收款])
+     */
+    private String autoAuditFlag;
+    /**
+     * 订单组织ID
+     */
+    private Integer organizeId;
+    /**
+     * 提示信息
+     */
+    private String tipMsg;
+    /**
+     * 订单机构用户ID
+     */
+    private Integer userId;
+    /**
+     * 订单机构客户名称
+     */
+    private String userName;
+    /**
+     * 关联订单列表
+     */
+    private List<OrderVo> orderList;
+    /**
+     * 收款人
+     */
+    private Integer receiptUserPermissionId;
+    private String receiptUserName;
+    /**
+     * 确认人
+     */
+    private Integer confirmUserPermissionId;
+    private String confirmUserName;
+    /**
+     * 审核人
+     */
+    private Integer reviewUserPermissionId;
+    private String reviewUserName;
+    /**
+     * 撤销人
+     */
+    private Integer cancelUserPermissionId;
+    private String cancelUserName;
+
+}

+ 25 - 0
src/main/java/com/caimei365/order/model/vo/ShopOrderVo.java

@@ -3,6 +3,7 @@ package com.caimei365.order.model.vo;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -146,6 +147,10 @@ public class ShopOrderVo implements Serializable {
      * 订单能否拆分 1 为可拆分, 0为不可拆分
      */
     private Integer splitFlag;
+    /**
+     * 付第三方
+     */
+    private Double shopOtherFee;
     /**
      * 子订单店铺促销活动
      */
@@ -166,4 +171,24 @@ public class ShopOrderVo implements Serializable {
      * (收款买家)收款状态:1待收款、2部分收款、3已收款
      */
     private Integer receiptStatus;
+    /**
+     * 供应商已退金额
+     */
+    private BigDecimal shopRefundAmount;
+    /**
+     * 存在退款中的记录 (0:否,1:是)
+     */
+    private String haveReturning;
+    /**
+     * 子订单返佣标识
+     */
+    private Integer receiptedFlag;
+    /**
+     * 返佣类型 10口头返佣   1或者其他值都是返佣
+     */
+    private Integer receiptedType;
+    /**
+     * 返佣订单关联页面展示用
+     */
+    private OrderVo mainOrder;
 }

+ 215 - 0
src/main/java/com/caimei365/order/service/ReceiptService.java

@@ -0,0 +1,215 @@
+package com.caimei365.order.service;
+
+import com.caimei365.order.model.ResponseJson;
+import com.caimei365.order.model.dto.ReceiptDto;
+import com.caimei365.order.model.dto.ReceiptOperateDto;
+import com.caimei365.order.model.po.ReceiptPo;
+import com.caimei365.order.model.vo.*;
+import com.github.pagehelper.PageInfo;
+import org.springframework.http.HttpHeaders;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/11
+ */
+public interface ReceiptService {
+    /**
+     * 线下收款用户微信授权
+     *
+     * @param code          微信授权code
+     * @param encryptedData 微信加密数据
+     * @param iv            加密算法的初始向量
+     * @return receiptUser
+     */
+    ResponseJson<ReceiptUserVo> receiptAuthorization(String code, String encryptedData, String iv, HttpHeaders headers);
+
+    /**
+     * 线下收款用户密码登录
+     *
+     * @param mobile   手机号
+     * @param password 密码
+     */
+    ResponseJson<ReceiptUserVo> passwordAuthorization(String mobile, String password, String code, String encryptedData, String iv, HttpHeaders headers);
+
+    /**
+     * 获取收款类型
+     */
+    ResponseJson<List<ReceiptTypeVo>> getReceiptType();
+
+    /**
+     * 识别收款短信
+     *
+     * @param smsContent 收款短信
+     */
+    ResponseJson<ReceiptVo> readSmsMessage(String smsContent, HttpHeaders headers);
+
+    /**
+     * 保存收款信息
+     */
+    ResponseJson<ReceiptPo> saveReceipt(ReceiptDto receiptDto, HttpHeaders headers);
+
+    /**
+     * 操作收款信息(作废,设为返佣/普通/供应商退款)
+     *
+     * @param receiptOperateDto {
+     *                          id:收款Id
+     *                          receiptType: 1订单款,2非订单款,3返佣款 4订单款或者非订单款(因财务阶段无法区分订单非订单), 5供应商退款
+     *                          noOrderReason: 非订单款说明
+     *                          delFlag: 作废标记: 0否,其余是
+     *                          }
+     */
+    ResponseJson<ReceiptPo> operateReceipt(ReceiptOperateDto receiptOperateDto, HttpHeaders headers);
+
+    /**
+     * 获取收款列表
+     *
+     * @param receiptType   收款款项类型:1订单款,2非订单款,3返佣款 4订单款或者非订单款(因财务阶段无法区分订单非订单), 5供应商退款
+     * @param receiptStatus 收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【线上支付成功为审核通过】
+     * @param smsContent    收款短信
+     * @param startDate     筛选开始时间
+     * @param endDate       筛选结束时间
+     * @param pageNum       页码
+     * @param pageSize      每页数量
+     */
+    ResponseJson<PageInfo<ReceiptVo>> getReceiptList(Integer receiptType, Integer receiptStatus, String smsContent, String startDate, String endDate, int pageNum, int pageSize, HttpHeaders headers);
+
+    /**
+     * 收款详情(订单/非订单)
+     *
+     * @param id 款项Id
+     */
+    ResponseJson<ReceiptVo> getReceiptDetail(Integer id, HttpHeaders headers);
+
+    /**
+     * 收款详情的款项类型
+     */
+    ResponseJson<Map<String, Integer>> getReceiptDetailType(Integer id, HttpHeaders headers);
+
+    /**
+     * 收款详情(供应商退款)
+     */
+    ResponseJson<ReceiptVo> getReceiptRefundDetail(Integer id, HttpHeaders headers);
+
+    /**
+     * 收款详情(返佣款)
+     */
+    ResponseJson<ReceiptVo> getReceiptRebateDetail(Integer id, HttpHeaders headers);
+
+    /**
+     * 收款详情-订单列表
+     *
+     * @param id                 收款ID
+     * @param organizeId         组织ID
+     * @param orderReceiptStatus 订单收款状态:1待收款,2部分收款,3已收款
+     * @param keyword            搜索关键词(客户名称/订单号)
+     * @param pageNum            页码
+     * @param pageSize           每页数量
+     */
+    ResponseJson<PageInfo<OrderVo>> getReceiptOrderList(Integer id, Integer type, Integer organizeId, String orderReceiptStatus, String keyword, int pageNum, int pageSize, HttpHeaders headers);
+
+    /**
+     * 收款详情-订单详情
+     *
+     * @param orderId 订单Id
+     */
+    ResponseJson<OrderVo> getReceiptOrderInfo(Integer orderId, HttpHeaders headers);
+
+    /**
+     * 供应商退款-子订单列表
+     *
+     * @param id            收款ID
+     * @param confirmedType 0待确认,2已确认子订单
+     * @param shopName      供应商名称
+     * @param keyword       搜索关键词(客户名称/订单号)
+     * @param pageNum       页码
+     * @param pageSize      每页数量
+     */
+    ResponseJson<PageInfo<ShopOrderVo>> getRefundShopOrderList(Integer id, Integer confirmedType, String shopName, String keyword, int pageNum, int pageSize, HttpHeaders headers);
+
+    /**
+     * 供应商退款-订单详情
+     *
+     * @param orderId 订单Id
+     */
+    ResponseJson<OrderVo> getRefundShopOrderInfo(Integer orderId, HttpHeaders headers);
+
+    /**
+     * 返佣款-子订单列表
+     *
+     * @param id       返佣收款ID
+     * @param shopName 供应商名称
+     * @param pageNum  页码
+     * @param pageSize 每页数量
+     */
+    ResponseJson<PageInfo<ShopOrderVo>> getRebateShopOrderList(Integer id, String shopName, int pageNum, int pageSize, HttpHeaders headers);
+
+
+    /**
+     * 关联收款信息
+     *
+     * @param receiptOperateDto {
+     *                          id: 收款Id
+     *                          orderIds: 关联订单IDS
+     *                          confirmType: 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联
+     *                          noOrderReason: 非订单款说明
+     *                          }
+     */
+    ResponseJson<Void> receiptAssociate(ReceiptOperateDto receiptOperateDto, HttpHeaders headers);
+
+    /**
+     * 确认收款-关联供应商退款
+     *
+     * @param receiptOperateDto {
+     *                          id:          收款Id
+     *                          shopOrderId: 关联供应商退款子订单ID
+     *                          }
+     */
+    ResponseJson<Void> receiptAssociateRefund(ReceiptOperateDto receiptOperateDto, HttpHeaders headers);
+
+    /**
+     * 确认收款-关联返佣款
+     *
+     * @param receiptOperateDto {
+     *                          id:           收款Id
+     *                          shopOrderIds: 关联返佣子订单IDs
+     *                          rebateRemarks:关联返佣备注
+     *                          }
+     */
+    ResponseJson<Void> receiptAssociateRebate(ReceiptOperateDto receiptOperateDto, HttpHeaders headers);
+
+    /**
+     * 确认收款-关联口头佣金
+     * crm:/api/user/rebate/verballyReceipt
+     *
+     * @param receiptOperateDto {
+     *                            verbalAmount: 口头返佣佣金
+     *                            shopOrderId: 关联返佣子订单ID
+     *                            rebateRemarks:关联返佣备注
+     *                            }
+     */
+    ResponseJson<Integer> associateVerbalRebate(ReceiptOperateDto receiptOperateDto, HttpHeaders headers);
+
+    /**
+     * 审核收款信息
+     *
+     * @param id            收款Id
+     * @param receiptStatus 收款状态
+     * @param reviewReason  审核不通过原因
+     */
+    ResponseJson<Void> receiptAudit(Integer id, Integer receiptStatus, String reviewReason, HttpHeaders headers);
+
+    /**
+     * 微信收款信息推送(待确认超时)
+     */
+    void sendWechatMsgConfirmTimeout();
+    /**
+     * 微信收款信息推送(待审核超时)
+     */
+    void sendWechatMsgAuditTimeout();
+
+}

+ 2132 - 0
src/main/java/com/caimei365/order/service/impl/ReceiptServiceImpl.java

@@ -0,0 +1,2132 @@
+package com.caimei365.order.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.caimei365.order.components.WeChatService;
+import com.caimei365.order.mapper.BaseMapper;
+import com.caimei365.order.mapper.OrderCommonMapper;
+import com.caimei365.order.mapper.PayOrderMapper;
+import com.caimei365.order.mapper.ReceiptMapper;
+import com.caimei365.order.model.ResponseJson;
+import com.caimei365.order.model.bo.ReceiptParamsBo;
+import com.caimei365.order.model.dto.ReceiptDto;
+import com.caimei365.order.model.dto.ReceiptOperateDto;
+import com.caimei365.order.model.enums.OrderStatus;
+import com.caimei365.order.model.enums.ReceiptStatus;
+import com.caimei365.order.model.enums.ReceiptType;
+import com.caimei365.order.model.po.*;
+import com.caimei365.order.model.vo.*;
+import com.caimei365.order.service.ReceiptService;
+import com.caimei365.order.utils.DateUtils;
+import com.caimei365.order.utils.ImageUtil;
+import com.caimei365.order.utils.MathUtil;
+import com.caimei365.order.utils.Md5Util;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/2/11
+ */
+@Slf4j
+@Service
+public class ReceiptServiceImpl implements ReceiptService {
+    @Value("${caimei.wwwDomain}")
+    private String domain;
+    @Resource
+    private WeChatService weChatService;
+    @Resource
+    private ReceiptMapper receiptMapper;
+    @Resource
+    private OrderCommonMapper orderCommonMapper;
+    @Resource
+    private PayOrderMapper payOrderMapper;
+    @Resource
+    private BaseMapper baseMapper;
+    /**
+     * 只有当识别出来的收款类型是广发银行-5461 ,中信银行-0897,中信银行0897-财付通 和 中信银行0897-支付宝时,才会显示手续费,并且自动勾选上手续费,默认按收款金额的千分之一计算 (收款金额 - 手续费 = 短信中收到的金额)
+     */
+    private static final List<Integer> TIP_FEE_TYPE = Arrays.asList(4, 5, 6, 11);
+    private static final Pattern PATTERN_NUMBER = Pattern.compile("[\\*0-9\\.:]+");
+    private static final Pattern PATTERN_ORDER = Pattern.compile("#(.*?)#");
+
+    /**
+     * 线下收款用户微信授权
+     *
+     * @param code          微信授权code
+     * @param encryptedData 微信加密数据
+     * @param iv            加密算法的初始向量
+     * @return receiptUser
+     */
+    @Override
+    public ResponseJson<ReceiptUserVo> receiptAuthorization(String code, String encryptedData, String iv, HttpHeaders headers) {
+        // 小程序微信授权获取登录信息
+        ResponseJson<Map<String, Object>> appletsInfo = weChatService.getInfoMapByApplets(code, headers, 2);
+        if (appletsInfo.getCode() == -1) {
+            return ResponseJson.error(appletsInfo.getMsg(), null);
+        }
+        Map<String, Object> infoData = appletsInfo.getData();
+        String openid = (String) infoData.get(WeChatService.Keys.OPEN_ID);
+        if (StringUtils.isNotBlank(openid)) {
+            // 收款用户
+            ReceiptUserVo receiptUser = receiptMapper.getReceiptUserByOpenid(openid);
+            if (null != receiptUser) {
+                // 用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+                List<Integer> permissions = receiptMapper.getPermissionsByUserId(receiptUser.getId());
+                receiptUser.setPermissions(permissions);
+                return ResponseJson.success(receiptUser);
+            }
+        }
+        return ResponseJson.error("微信授权失败,请尝试账号密码登录!", null);
+    }
+
+    /**
+     * 线下收款用户密码登录
+     *
+     * @param mobile   手机号
+     * @param password 密码
+     */
+    @Override
+    public ResponseJson<ReceiptUserVo> passwordAuthorization(String mobile, String password, String code, String encryptedData, String iv, HttpHeaders headers) {
+        // 收款用户
+        ReceiptUserVo receiptUser = receiptMapper.getReceiptUserByMobile(mobile);
+        if (null != receiptUser) {
+            // 比对密码
+            String md5Password = Md5Util.md5(password);
+            String dbPassword = receiptUser.getPassword();
+            if (md5Password.equals(dbPassword)) {
+                // 小程序微信授权获取登录信息
+                ResponseJson<Map<String, Object>> appletsInfo = weChatService.getInfoMapByApplets(code, headers, 2);
+                if (appletsInfo.getCode() != -1) {
+                    Map<String, Object> infoData = appletsInfo.getData();
+                    String openid = (String) infoData.get(WeChatService.Keys.OPEN_ID);
+                    String unionId = (String) infoData.get(WeChatService.Keys.UNION_ID);
+                    if (StringUtils.isBlank(unionId)) {
+                        try {
+                            String sessionKey = (String) infoData.get(WeChatService.Keys.SESSION_KEY);
+                            String result = WeChatService.decrypt(encryptedData, sessionKey, iv, "UTF-8");
+                            log.info("解密数据>>>>>>" + result);
+                            Map parseMap = JSONObject.parseObject(result, Map.class);
+                            assert parseMap != null;
+                            unionId = parseMap.get(WeChatService.Keys.UNION_ID).toString();
+                            infoData.put(WeChatService.Keys.UNION_ID, unionId);
+                        } catch (Exception e) {
+                            log.error("微信解密unionId失败:", e);
+                        }
+                    }
+                    receiptUser.setOpenid(openid);
+                    receiptUser.setUnionId(unionId);
+                    receiptMapper.updateOpenidByMobile(openid, unionId, mobile);
+                } else {
+                    return ResponseJson.error(appletsInfo.getMsg(), null);
+                }
+                // 用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+                List<Integer> permissions = receiptMapper.getPermissionsByUserId(receiptUser.getId());
+                receiptUser.setPermissions(permissions);
+                return ResponseJson.success(receiptUser);
+            }
+        }
+        return ResponseJson.error("登录失败!", null);
+    }
+
+    /**
+     * 获取收款类型
+     */
+    @Override
+    public ResponseJson<List<ReceiptTypeVo>> getReceiptType() {
+        List<ReceiptTypeVo> list = receiptMapper.getReceiptType();
+        return ResponseJson.success(list);
+    }
+
+    /**
+     * 识别收款短信
+     *
+     * @param smsContent 收款短信
+     */
+    @Override
+    public ResponseJson<ReceiptVo> readSmsMessage(String smsContent, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(3, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        String md5Msg = Md5Util.md5(smsContent);
+        ReceiptVo receiptVo = getReceiptByMd5Msg(md5Msg);
+        if (receiptVo != null) {
+            // 短信已存在不需要识别
+            return ResponseJson.error("短信已存在不需要重复识别", null);
+        }
+        try {
+            // 识别短信内容
+            ResponseJson<ReceiptVo> receipt = identifyMessage(smsContent);
+            if (receipt != null) {
+                return receipt;
+            }
+        } catch (Exception e) {
+            log.error("短信识别错误!", e);
+        }
+        return ResponseJson.error("短信识别错误!", null);
+    }
+
+    /**
+     * 保存收款信息
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public ResponseJson<ReceiptPo> saveReceipt(ReceiptDto receiptDto, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(3, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        String time = DateUtils.getDateTime();
+        // receiptDto -- copy to --> ReceiptPo
+        ReceiptPo receiptPo = new ReceiptPo();
+        BeanUtils.copyProperties(receiptDto, receiptPo);
+        receiptPo.setUpdateDate(time);
+        // 默认不自动确认审核
+        receiptPo.setAutoAuditFlag(0);
+        if (StringUtils.isNotBlank(receiptDto.getSmsContent())) {
+            receiptPo.setSmsMd5Code(Md5Util.md5(receiptDto.getSmsContent()));
+        }
+        if (null != receiptDto.getId()) {
+            return ResponseJson.error("接口调用错误,该接口用作保存收款信息", null);
+        } else {
+            // 如果是添加收款则判断是否存在重复添加
+            if (StringUtils.isNotBlank(receiptDto.getSmsContent()) && null != receiptDto.getPayWay()) {
+                // 无短信备注内容短信无需判断(payWay传值的话是短信识别来的)
+                String md5Msg = Md5Util.md5(receiptDto.getSmsContent());
+                ReceiptVo receiptVo = getReceiptByMd5Msg(md5Msg);
+                if (receiptVo != null) {
+                    // 短信已存在不需要识别
+                    receiptPo.setSmsContentPresenceFlag(1);
+                    return ResponseJson.success(receiptPo);
+                } else {
+                    receiptPo.setSmsContentPresenceFlag(0);
+                }
+            }
+            if (StringUtils.isNotBlank(receiptDto.getSmsContent())) {
+                String message = receiptDto.getSmsContent().trim();
+                // 隐藏余额
+                message = hiddenBalance(message);
+                receiptPo.setSmsContent(message);
+            }
+            // 收款款项类型receiptType:2非订单款
+            if (null != receiptDto.getReceiptType() && 2 == receiptDto.getReceiptType()) {
+                receiptPo.setReceiptStatus(2);
+            } else {
+                receiptPo.setReceiptStatus(1);
+            }
+            receiptPo.setDelFlag(0);
+            receiptPo.setConfirmType(0);
+            receiptPo.setPayWay(2);
+            if (null == receiptDto.getHandlingFee()) {
+                receiptPo.setHandlingFee(0d);
+            }
+            receiptPo.setReceiptUserPermissionId(receiptUser.getId());
+            // 保存收款记录
+            receiptMapper.insertReceipt(receiptPo);
+
+            /* 如果订单是小程序组织订单来源则包含对应订单Id直接通过订单标识审核收款 */
+            Integer orderFlag = receiptDto.getOrderFlag();
+            // 参数订单标识
+            log.info("收款订单标识>>>>>>" + orderFlag);
+            // 订单款时才走自动关联
+            if (null != receiptDto.getOrderFlag() && null != receiptDto.getReceiptType() && 1 == receiptDto.getReceiptType()) {
+                if (null != orderFlag && orderFlag > 0) {
+                    // 订单信息
+                    OrderVo order = orderCommonMapper.getOrderByOrderId(orderFlag);
+                    if (null != order && 0 != order.getStatus() && 1 != order.getRebateFlag()) {
+                        if (3 == order.getReceiptStatus()) {
+                            // 设置手动回滚事务
+                            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+                            log.info("订单(Id:" + orderFlag + ")已全部收款,无需再收款!");
+                            return ResponseJson.error("订单(Id:" + orderFlag + ")已全部收款,无需再收款!", receiptPo);
+                        }
+                        // 收款项和订单关系表
+                        OrderReceiptRelationPo relation = new OrderReceiptRelationPo();
+                        relation.setDelFlag(0);
+                        relation.setRelationType(2);
+                        relation.setAssociationType(2);
+                        // 记录原始应付金额
+                        Double payAbleAmount = order.getPayableAmount();
+                        // 设置订单未付金额(此处payableAmount用作实际应收)
+                        order.setPayableAmount(getUnPayMoney(order, true));
+                        if (order.getPayableAmount().equals(receiptPo.getReceiptAmount())) {
+                            //确认关联(收款金额=订单应付金额)
+                            log.info(">>>>>>>>>>>>>>>>>>1:" + order.getPayableAmount() + "====" + receiptPo.getReceiptAmount());
+                            receiptPo.setConfirmType(4);
+                            relation.setReceiptId(receiptPo.getId());
+                            relation.setOrderId(order.getOrderId());
+                            relation.setAssociateAmount(receiptPo.getReceiptAmount());
+                        } else if (receiptPo.getReceiptAmount() < order.getPayableAmount()) {
+                            /*(收款金额<订单应付金额)【默认部分收款确认,自动收款不存在抹平确认】*/
+                            log.info(">>>>>>>>>>>>>>>>>>2:" + order.getPayableAmount() + "====" + receiptPo.getReceiptAmount());
+                            receiptPo.setConfirmType(4);
+                            relation.setReceiptId(receiptPo.getId());
+                            relation.setOrderId(order.getOrderId());
+                            relation.setAssociateAmount(receiptPo.getReceiptAmount());
+                        } else if (receiptPo.getReceiptAmount() > order.getPayableAmount()) {
+                            /*(收款金额>订单应付金额)【默认大额退款余额,自动不存在抹平】*/
+                            log.info(">>>>>>>>>>>>>>>>>>3:" + order.getPayableAmount() + "====" + receiptPo.getReceiptAmount());
+                            receiptPo.setConfirmType(3);
+                            relation.setReceiptId(receiptPo.getId());
+                            relation.setOrderId(order.getOrderId());
+                            relation.setAssociateAmount(order.getPayableAmount());
+                        }
+                        // 保存 收款项和订单关系
+                        baseMapper.insertOrderReceiptRelation(relation);
+                        // 默认确认人就是当前短信识别人
+                        receiptPo.setConfirmUserPermissionId(receiptUser.getId());
+                        receiptPo.setUpdateDate(time);
+                        receiptPo.setConfirmDate(time);
+                        receiptPo.setReviewDate(receiptDto.getReceiptDate());
+                        receiptPo.setPayWay(2);
+                        // 更新收款记录
+                        receiptMapper.updateReceipt(receiptPo);
+
+                        /* 审核订单关系 */
+                        String statusFlag = "";
+                        String receiptStatus = "";
+                        Double totalAssociateAmount = 0d;
+                        // 重置应付金额已供重新计算
+                        order.setPayableAmount(payAbleAmount);
+                        log.info(">>>>>>>>>>>>>>>>>>setPayableAmount:" + payAbleAmount);
+                        order.setPayableAmount(getUnPayMoney(order, false));
+                        log.info("-----------------UnPayMoney:" + getUnPayMoney(order, false));
+                        log.info("-----------------payAbleAmount:" + order.getPayableAmount());
+                        if (order.getPayableAmount().equals(receiptPo.getReceiptAmount())) {
+                            //确认关联(收款金额=订单应付金额)
+                            log.info(">>>>>>>>>>>>>>>>>>11:" + order.getPayableAmount() + "====" + receiptPo.getReceiptAmount());
+                            statusFlag = "3";
+                            receiptStatus = "3";
+                        } else if (receiptPo.getReceiptAmount() < order.getPayableAmount()) {
+                            /*(收款金额<订单应付金额)*/
+                            log.info(">>>>>>>>>>>>>>>>>>22:" + order.getPayableAmount() + "====" + receiptPo.getReceiptAmount());
+                            statusFlag = "2";
+                            receiptStatus = "2";
+                        } else if (receiptPo.getReceiptAmount() > order.getPayableAmount()) {
+                            /*(收款金额>订单应付金额)*/
+                            log.info(">>>>>>>>>>>>>>>>>>33:" + order.getPayableAmount() + "====" + receiptPo.getReceiptAmount());
+                            statusFlag = "3";
+                            receiptStatus = "3";
+                            totalAssociateAmount += relation.getAssociateAmount();
+                        }
+                        Integer status = order.getStatus();
+                        log.info(">>>>>>status:" + status);
+                        if (status == 0 || status == 4 || status == 5 || status == 6 || status == 7 || status == 33 || status == 32 || status == 31) {
+                            // 防止使用短信操作已支付或者为确认的订单。
+                            receiptPo.setAutoAuditFlag(0);
+                            return ResponseJson.success(receiptPo);
+                        }
+                        statusFlag = statusFlag + status.toString().charAt(1);
+                        order.setPayableAmount(payAbleAmount);
+                        order.setStatus(Integer.parseInt(statusFlag));
+                        order.setReceiptStatus(Integer.parseInt(receiptStatus));
+                        /* 设置订单状态 */
+                        // 获取主订单下面的所有的确认收货商品数量
+                        Integer receiptNum = receiptMapper.getReceiptNumByOrderId(order.getOrderId());
+                        Integer returnAndCancelNum = 0;
+                        boolean returnFlag = null != order.getRefundType() && (1 == order.getRefundType() || 2 == order.getRefundType());
+                        // 获取该主订单下面所有已完成状态退货退款商品数量
+                        if (returnFlag) {
+                            returnAndCancelNum = receiptMapper.getReturnAndCancelNum(order.getOrderId());
+                        }
+                        // 确认收货商品数量+已完成状态退货退款商品数量 = 订单商品总数
+                        int compare = MathUtil.compare(order.getProductCount(), MathUtil.add(returnAndCancelNum, receiptNum));
+                        if (compare == 0) {
+                            //已收款--->交易完成
+                            if (null != order.getReceiptStatus() && 3 == order.getReceiptStatus()) {
+                                order.setStatus(OrderStatus.TRANSACTION_COMPLETED.getCode());
+                                //已收款+已付款--->订单完成
+                                if (null != order.getPayStatus() && 3 == order.getPayStatus()) {
+                                    order.setStatus(OrderStatus.ORDER_COMPLETED.getCode());
+                                }
+                            }
+                        }
+                        // 更新订单收款状态
+                        payOrderMapper.updateOrderStatus(order);
+                        log.info("-------------审核通过订单状态改变---orderId:" + order.getOrderId() + "--status:" + order.getStatus() + "----------" + receiptPo.getConfirmType());
+
+                        /* 审核订单关系 */
+                        if (null != receiptPo.getConfirmType() && 3 == receiptPo.getConfirmType()) {
+                            log.info(">>>>>>>>>>>>>>>>>>55:" + receiptPo.getReceiptAmount() + "====" + totalAssociateAmount);
+                            // 本次收款减已经收款金额
+                            Double money = receiptPo.getReceiptAmount() - totalAssociateAmount;
+                            // 加入用户余额
+
+                            // 修改账户余额
+                            double userMoney = baseMapper.getUserMoney(order.getUserId());
+                            // 可用余额
+                            Double availableMoney = baseMapper.getAbleUserMoney(order.getUserId());
+                            log.info("------->userId:" + order.getUserId() + ",原余额:" + userMoney + "==原可用余额:" + availableMoney);
+                            if (money < 0d) {
+                                // 待确认状态不需要修改余额,因为本身存在冻结余额不需要重复退回
+                                if (0 != order.getStatus()) {
+                                    userMoney = MathUtil.add(userMoney, money).doubleValue();
+                                }
+                            } else {
+                                if (0 != order.getStatus()) {
+                                    userMoney = MathUtil.add(availableMoney, money).doubleValue();
+                                }
+                                availableMoney = MathUtil.add(availableMoney, money).doubleValue();
+                            }
+
+                            payOrderMapper.updateUserMoney(userMoney, availableMoney, order.getUserId());
+                            log.info("------->userId:" + order.getUserId() + ",更新余额:" + userMoney + "==更新可用余额:" + availableMoney);
+                            log.info("----------->大额抹平金额退回账户:" + order.getUserId());
+                            log.info("----------->大额抹平金额:" + money);
+                            //保存余额到余额收支记录
+                            BalanceRecordPo balanceRecord = new BalanceRecordPo();
+                            balanceRecord.setUserId(order.getUserId());
+                            balanceRecord.setType(1);
+                            balanceRecord.setBalanceType(2);
+                            balanceRecord.setAddDate(new Date());
+                            balanceRecord.setAmount(money);
+                            balanceRecord.setOrderId(order.getOrderId());
+                            balanceRecord.setReceiptId(receiptPo.getId());
+                            balanceRecord.setRemark("大额抹平金额退回账户");
+                            balanceRecord.setDelFlag(0);
+                            // 保存 余额收支记录
+                            baseMapper.insertBalanceRecord(balanceRecord);
+                        }
+
+                        receiptPo.setReceiptStatus(3);
+                        receiptPo.setReviewUserPermissionId(receiptUser.getId());
+                        receiptMapper.updateReceipt(receiptPo);
+
+                        /* 保存订单关系之后自动审核*/
+                        receiptPo.setAutoAuditFlag(1);
+
+                    } else {//针对订单号异常或订单不存在的则继续分享人工确认
+                        receiptPo.setAutoAuditFlag(0);
+                    }
+                } else {
+                    // 针对没有订单号的也是走老流程
+                    receiptPo.setAutoAuditFlag(0);
+                }
+            }
+        }
+        return ResponseJson.success(receiptPo);
+    }
+
+    /**
+     * 操作收款信息(作废,设为返佣/普通/供应商退款/)
+     *
+     * @param receiptOperateDto {
+     *                   id:收款Id
+     *                   receiptType: 1订单款,2非订单款,3返佣款 4订单款或者非订单款(因财务阶段无法区分订单非订单), 5供应商退款
+     *                   noOrderReason: 非订单款说明
+     *                   delFlag: 作废标记: 0否,其余是
+     *                   }
+     */
+    @Override
+    public ResponseJson<ReceiptPo> operateReceipt(ReceiptOperateDto receiptOperateDto, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(3, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(receiptOperateDto.getId());
+        if (null == receipt) {
+            return ResponseJson.error("收款数据异常!", null);
+        }
+        String time = DateUtils.getDateTime();
+        ReceiptPo receiptPo = new ReceiptPo();
+        receiptPo.setId(receiptOperateDto.getId());
+        receiptPo.setUpdateDate(time);
+        // 收款作废
+        if (null != receiptOperateDto.getDelFlag() && receiptOperateDto.getDelFlag() > 0) {
+            // 收款状态receiptStatus:2已确认(待审核)、3审核通过
+            if (null != receipt.getReceiptStatus() && (2 == receipt.getReceiptStatus() || 3 == receipt.getReceiptStatus())) {
+                // 收款款项类型receiptType:1订单款
+                if (null != receipt.getReceiptType() && 1 == receipt.getReceiptType()) {
+                    return ResponseJson.error(-2, "该收款已关联了订单,不能作废!请刷新页面查看最新数据", null);
+                }
+                // 收款款项类型receiptType:3返佣款
+                else if (null != receipt.getReceiptType() && 3 == receipt.getReceiptType()) {
+                    return ResponseJson.error(-2, "该收款已关联了返佣订单,不能作废!请刷新页面查看最新数据", null);
+                }
+            }
+            receiptPo.setDelFlag(receiptOperateDto.getDelFlag());
+        }
+        receiptPo.setReceiptType(receiptOperateDto.getReceiptType());
+        // 收款款项类型receiptType:2非订单款
+        if (null != receiptOperateDto.getReceiptType() && 2 == receiptOperateDto.getReceiptType()) {
+            receiptPo.setNoOrderReason(receiptOperateDto.getNoOrderReason());
+            receiptPo.setReceiptStatus(2);
+            receiptPo.setConfirmUserPermissionId(receiptUser.getId());
+            receiptPo.setConfirmDate(time);
+        }
+        // 更新收款记录
+        receiptMapper.updateReceipt(receiptPo);
+        return ResponseJson.success(receiptPo);
+    }
+
+    /**
+     * 获取收款列表
+     *
+     * @param receiptType   收款款项类型:1订单款,2非订单款,3返佣款 4订单款或者非订单款(因财务阶段无法区分订单非订单) 5供应商退款
+     * @param receiptStatus 收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【线上支付成功为审核通过】
+     * @param smsContent    收款短信
+     * @param startDate     筛选开始时间
+     * @param endDate       筛选结束时间
+     * @param pageNum       页码
+     * @param pageSize      每页数量
+     */
+    @Override
+    public ResponseJson<PageInfo<ReceiptVo>> getReceiptList(Integer receiptType, Integer receiptStatus, String smsContent, String startDate, String endDate, int pageNum, int pageSize, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(1, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        ReceiptParamsBo paramsBo = new ReceiptParamsBo();
+        if (StringUtils.isNotBlank(smsContent)) {
+            // 短信内容隐藏余额
+            String message = hiddenBalance(smsContent);
+            paramsBo.setSmsMd5Code(Md5Util.md5(message));
+        }
+        if (null != receiptStatus && receiptStatus > 0) {
+            String[] receiptStatusArr;
+            if (1 == receiptStatus) {
+                receiptStatusArr = "1,4,5".split(",");
+            } else {
+                receiptStatusArr = receiptStatus.toString().split(",");
+            }
+            paramsBo.setReceiptStatusArr(receiptStatusArr);
+        }
+        // 用户openid
+        String openid = headers.getFirst("x-openid");
+        // 获取该收款用户下的机构userIds
+        List<Integer> clubUserIds = getReceiptClubUserIds(openid, receiptStatus);
+        paramsBo.setUserIds(clubUserIds);
+        paramsBo.setReceiptType(receiptType);
+        paramsBo.setStartDate(startDate);
+        paramsBo.setEndDate(endDate);
+        // 分页请求数据
+        PageHelper.startPage(pageNum, pageSize);
+        List<ReceiptVo> list = receiptMapper.getReceiptList(paramsBo);
+        for (ReceiptVo receipt : list) {
+            if (null != receipt) {
+                setReceiptDetail(receipt);
+            }
+        }
+        PageInfo<ReceiptVo> pageInfo = new PageInfo(list);
+        return ResponseJson.success(pageInfo);
+    }
+
+    /**
+     * 收款详情(订单/非订单)
+     *
+     * @param id 款项Id
+     */
+    @Override
+    public ResponseJson<ReceiptVo> getReceiptDetail(Integer id, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(2, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(id);
+        if (null != receipt) {
+            setReceiptDetail(receipt);
+            List<OrderVo> orderList = new ArrayList<>();
+            if (null != receipt.getReceiptStatus() && 1 == receipt.getReceiptStatus()) {
+                // 待确认
+                if (StringUtils.isNotBlank(receipt.getOrderFlag())) {
+                    // 订单信息
+                    OrderVo order = orderCommonMapper.getOrderByOrderId(Integer.parseInt(receipt.getOrderFlag()));
+                    if (null != order) {
+                        orderList.add(order);
+                    }
+                }
+                log.info("--------待确认--------");
+            } else {
+                // 已确认
+                orderList = receiptMapper.getReceiptOrders(receipt.getId());
+                log.info("--------已确认--------");
+            }
+            if (!CollectionUtils.isEmpty(orderList)) {
+                // 用户openid
+                String openid = headers.getFirst("x-openid");
+                // 获取该收款用户下的机构userIds
+                List<Integer> clubUserIds = getReceiptClubUserIds(openid, receipt.getReceiptStatus());
+                for (OrderVo order : orderList) {
+                    if (4 == receiptUser.getUserType() || clubUserIds.contains(order.getUserId())) {
+                        // 收款详情是否可以查看
+                        order.setAuthority(true);
+                    }
+                }
+            }
+            receipt.setOrderList(orderList);
+        }
+        return ResponseJson.success(receipt);
+    }
+
+    /**
+     * 收款详情的款项类型
+     *
+     * @param id 款项Id
+     */
+    @Override
+    public ResponseJson<Map<String, Integer>> getReceiptDetailType(Integer id, HttpHeaders headers) {
+        // 检查收款用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+        ReceiptUserVo receiptUser = checkPermissions(0, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(id);
+        Map<String, Integer> map = new HashMap<>();
+        map.put("id", id);
+        if (null != receipt) {
+            map.put("receiptType", receipt.getReceiptType());
+            map.put("receiptStatus", receipt.getReceiptStatus());
+        }
+        return ResponseJson.success(map);
+    }
+
+    /**
+     * 收款详情(供应商退款)
+     */
+    @Override
+    public ResponseJson<ReceiptVo> getReceiptRefundDetail(Integer id, HttpHeaders headers) {
+        // 检查收款用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+        ReceiptUserVo receiptUser = checkPermissions(6, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(id);
+        if (null != receipt) {
+            setReceiptDetail(receipt);
+        }
+        return ResponseJson.success(receipt);
+    }
+
+    /**
+     * 收款详情(返佣款)
+     */
+    @Override
+    public ResponseJson<ReceiptVo> getReceiptRebateDetail(Integer id, HttpHeaders headers) {
+        // 检查收款用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+        ReceiptUserVo receiptUser = checkPermissions(5, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(id);
+        if (null != receipt) {
+            setReceiptDetail(receipt);
+        }
+        return ResponseJson.success(receipt);
+    }
+
+    /**
+     * 收款详情-订单列表
+     *
+     * @param id                 收款ID
+     * @param organizeId         组织ID
+     * @param orderReceiptStatus 订单收款状态:1待收款,2部分收款,3已收款
+     * @param keyword            搜索关键词(客户名称/订单号)
+     * @param pageNum            页码
+     * @param pageSize           每页数量
+     */
+    @Override
+    public ResponseJson<PageInfo<OrderVo>> getReceiptOrderList(Integer id, Integer type, Integer organizeId, String orderReceiptStatus, String keyword, int pageNum, int pageSize, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(2, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        if (StringUtils.isBlank(orderReceiptStatus)) {
+            orderReceiptStatus = "1,2";
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(id);
+        if (null == receipt) {
+            return ResponseJson.error("收款数据异常!", null);
+        }
+        Integer excludeOrderId = null;
+        try {
+            excludeOrderId = Integer.parseInt(receipt.getOrderFlag());
+        } catch (NumberFormatException e) {
+            log.info("---------此收款无订单标注------");
+        }
+        String[] receiptStatusArr = orderReceiptStatus.split(",");
+        List<Integer> orderStatus;
+        boolean receiptFlag = false;
+        if (ArrayUtils.contains(receiptStatusArr, "3")) {
+            orderStatus = Arrays.asList(4, 5, 31, 32, 33);
+            receiptFlag = true;
+        } else {
+            orderStatus = Arrays.asList(11, 12, 13, 21, 22, 23);
+        }
+        if (null == type || 0 == type) {
+            type = 0;
+        }
+        // 用户openid
+        String openid = headers.getFirst("x-openid");
+        // 获取该收款用户下的机构userIds
+        List<Integer> userIds = getReceiptClubUserIds(openid, receipt.getReceiptStatus());
+        String userName;
+        Integer orderId = null;
+        try {
+            orderId = Integer.parseInt(keyword);
+        } catch (NumberFormatException ignored) {
+        }
+        if (null != orderId && orderId.toString().length() == keyword.length()) {
+            userName = "";
+        } else {
+            userName = keyword;
+        }
+        ReceiptParamsBo params = new ReceiptParamsBo();
+        params.setUserIds(userIds);
+        params.setOrderId(orderId);
+        params.setExcludeOrderId(excludeOrderId);
+        params.setReceiptStatusArr(receiptStatusArr);
+        params.setOrderStatusList(orderStatus);
+        params.setUserName(userName);
+        params.setOrganizeId(organizeId);
+        params.setOrderType(type);
+        // 分页请求数据
+        PageHelper.startPage(pageNum, pageSize);
+        // 将有订单标识的订单放到列表的首位显示
+        List<OrderVo> orderList = receiptMapper.getOrderListByParams(params);
+        Iterator<OrderVo> iterator = orderList.iterator();
+        while (iterator.hasNext()) {
+            OrderVo order = iterator.next();
+            Integer receiptOnlineId = receiptMapper.getOnlineReceiptId(order.getOrderId());
+            if (null != receiptOnlineId && receiptOnlineId > 0) {
+                // 排除线上支付订单
+                iterator.remove();
+            } else {
+                // 存在退款中的记录 (0:否,1:是)
+                Integer haveReturning = receiptMapper.countHaveReturning(order.getOrderId());
+                if (null != haveReturning && haveReturning > 0) {
+                    haveReturning = 1;
+                } else {
+                    haveReturning = 0;
+                }
+                order.setHaveReturning(haveReturning.toString());
+                // 存在收款中的记录 (0:否,1:是)
+                Integer haveReceipting = receiptMapper.countHaveReceipting(order.getOrderId());
+                if (null != haveReceipting && haveReceipting > 0) {
+                    haveReceipting = 1;
+                } else {
+                    haveReceipting = 0;
+                }
+                order.setHaveReceipting(haveReceipting.toString());
+                if (receiptFlag) {
+                    order.setSurplusAmount(0d);
+                }
+                // 设置订单的款项信息
+                setOrderAmountInfo(order);
+            }
+        }
+        PageInfo<OrderVo> pageInfo = new PageInfo(orderList);
+        return ResponseJson.success(pageInfo);
+    }
+
+    /**
+     * 收款详情-订单详情
+     *
+     * @param orderId 订单Id
+     */
+    @Override
+    public ResponseJson<OrderVo> getReceiptOrderInfo(Integer orderId, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(2, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        // 订单信息
+        OrderVo order = orderCommonMapper.getOrderByOrderId(orderId);
+        // 设置订单的款项信息
+        setOrderAmountInfo(order);
+        // 子订单信息
+        List<ShopOrderVo> shopOrderList = orderCommonMapper.getShopOrderListByOrderId(order.getOrderId());
+        shopOrderList.removeIf(s -> s.getShopId() == 998);
+        // 订单商品信息
+        double totalAddedValueTax = 0d;
+        for (ShopOrderVo shopOrder : shopOrderList) {
+            // 供应商名称
+            String shopName = baseMapper.getShopNameById(shopOrder.getShopId());
+            shopOrder.setShopName(shopName);
+            List<OrderProductVo> orderProductList = orderCommonMapper.getShopOrderProduct(shopOrder.getShopOrderId());
+            for (OrderProductVo orderProduct : orderProductList) {
+                if (null != orderProduct.getTotalAddedValueTax()) {
+                    totalAddedValueTax = MathUtil.add(totalAddedValueTax, orderProduct.getTotalAddedValueTax()).doubleValue();
+                }
+            }
+            shopOrder.setOrderProductList(orderProductList);
+        }
+        order.setTotalAddedValueTax(totalAddedValueTax);
+        order.setShopOrderList(shopOrderList);
+        return ResponseJson.success(order);
+    }
+
+    /**
+     * 供应商退款-子订单列表
+     *
+     * @param id            收款ID
+     * @param confirmedType 0待确认,2已确认子订单
+     * @param shopName      供应商名称
+     * @param keyword       搜索关键词(客户名称/订单号)
+     * @param pageNum       页码
+     * @param pageSize      每页数量
+     */
+    @Override
+    public ResponseJson<PageInfo<ShopOrderVo>> getRefundShopOrderList(Integer id, Integer confirmedType, String shopName, String keyword, int pageNum, int pageSize, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(2, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        List<ShopOrderVo> shopOrderList;
+        PageHelper.startPage(pageNum, pageSize);
+        if (id != null && id > 0) {
+            // 已关联子订单,展示关联信息
+            shopOrderList = receiptMapper.getShopOrderListByReceiptId(id);
+            for (ShopOrderVo shopOrder : shopOrderList) {
+                setReceiptedAmount(shopOrder);
+            }
+        } else {
+            String userName = keyword;
+            Integer orderId = null;
+            try {
+                orderId = Integer.parseInt(keyword);
+            } catch (NumberFormatException ignored) {
+            }
+            if (null != orderId && orderId.toString().length() == keyword.length()) {
+                userName = "";
+            }
+            //没有关联子订单,根据shopName搜索子订单进行关联
+            //这里的子订单  必须满足 它的主订单只进行过返佣收款或者主订单根本没有收过款
+            shopOrderList = receiptMapper.getRefundShopOrders(confirmedType, shopName, userName, orderId);
+            for (ShopOrderVo shopOrder : shopOrderList) {
+                setReceiptedAmount(shopOrder);
+                if (confirmedType != null && confirmedType == 2) {
+                    shopOrder.setHaveReturning("1");
+                }
+            }
+        }
+        PageInfo<ShopOrderVo> pageInfo = new PageInfo(shopOrderList);
+        return ResponseJson.success(pageInfo);
+    }
+
+    /**
+     * 供应商退款-订单详情
+     *
+     * @param orderId 订单Id
+     */
+    @Override
+    public ResponseJson<OrderVo> getRefundShopOrderInfo(Integer orderId, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(2, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        // 订单信息
+        OrderVo order = orderCommonMapper.getOrderByOrderId(orderId);
+        // 设置订单的款项信息
+        setOrderAmountInfo(order);
+        // 子订单信息
+        List<ShopOrderVo> shopOrderList = orderCommonMapper.getShopOrderListByOrderId(order.getOrderId());
+        // 订单商品信息
+        double needPayAmount = 0d;
+        for (ShopOrderVo shopOrder : shopOrderList) {
+            // 供应商名称
+            String shopName = baseMapper.getShopNameById(shopOrder.getShopId());
+            shopOrder.setShopName(shopName);
+            needPayAmount += (shopOrder.getNeedPayAmount() == null ? 0d : shopOrder.getNeedPayAmount());
+            List<OrderProductVo> orderProductList = orderCommonMapper.getShopOrderProduct(shopOrder.getShopOrderId());
+            for (OrderProductVo orderProduct : orderProductList) {
+                orderProduct.setImage(ImageUtil.getImageUrl("product", orderProduct.getImage(), domain));
+            }
+            shopOrder.setOrderProductList(orderProductList);
+            List<Integer> rebatePayType = receiptMapper.getRebatePayTypeList(shopOrder.getShopOrderId());
+            if (CollectionUtils.isEmpty(rebatePayType)) {
+                // 没返佣
+                shopOrder.setReceiptedFlag(0);
+            } else if (10 == rebatePayType.get(0)) {
+                // 口头返佣
+                shopOrder.setReceiptedType(10);
+                shopOrder.setReceiptedFlag(1);
+            } else {
+                shopOrder.setReceiptedType(1);
+                shopOrder.setReceiptedFlag(1);
+            }
+        }
+        order.setShopOrderList(shopOrderList);
+        //收款金额
+        if (null != order.getRebateFlag() && 1 == order.getRebateFlag()) {
+            order.setReceiptAmount(needPayAmount);
+            double surplusAmount = MathUtil.sub(order.getPayTotalFee(), needPayAmount).doubleValue();
+            order.setSurplusAmount(surplusAmount);
+        } else {
+            // 获取订单已支付金额
+            Double paidAmount = receiptMapper.getPaidAmountByOrderId(order.getOrderId());
+            order.setReceiptAmount(paidAmount);
+            double surplusAmount = MathUtil.sub(order.getPayTotalFee(), paidAmount).doubleValue();
+            order.setSurplusAmount(surplusAmount);
+        }
+        return ResponseJson.success(order);
+    }
+
+    /**
+     * 返佣款-子订单列表
+     *
+     * @param id       返佣收款ID
+     * @param shopName 供应商名称
+     * @param pageNum  页码
+     * @param pageSize 每页数量
+     */
+    @Override
+    public ResponseJson<PageInfo<ShopOrderVo>> getRebateShopOrderList(Integer id, String shopName, int pageNum, int pageSize, HttpHeaders headers) {
+        // 检查收款用户权限
+        ReceiptUserVo receiptUser = checkPermissions(2, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        List<ShopOrderVo> shopOrderList;
+        PageHelper.startPage(pageNum, pageSize);
+        if (id != null && id > 0) {
+            // 已关联子订单,展示关联信息
+            shopOrderList = receiptMapper.getShopOrderListByReceiptId(id);
+            for (ShopOrderVo shopOrder : shopOrderList) {
+                setReceiptedAmount(shopOrder);
+            }
+        } else {
+            //没有关联子订单,根据shopName搜索子订单进行关联
+            //这里的子订单  必须满足 它的主订单只进行过返佣收款或者主订单根本没有收过款
+            shopOrderList = receiptMapper.getRebateShopOrders(shopName);
+            for (ShopOrderVo shopOrder : shopOrderList) {
+                setReceiptedAmount(shopOrder);
+            }
+        }
+        PageInfo<ShopOrderVo> pageInfo = new PageInfo(shopOrderList);
+        return ResponseJson.success(pageInfo);
+    }
+
+    /**
+     * 关联收款信息
+     *
+     * @param receiptOperateDto {
+     *                            id: 收款Id
+     *                            orderIds: 关联订单IDS
+     *                            confirmType: 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联
+     *                            noOrderReason: 非订单款说明
+     *                            }
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public ResponseJson<Void> receiptAssociate(ReceiptOperateDto receiptOperateDto, HttpHeaders headers) {
+        // 检查收款用户权限: 4收款关联订单
+        ReceiptUserVo receiptUser = checkPermissions(4, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(receiptOperateDto.getId());
+        if (null == receipt) {
+            return ResponseJson.error("收款数据异常!", null);
+        }
+        // 删除之前与这个收款所有的关系
+        receiptMapper.deleteReceiptRelation(receipt.getId());
+        // ReceiptVo -- copy to --> ReceiptPo
+        String time = DateUtils.getDateTime();
+        ReceiptPo receiptPo = new ReceiptPo();
+        BeanUtils.copyProperties(receipt, receiptPo);
+        receiptPo.setUpdateDate(time);
+        receiptPo.setReceiptStatus(2);
+        receiptPo.setConfirmType(receiptOperateDto.getConfirmType());
+        // 非订单款项确认
+        if (2 == receipt.getReceiptType()) {
+            if (StringUtils.isNotBlank(receiptOperateDto.getNoOrderReason())) {
+                receiptPo.setNoOrderReason(receiptOperateDto.getNoOrderReason());
+            }
+        }
+        // 订单款项
+        if (1 == receipt.getReceiptType()) {
+            if (StringUtils.isNotBlank(receiptOperateDto.getOrderIds())) {
+                String[] orderIDS = receiptOperateDto.getOrderIds().split(",");
+                // 关联一个订单
+                if (orderIDS.length == 1) {
+                    // 收款项和订单关系表
+                    OrderReceiptRelationPo relation = new OrderReceiptRelationPo();
+                    relation.setDelFlag(0);
+                    relation.setRelationType(2);
+                    relation.setAssociationType(1);
+                    // 订单信息
+                    OrderVo order = orderCommonMapper.getOrderByOrderId(Integer.parseInt(orderIDS[0]));
+                    //设置订单未付金额(此处payableAmount用作实际应收)
+                    order.setPayableAmount(getUnPayMoney(order, true));
+                    // 收款金额 = 订单应付金额
+                    if (0 == MathUtil.compare(receiptPo.getReceiptAmount(), order.getPayableAmount())) {
+                        //确认关联
+                        receiptPo.setConfirmType(4);
+                        relation.setReceiptId(receiptPo.getId());
+                        relation.setOrderId(order.getOrderId());
+                        relation.setAssociateAmount(receiptPo.getReceiptAmount());
+                    }
+                    // 收款金额 < 订单应付金额
+                    else if (-1 == MathUtil.compare(receiptPo.getReceiptAmount(), order.getPayableAmount())) {
+                        // 小额抹平确认
+                        if (null != receiptPo.getConfirmType() && 1 == receiptPo.getConfirmType()) {
+                            relation.setReceiptId(receiptPo.getId());
+                            relation.setOrderId(order.getOrderId());
+                            relation.setAssociateAmount(receiptPo.getReceiptAmount());
+                        }
+                        // 部分收款确认
+                        if (null != receiptPo.getConfirmType() && 4 == receiptPo.getConfirmType()) {
+                            relation.setReceiptId(receiptPo.getId());
+                            relation.setOrderId(order.getOrderId());
+                            relation.setAssociateAmount(receiptPo.getReceiptAmount());
+                        }
+                    }
+                    // 收款金额 > 订单应付金额
+                    else if (1 == MathUtil.compare(receiptPo.getReceiptAmount(), order.getPayableAmount())) {
+                        // 大额抹平确认
+                        if (null != receiptPo.getConfirmType() && 2 ==receiptPo.getConfirmType()) {
+                            relation.setReceiptId(receiptPo.getId());
+                            relation.setOrderId(order.getOrderId());
+                            relation.setAssociateAmount(order.getPayableAmount());
+                        }
+                        // 大额退款余额
+                        if (null != receiptPo.getConfirmType() && 3 == receiptPo.getConfirmType()) {
+                            relation.setReceiptId(receiptPo.getId());
+                            relation.setOrderId(order.getOrderId());
+                            relation.setAssociateAmount(order.getPayableAmount());
+                        }
+                    }
+                    // 保存 收款项和订单关系
+                    baseMapper.insertOrderReceiptRelation(relation);
+                }
+                // 关联多个订单
+                else if (orderIDS.length > 1) {
+                    // 先满足金额小的订单,如果金额相等就先满足ID较小的订单
+                    List<OrderVo> list = receiptMapper.getOrderListByOrderIds(orderIDS);
+                    for (OrderVo item : list) {
+                        item.setPayableAmount(getUnPayMoney(item, true));
+                    }
+                    // 余下的收款金额
+                    Double amount = receiptPo.getReceiptAmount();
+                    if (null != receiptPo.getConfirmType() && 4 == receiptPo.getConfirmType()) {
+                        // 确认关联
+                        for (OrderVo order : list) {
+                            if (amount > 0) {
+                                OrderReceiptRelationPo relation = new OrderReceiptRelationPo();
+                                relation.setDelFlag(0);
+                                relation.setRelationType(2);
+                                if (amount >= order.getPayableAmount()) {
+                                    relation.setReceiptId(receiptPo.getId());
+                                    relation.setOrderId(order.getOrderId());
+                                    relation.setAssociateAmount(order.getPayableAmount());
+                                } else if (amount < order.getPayableAmount()) {
+                                    relation.setReceiptId(receiptPo.getId());
+                                    relation.setOrderId(order.getOrderId());
+                                    relation.setAssociateAmount(amount);
+                                }
+                                // 保存 收款项和订单关系
+                                baseMapper.insertOrderReceiptRelation(relation);
+                                amount -= order.getPayableAmount();
+                            }
+                        }
+                    } else if (null != receiptPo.getConfirmType() && 1 == receiptPo.getConfirmType()) {
+                        //小额抹平确认
+                        for (OrderVo order : list) {
+                            OrderReceiptRelationPo relation = new OrderReceiptRelationPo();
+                            relation.setDelFlag(0);
+                            relation.setRelationType(2);
+                            if (amount >= order.getPayableAmount()) {
+                                relation.setReceiptId(receiptPo.getId());
+                                relation.setOrderId(order.getOrderId());
+                                relation.setAssociateAmount(order.getPayableAmount());
+                            } else if (amount < order.getPayableAmount()) {
+                                relation.setReceiptId(receiptPo.getId());
+                                relation.setOrderId(order.getOrderId());
+                                relation.setAssociateAmount(amount < 0d ? 0d : amount);
+                            }
+                            // 保存 收款项和订单关系
+                            baseMapper.insertOrderReceiptRelation(relation);
+                            amount -= order.getPayableAmount();
+                        }
+                    } else if (null != receiptPo.getConfirmType() && 2 ==receiptPo.getConfirmType()) {
+                        //大额抹平确认
+                        for (OrderVo order : list) {
+                            OrderReceiptRelationPo relation = new OrderReceiptRelationPo();
+                            relation.setDelFlag(0);
+                            relation.setRelationType(2);
+                            if (amount >= order.getPayableAmount()) {
+                                relation.setReceiptId(receiptPo.getId());
+                                relation.setOrderId(order.getOrderId());
+                                relation.setAssociateAmount(order.getPayableAmount());
+                                // 保存 收款项和订单关系
+                                baseMapper.insertOrderReceiptRelation(relation);
+                                amount -= order.getPayableAmount();
+                            } else {
+                                // 设置手动回滚事务
+                                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+                                log.info("订单(Id:" + order.getOrderId() + ")大额抹平确认--金额不正确!");
+                                return ResponseJson.error("订单(Id:" + order.getOrderId() + ")大额抹平确认--金额不正确!", null);
+                            }
+                        }
+                    } else if (null != receiptPo.getConfirmType() && 3 == receiptPo.getConfirmType()) {
+                        //大额退款余额
+                        for (OrderVo order : list) {
+                            OrderReceiptRelationPo relation = new OrderReceiptRelationPo();
+                            relation.setDelFlag(0);
+                            relation.setRelationType(2);
+                            if (amount >= order.getPayableAmount()) {
+                                relation.setReceiptId(receiptPo.getId());
+                                relation.setOrderId(order.getOrderId());
+                                relation.setAssociateAmount(order.getPayableAmount());
+                                // 保存 收款项和订单关系
+                                baseMapper.insertOrderReceiptRelation(relation);
+                                amount -= order.getPayableAmount();
+                            } else {
+                                // 设置手动回滚事务
+                                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+                                log.info("订单(Id:" + order.getOrderId() + ")大额退款余额--金额不正确!");
+                                return ResponseJson.error("订单(Id:" + order.getOrderId() + ")大额退款余额--金额不正确!", null);
+                            }
+                        }
+                    }
+                }
+            } else {
+                // 设置手动回滚事务
+                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+                log.info("关联订单为空!");
+                return ResponseJson.error("关联订单为空!", null);
+            }
+        }
+        receiptPo.setPayWay(2);
+        receiptPo.setConfirmUserPermissionId(receiptUser.getId());
+        receiptPo.setConfirmDate(time);
+        receiptMapper.updateReceipt(receiptPo);
+
+        // 关联成功推送模板消息给指定人员(用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核)
+        List<String> openidList = receiptMapper.getOpenidListByPermission(Collections.singletonList(7));
+        openidList.removeIf(Objects::isNull);
+        double associateAmount = receiptMapper.countAssociateAmountById(receipt.getId());
+        String associateTitle = "收款和订单已确认关联,请及时审核!";
+        String associateDate = receipt.getReceiptDate();
+        String associateType = receiptMapper.getReceiptTypeStrById(receipt.getPayType());
+        String remarkText = "收款类型:" + associateType + ",确认时间:"+receipt.getConfirmDate();
+        // 跳转到【普通款项待审核】页面
+        String pagePath = "/pages/relation/ordinary/examine-detail?id="+receipt.getId();
+        for (String openid : openidList) {
+            // sendTemplateMsg(openid, 标题, 金额, 收款日期, 备注, 跳转链接)
+            weChatService.sendTemplateMsg(openid, associateTitle, associateAmount, associateDate, remarkText, pagePath);
+        }
+        return ResponseJson.success("关联收款信息成功!", null);
+    }
+
+    /**
+     * 确认收款-关联供应商退款
+     *
+     * @param receiptOperateDto {
+     *                            id:          收款Id
+     *                            shopOrderId: 关联供应商退款子订单ID
+     *                            }
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public ResponseJson<Void> receiptAssociateRefund(ReceiptOperateDto receiptOperateDto, HttpHeaders headers) {
+        // 检查收款用户权限: 6收款关联供应商退款
+        ReceiptUserVo receiptUser = checkPermissions(6, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(receiptOperateDto.getId());
+        if (null == receipt) {
+            return ResponseJson.error("收款数据异常!", null);
+        }
+        // 只有在待确认 和撤销的状态下才能关联  子订单  不然就是状态出错了
+        if (5 != receipt.getReceiptStatus() && 1 != receipt.getReceiptStatus()) {
+            return ResponseJson.error("收款记录状态异常!", null);
+        }
+        ShopOrderVo shopOrder = receiptMapper.getShopOrderById(receiptOperateDto.getShopOrderId());
+        // 总退款金额
+        Double refundAmount = receiptMapper.getRefundShopAmount(receiptOperateDto.getShopOrderId());
+        // 确认本次收款金额大于已付金额与已退金额的差值,不能关联
+        if (MathUtil.compare(shopOrder.getPayedShopAmount(), MathUtil.add(refundAmount, receipt.getReceiptAmount())) < 0) {
+            return ResponseJson.error("本次确认收款金额大于已付金额与已退金额的差值,不能关联!", null);
+        }
+        //删除之前与这个收款的关联
+        receiptMapper.deleteReceiptRelation(receipt.getId());
+        // ReceiptVo -- copy to --> ReceiptPo
+        String time = DateUtils.getDateTime();
+        ReceiptPo receiptPo = new ReceiptPo();
+        BeanUtils.copyProperties(receipt, receiptPo);
+        receiptPo.setUpdateDate(time);
+        receiptPo.setReceiptType(5);
+        receiptPo.setReceiptStatus(2);
+        receiptPo.setConfirmDate(time);
+        receiptPo.setConfirmUserPermissionId(receiptUser.getId());
+        receiptMapper.updateReceipt(receiptPo);
+        // 保存 收款项和订单关系
+        OrderReceiptRelationPo relation = new OrderReceiptRelationPo();
+        relation.setDelFlag(0);
+        relation.setOrderId(receiptOperateDto.getShopOrderId());
+        relation.setReceiptId(receipt.getId());
+        relation.setRelationType(1);
+        relation.setAssociateAmount(receipt.getReceiptAmount());
+        baseMapper.insertOrderReceiptRelation(relation);
+        /*
+         * 保存供应商退款
+         */
+        RefundShopPo refundShop = new RefundShopPo();
+        refundShop.setOperator(receiptUser.getId());
+        refundShop.setOperateTime(receipt.getConfirmDate());
+        refundShop.setRefundWay(4);
+        refundShop.setShopId(shopOrder.getShopId());
+        refundShop.setRefundAmount(receipt.getReceiptAmount());
+        refundShop.setRefundBalanceAmount(0d);
+        refundShop.setRemark(receipt.getSmsContent());
+        /*
+                退款:1建设银行7297, 2中信银行0897, 3中信银行7172, 4广发银行0115, 5广发银行5461, 6虚拟银行0000
+                付款: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网银
+         */
+        if (1 == receipt.getPayType()) {
+            refundShop.setRefundType(1);
+        } else if (4 == receipt.getPayType() || 5 == receipt.getPayType() || 6 == receipt.getPayType()) {
+            refundShop.setRefundType(2);
+        } else if (3 == receipt.getPayType()) {
+            refundShop.setRefundType(3);
+        } else if (2 == receipt.getPayType()) {
+            refundShop.setRefundType(4);
+        } else if (11 == receipt.getPayType()) {
+            refundShop.setRefundType(5);
+        } else {
+            refundShop.setRefundType(6);
+        }
+        receiptMapper.insertRefundShop(refundShop);
+        //保存供应商退款记录
+        RefundShopRecordPo refundShopRecord = new RefundShopRecordPo();
+        refundShopRecord.setRefundShopId(refundShop.getId());
+        refundShopRecord.setShopId(refundShop.getShopId());
+        refundShopRecord.setShopOrderId(shopOrder.getShopOrderId());
+        refundShopRecord.setShopOrderNo(shopOrder.getShopOrderNo());
+        refundShopRecord.setRefundAmount(receipt.getReceiptAmount());
+        refundShopRecord.setRefundTime(receipt.getConfirmDate());
+        receiptMapper.insertRefundShopRecord(refundShopRecord);
+        return ResponseJson.success("关联供应商退款成功!", null);
+    }
+
+    /**
+     * 确认收款-关联返佣款
+     *
+     * @param receiptOperateDto {
+     *                            id:           收款Id
+     *                            shopOrderIds: 关联返佣子订单IDs
+     *                            rebateRemarks:关联返佣备注
+     *                            }
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public ResponseJson<Void> receiptAssociateRebate(ReceiptOperateDto receiptOperateDto, HttpHeaders headers) {
+        // 检查收款用户权限: 5收款关联返佣
+        ReceiptUserVo receiptUser = checkPermissions(5, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        String[] shopOrderIdArr = receiptOperateDto.getShopOrderIds().split(",");
+        List<Integer> shopIdList = receiptMapper.getShopIdList(shopOrderIdArr);
+        if (shopIdList.size() > 1) {
+            return ResponseJson.error("子订单不属于相同的供应商!", null);
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(receiptOperateDto.getId());
+        if (null == receipt) {
+            return ResponseJson.error("收款数据异常!", null);
+        }
+        // 只有在待确认 和撤销的状态下才能关联  子订单  不然就是状态出错了
+        if (5 != receipt.getReceiptStatus() && 1 != receipt.getReceiptStatus()) {
+            return ResponseJson.error("收款记录状态异常!", null);
+        }
+        //删除之前与这个收款的关联
+        receiptMapper.deleteReceiptRelation(receipt.getId());
+        // ReceiptVo -- copy to --> ReceiptPo
+        String time = DateUtils.getDateTime();
+        ReceiptPo receiptPo = new ReceiptPo();
+        BeanUtils.copyProperties(receipt, receiptPo);
+        receiptPo.setUpdateDate(time);
+        receiptPo.setReceiptType(3);
+        receiptPo.setReceiptStatus(2);
+        receiptPo.setConfirmDate(time);
+        receiptPo.setConfirmUserPermissionId(receiptUser.getId());
+        if (StringUtils.isNotBlank(receiptOperateDto.getRebateRemarks())) {
+            receiptPo.setRebateRemarks(receiptOperateDto.getRebateRemarks());
+        }
+        receiptMapper.updateReceipt(receiptPo);
+        // 保存返佣收款关系和修改订单状态
+        saveRelationAndUpdateOrderStatus(shopOrderIdArr, receipt.getId());
+        return ResponseJson.success("关联返佣款成功!", null);
+    }
+
+    /**
+     * 确认收款-关联口头佣金
+     * crm:/api/user/rebate/verballyReceipt
+     *
+     * @param receiptOperateDto {
+     *                          verbalAmount: 口头返佣佣金
+     *                          shopOrderId: 关联返佣子订单ID
+     *                          rebateRemarks:关联返佣备注
+     *                          }
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public ResponseJson<Integer> associateVerbalRebate(ReceiptOperateDto receiptOperateDto, HttpHeaders headers) {
+        // 检查收款用户权限: 5收款关联返佣
+        ReceiptUserVo receiptUser = checkPermissions(5, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        String[] shopOrderIdArr = receiptOperateDto.getShopOrderId().toString().split(",");
+        List<Integer> shopIdList = receiptMapper.getShopIdList(shopOrderIdArr);
+        String date = DateUtils.getDate("yyyy-MM-dd HH:mm:ss");
+        ReceiptPo receiptPo = new ReceiptPo();
+        receiptPo.setPayWay(2);
+        receiptPo.setDelFlag(0);
+        receiptPo.setPayType(10);
+        receiptPo.setReceiptType(3);
+        receiptPo.setReceiptStatus(2);
+        receiptPo.setReceiptAmount(receiptOperateDto.getVerbalAmount());
+        receiptPo.setConfirmUserPermissionId(receiptUser.getId());
+        receiptPo.setReceiptDate(date);
+        receiptPo.setConfirmDate(date);
+        receiptPo.setRebateRemarks(receiptOperateDto.getRebateRemarks());
+        // 保存收款记录
+        receiptMapper.insertReceipt(receiptPo);
+        // 保存返佣收款关系和修改订单状态
+        saveRelationAndUpdateOrderStatus(shopOrderIdArr, receiptPo.getId());
+        // 添加 返佣欠款记录
+        RebateRecordPo crr = new RebateRecordPo();
+        crr.setType(1);
+        crr.setShopId(shopIdList.get(0));
+        crr.setReceiptId(receiptPo.getId());
+        crr.setRebateAmount(receiptOperateDto.getVerbalAmount());
+        crr.setShopOrderId(receiptOperateDto.getShopOrderIds());
+        crr.setOperateTime(date);
+        receiptMapper.insertRebateRecord(crr);
+        // 更新供应商欠款金额
+        receiptMapper.updateShopRebateAmount(shopIdList.get(0),receiptOperateDto.getVerbalAmount());
+        //返回 款项Id
+        return ResponseJson.success("关联口头返佣款成功!", receiptPo.getId());
+    }
+
+
+    /**
+     * 保存返佣收款关系和修改订单状态
+     * @param shopOrderIdArr 子订单Ids
+     * @param receiptId 款项Id
+     */
+    private void saveRelationAndUpdateOrderStatus(String[] shopOrderIdArr, Integer receiptId) {
+        for (String shopOrderId : shopOrderIdArr) {
+            // 保存 收款项和订单关系
+            OrderReceiptRelationPo relation = new OrderReceiptRelationPo();
+            relation.setDelFlag(0);
+            relation.setOrderId(Integer.valueOf(shopOrderId));
+            relation.setReceiptId(receiptId);
+            relation.setRelationType(1);
+            relation.setAssociateAmount(0d);
+            baseMapper.insertOrderReceiptRelation(relation);
+            //查询这个子订单的主订单下面的所有子订单ID 结果里面包含了 shopOrderId自己的   也就是这个这订单的兄弟子订单
+            List<Integer> shopOrderIdList = receiptMapper.getSubShopOrderList(shopOrderId);
+            //因为最起码已经有一个子订单进行了返佣操作  所以主订单最起码是部分收款付款
+            //如果子订单全部进行了 返佣操作 那么就是全部收付款了
+            boolean flag = true;
+            // 查询已经返佣了的子订单
+            List<Integer> shopOrderIdRebate = receiptMapper.getSubShopOrderRebate(shopOrderIdList);
+            for (Integer soId : shopOrderIdList) {
+                if (!shopOrderIdRebate.contains(soId)) {
+                    //只要有一个子订单没有进行过返佣
+                    //那么这个主订单就是部分的收付款
+                    flag = false;
+                    break;
+                }
+            }
+            receiptMapper.updateShopOrderPayStatus(shopOrderId, 3);
+            // 订单信息
+            OrderVo order = orderCommonMapper.getOrderByShopOrderId(Integer.parseInt(shopOrderId));
+            if (flag) {
+                //子订单全部收到款了
+                order.setReceiptStatus(3);
+                order.setPayStatus(3);
+                if (order.getStatus().toString().length() == 2) {
+                    order.setStatus(Integer.valueOf("3" + order.getStatus().toString().charAt(1)));
+                }
+            } else {
+                //子订单的款还没有收全
+                order.setReceiptStatus(2);
+                order.setPayStatus(2);
+                if (order.getStatus().toString().length() == 2) {
+                    order.setStatus(Integer.valueOf("2" + order.getStatus().toString().charAt(1)));
+                }
+            }
+            order.setUpdateDate(DateUtils.getDateTime());
+            // 更新订单收款状态
+            payOrderMapper.updateOrderStatus(order);
+        }
+    }
+
+
+    /**
+     * 审核收款信息
+     *
+     * @param id            收款Id
+     * @param receiptStatus 收款状态
+     * @param reviewReason  审核不通过原因
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public ResponseJson<Void> receiptAudit(Integer id, Integer receiptStatus, String reviewReason, HttpHeaders headers) {
+        // 检查收款用户权限: 7收款审核
+        ReceiptUserVo receiptUser = checkPermissions(7, headers);
+        if (null == receiptUser) {
+            return ResponseJson.error("无权限操作!", null);
+        }
+        ReceiptVo receipt = receiptMapper.getReceiptDetail(id);
+        if (null == receipt) {
+            return ResponseJson.error("收款数据异常!", null);
+        }
+        // ReceiptVo -- copy to --> ReceiptPo
+        String time = DateUtils.getDateTime();
+        ReceiptPo receiptPo = new ReceiptPo();
+        BeanUtils.copyProperties(receipt, receiptPo);
+        receiptPo.setUpdateDate(time);
+        // 收款项和订单关系
+        List<OrderReceiptRelationPo> listRelation = new ArrayList<>();
+        // 订单款项
+        if (null != receipt.getReceiptType() && 1 == receipt.getReceiptType()){
+            // 只要当收款状态为已确认状态时候才能做审核操作
+            if (3 == receiptStatus && 2 == receipt.getReceiptStatus()) {
+                // 收款项和订单关系
+                listRelation = receiptMapper.getOrderReceiptRelationList(receipt.getId());
+                String statusFlag = "";
+                Integer orderReceiptStatus = null;
+                Double totalAssociateAmount = 0d;
+                for (OrderReceiptRelationPo relationItem : listRelation) {
+                    // 订单信息
+                    OrderVo order = orderCommonMapper.getOrderByOrderId(relationItem.getOrderId());
+                    Double payAbleAmount = order.getPayableAmount();
+                    Double unPayMoney = getUnPayMoney(order, false);
+                    log.info("-----------------UnPayMoney:" + unPayMoney + ", payAbleAmount:" + payAbleAmount);
+                    if (unPayMoney.equals(relationItem.getAssociateAmount())) {
+                        //确认关联(收款金额=订单应付金额)
+                        statusFlag = "3";
+                        orderReceiptStatus = 3;
+                    } else if (relationItem.getAssociateAmount() < unPayMoney) {
+                        /*(收款金额<订单应付金额)*/
+                        statusFlag = "2";
+                        orderReceiptStatus = 2;
+                    } else if (relationItem.getAssociateAmount() > unPayMoney) {
+                        /*(收款金额>订单应付金额)*/
+                        statusFlag = "3";
+                        orderReceiptStatus = 3;
+                    }
+                    //大额退款余额-大额抹平确认-小额抹平确认
+                    if (null != receipt.getConfirmType() && (3 == receipt.getConfirmType() || 2 == receipt.getConfirmType() || 1 == receipt.getConfirmType())) {
+                        // 查询待审核数量
+                        int pendingCount = receiptMapper.getPendingAuditCount(order.getOrderId());
+                        if (pendingCount > 1) {
+                            int i = pendingCount - 1;
+                            String operating;
+                            if (3 == receipt.getConfirmType()) {
+                                operating = "多收退余额";
+                            } else if (2 == receipt.getConfirmType()) {
+                                operating = "多收抹平";
+                            } else {
+                                operating = "少收抹平";
+                            }
+                            // 设置手动回滚事务
+                            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+                            log.info("本条收款进行了" + operating + "操作需要最后审核,请先审核该订单的其他" + i + "条收款");
+                            return ResponseJson.error("本条收款进行了" + operating + "操作需要最后审核,请先审核该订单的其他" + i + "条收款", null);
+                        }
+                        statusFlag = "3";
+                        orderReceiptStatus = 3;
+                        totalAssociateAmount += relationItem.getAssociateAmount();
+                    }
+                    statusFlag = statusFlag + order.getStatus().toString().charAt(1);
+                    order.setStatus(Integer.parseInt(statusFlag));
+                    order.setReceiptStatus(orderReceiptStatus);
+                    //设置订单状态
+                    //获取主订单下面的所有的确认收货商品数量
+                    Integer receiptNum = receiptMapper.getReceiptNumByOrderId(order.getOrderId());
+                    Integer returnAndCancelNum = 0;
+                    //获取该主订单下面所有已完成状态退货退款商品数量
+                    if (null != order.getRefundType() && (1 == order.getRefundType() || 2 == order.getRefundType())) {
+                        returnAndCancelNum = receiptMapper.getReturnAndCancelNum(order.getOrderId());
+                    }
+                    //确认收货商品数量+已完成状态退货退款商品数量 = 订单商品总数
+                    if (order.getProductCount().equals(returnAndCancelNum + receiptNum)) {
+                        //已收款--->交易完成
+                        //已收款+已付款--->订单完成
+                        if (3 == order.getReceiptStatus()) {
+                            order.setStatus(OrderStatus.TRANSACTION_COMPLETED.getCode());
+                        }
+                        if (3 == order.getReceiptStatus() && 3 == order.getPayStatus()) {
+                            order.setStatus(OrderStatus.ORDER_COMPLETED.getCode());
+                        }
+                    }
+                    // 更新订单收款状态
+                    payOrderMapper.updateOrderStatus(order);
+                    log.info("-------------审核通过订单状态改变---orderId:" + order.getOrderId() + "--status:" + order.getStatus());
+                    //判断是否有充值商品,充值到余额
+                    Integer rechargeGoods = null;
+                    //缴纳订金订单
+                    int[] productId1 = {6060, 6061, 6062, 6063, 6064};
+                    //充值余额订单
+                    int[] productId2 = {6065, 6066, 6067, 6068, 6069};
+                    List<OrderProductVo> orderProductList = orderCommonMapper.getOrderProductByOrderId(order.getOrderId());
+                    for (OrderProductVo orderProduct : orderProductList) {
+                        if (ArrayUtils.contains(productId1, orderProduct.getProductId())) {
+                            rechargeGoods = 1;
+                            break;
+                        }
+                        if (ArrayUtils.contains(productId2, orderProduct.getProductId())) {
+                            rechargeGoods = 2;
+                            break;
+                        }
+                    }
+                    // 充值与订金订单
+                    if (rechargeGoods != null) {
+                        Double userMoney = baseMapper.getUserMoney(order.getUserId());
+                        Double ableUserMoney = baseMapper.getAbleUserMoney(order.getUserId());
+                        userMoney = MathUtil.add(userMoney, relationItem.getAssociateAmount()).doubleValue();
+                        ableUserMoney = MathUtil.add(ableUserMoney, relationItem.getAssociateAmount()).doubleValue();
+                        //保存余额到余额收支记录
+                        BalanceRecordPo balanceRecord = new BalanceRecordPo();
+                        balanceRecord.setUserId(order.getUserId());
+                        balanceRecord.setType(1);
+                        if (rechargeGoods == 1) {
+                            balanceRecord.setBalanceType(6);
+                        } else {
+                            balanceRecord.setBalanceType(7);
+                        }
+                        balanceRecord.setAddDate(new Date());
+                        balanceRecord.setAmount(relationItem.getAssociateAmount());
+                        balanceRecord.setOrderId(order.getOrderId());
+                        balanceRecord.setReceiptId(receipt.getId());
+                        balanceRecord.setRemark("订单商品充值余额");
+                        balanceRecord.setDelFlag(0);
+                        // 保存 余额收支记录
+                        baseMapper.insertBalanceRecord(balanceRecord);
+                        baseMapper.updateUserMoney(userMoney, ableUserMoney, order.getUserId());
+                        log.info("订单商品充值余额>>>>>>>>>>>>>" + relationItem.getAssociateAmount());
+                    }
+                    if (null != orderReceiptStatus && 3 == orderReceiptStatus) {
+                        // 已收款赠送采美豆自主下单赠送采美豆
+                        if (1 == order.getOrderType()) {
+                            UserBeansHistoryPo beansHistory = new UserBeansHistoryPo();
+                            beansHistory.setUserId(order.getUserId());
+                            beansHistory.setOrderId(order.getOrderId());
+                            beansHistory.setBeansType(5);
+                            beansHistory.setType(1);
+                            beansHistory.setPushStatus(0);
+                            beansHistory.setAddTime(new Date());
+                            beansHistory.setDelFlag(0);
+                            int num;
+                            if (MathUtil.compare(1000, order.getPayTotalFee()) > -1) {
+                                num = 0;
+                            } else if (MathUtil.compare(5000, order.getPayTotalFee()) > -1) {
+                                num = 1000;
+                            } else if (MathUtil.compare(10000, order.getPayTotalFee()) > -1) {
+                                num = 2500;
+                            } else if (MathUtil.compare(25000, order.getPayTotalFee()) > -1) {
+                                num = 5000;
+                            } else if (MathUtil.compare(100000, order.getPayTotalFee()) > -1) {
+                                num = 7500;
+                            } else if (MathUtil.compare(250000, order.getPayTotalFee()) > -1) {
+                                num = 10000;
+                            } else if (MathUtil.compare(500000, order.getPayTotalFee()) > -1) {
+                                num = 12500;
+                            } else {
+                                num = 20000;
+                            }
+                            if (num > 0) {
+                                beansHistory.setNum(num);
+                                // 保存 采美豆使用记录
+                                baseMapper.insertBeansHistory(beansHistory);
+                                Integer userBeans = baseMapper.getUserBeans(order.getUserId());
+                                userBeans += num;
+                                baseMapper.updateUserBeans(order.getUserId(), userBeans);
+                                log.info("【线下收款】>>>>>更新用户采美豆(update[user(userBeans:" + userBeans + ")]),userId:" + beansHistory.getUserId());
+
+                            }
+                        }
+                    }
+                }
+
+                if (null != receipt.getConfirmType() && 3 == receipt.getConfirmType()) {
+                    Double money = receipt.getReceiptAmount() - totalAssociateAmount;
+                    // 订单信息
+                    OrderVo order = orderCommonMapper.getOrderByOrderId(listRelation.get(0).getOrderId());
+                    Double userMoney = baseMapper.getUserMoney(order.getUserId());
+                    Double ableUserMoney = baseMapper.getAbleUserMoney(order.getUserId());
+                    log.info("----------------------->原余额:" + userMoney + "==原可用余额:" + ableUserMoney);
+                    // 减操作不更新可用余额
+                    if (money < 0d) {
+                        if (0 != order.getStatus()) {
+                            userMoney = MathUtil.add(userMoney, money).doubleValue();
+                        }
+                    } else {
+                        // 待确认状态不需要修改余额,因为本身存在冻结余额不需要重复退回
+                        if (0 != order.getStatus()) {
+                            userMoney = MathUtil.add(ableUserMoney, money).doubleValue();
+                        }
+                        ableUserMoney = MathUtil.add(ableUserMoney, money).doubleValue();
+                    }
+                    log.info("大额抹平金额退回账户:" + order.getUserId() + ",------->大额抹平金额:" + money);
+                    //保存余额到余额收支记录
+                    BalanceRecordPo balanceRecord = new BalanceRecordPo();
+                    balanceRecord.setUserId(order.getUserId());
+                    balanceRecord.setType(1);
+                    balanceRecord.setBalanceType(2);
+                    balanceRecord.setAddDate(new Date());
+                    balanceRecord.setAmount(money);
+                    balanceRecord.setOrderId(order.getOrderId());
+                    balanceRecord.setReceiptId(receipt.getId());
+                    balanceRecord.setRemark("订单商品充值余额");
+                    balanceRecord.setDelFlag(0);
+                    // 保存 余额收支记录
+                    baseMapper.insertBalanceRecord(balanceRecord);
+                    baseMapper.updateUserMoney(userMoney, ableUserMoney, order.getUserId());
+                    log.info("大额抹平金额退回账户>>>>>>>>>>>>>" + money);
+                }
+            } else if (4 == receiptStatus && 2 == receipt.getReceiptStatus()) {
+                //审核不通过
+                receiptPo.setReviewReason(reviewReason);
+            } else {
+                // 设置手动回滚事务
+                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+                log.info("订单不是已确认状态 或者 审核结果不正确");
+                return ResponseJson.error("审核信息有误!", null);
+            }
+        }
+        receiptPo.setReviewUserPermissionId(receiptUser.getId());
+        receiptPo.setReceiptStatus(receiptStatus);
+        receiptPo.setReviewDate(time);
+        receiptMapper.updateReceipt(receiptPo);
+
+        // 审核未通过 推送模板消息给指定人员
+        if (4 == receiptStatus) {
+            // 审核未通过
+            String title = "订单审核未通过,请重新确认审核!";
+            String date = receipt.getReceiptDate();
+            String receiptTypeStr = receiptMapper.getReceiptTypeStrById(receipt.getPayType());
+            String remarkText = "收款类型:"+ receiptTypeStr +";"+"审核时间:"+receiptPo.getReviewDate();
+            double associateAmount = receiptMapper.countAssociateAmountById(receipt.getId());
+            // (用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核)
+            List<String> openidList = receiptMapper.getOpenidListByPermission(Collections.singletonList(4));
+            openidList.removeIf(Objects::isNull);
+            // 跳转到【普通款项待审核】页面
+            String pagePath = "/pages/relation/ordinary/examine-detail?id="+receipt.getId();
+            for (String openid : openidList) {
+                // sendTemplateMsg(openid, 标题, 金额, 收款日期, 备注, 跳转链接)
+                weChatService.sendTemplateMsg(openid, title, associateAmount, date, remarkText, pagePath);
+            }
+        }
+        return ResponseJson.success("审核收款信息成功!", null);
+    }
+
+    /**
+     * 微信收款信息推送(待确认超时)
+     */
+    @Override
+    public void sendWechatMsgConfirmTimeout() {
+        //待确认超时
+        ReceiptParamsBo paramsBo = new ReceiptParamsBo();
+        String[] receiptStatusArr = "1".split(",");
+        paramsBo.setReceiptStatusArr(receiptStatusArr);
+        paramsBo.setStartDate("2022-01-01 00:00:00");
+        List<ReceiptVo> list = receiptMapper.getReceiptList(paramsBo);
+        list.removeIf(Objects::isNull);
+        // (用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核)
+        List<String> openidList = receiptMapper.getOpenidListByPermission(Arrays.asList(4,5,6));
+        openidList.removeIf(Objects::isNull);
+        String title = "超时未确认,请及时查看详情并确认关联!";
+        String pagePath = "";
+        for (ReceiptVo receipt : list) {
+            // 收款款项类型:1订单款,2非订单款,3返佣款,5供应商退款
+            Integer receiptType = receipt.getReceiptType();
+            if (null != receiptType && 1 == receiptType) {
+                // 未关联普通款项
+                pagePath = "/pages/relation/ordinary/index?id="+receipt.getId();
+            }
+            if (null != receiptType && 3 == receiptType) {
+                // 未关联返佣款项
+                pagePath = "/pages/relation/return/index?id="+receipt.getId();
+            }
+            if (null != receiptType && 5 == receiptType) {
+                // 未关联供应商退款
+                pagePath = "/pages/relation/refund/index?id="+receipt.getId();
+            }
+            double amount = receipt.getReceiptAmount();
+            String date = receipt.getReceiptDate();
+            String receiptTypeStr = receiptMapper.getReceiptTypeStrById(receipt.getPayType());
+            String remarkText = "收款类型:" + receiptTypeStr;
+            for (String openid : openidList) {
+                // sendTemplateMsg(openid, 标题, 金额, 收款日期, 备注, 跳转链接)
+                weChatService.sendTemplateMsg(openid, title, amount, date, remarkText, pagePath);
+            }
+        }
+    }
+
+    /**
+     * 微信收款信息推送(待审核超时)
+     */
+    @Override
+    public void sendWechatMsgAuditTimeout() {
+        //待审核超时
+        ReceiptParamsBo paramsBo = new ReceiptParamsBo();
+        String[] receiptStatusArr = "2".split(",");
+        paramsBo.setReceiptStatusArr(receiptStatusArr);
+        paramsBo.setStartDate("2022-01-01 00:00:00");
+        List<ReceiptVo> list = receiptMapper.getReceiptList(paramsBo);
+        list.removeIf(Objects::isNull);
+        // (用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核)
+        List<String> openidList = receiptMapper.getOpenidListByPermission(Collections.singletonList(7));
+        openidList.removeIf(Objects::isNull);
+        String title = "超时未审核,请及时查看详情并审核!";
+        for (ReceiptVo receipt : list) {
+            double amount = receiptMapper.countAssociateAmountById(receipt.getId());
+            String date = receipt.getConfirmDate();
+            String receiptTypeStr = receiptMapper.getReceiptTypeStrById(receipt.getPayType());
+            String remarkText = "收款类型:" + receiptTypeStr + ",确认时间:"+receipt.getConfirmDate();
+            // 跳转到【普通款项待审核】页面
+            String pagePath = "/pages/relation/ordinary/examine-detail?id="+receipt.getId();
+            for (String openid : openidList) {
+                // sendTemplateMsg(openid, 标题, 金额, 收款日期, 备注, 跳转链接)
+                weChatService.sendTemplateMsg(openid, title, amount, date, remarkText, pagePath);
+            }
+        }
+    }
+
+    /**
+     * 设置订单的款项信息
+     */
+    private void setOrderAmountInfo(OrderVo order) {
+        String userName = baseMapper.getUserNameByUserId(order.getUserId());
+        order.setClubName(userName);
+        order.setPaidAmount(getReceivedAmount(order, true));
+        order.setStatusName(OrderStatus.getOrderStatusText(order.getStatus()));
+        order.setUnCheckAmount(receiptMapper.getUncheckAmount(order.getOrderId()));
+        //获取退款价值
+        order.setReturnValue(getReturnValue(order));
+        //退款(退货)总金额
+        Double returnedPurchaseFee = 0d;
+        //退款总额(给买家)
+        Double refundFee = 0d;
+        //获取已退款金额
+        List<ReturnedPurchaseVo> returnedList = receiptMapper.getRefundFeeByOrderId(order.getOrderId());
+        if (null != returnedList && returnedList.size() > 0) {
+            for (ReturnedPurchaseVo returned : returnedList) {
+                returnedPurchaseFee += returned.getReturnedPurchaseFee();
+                refundFee += returned.getRefundFee();
+            }
+        }
+        order.setReturnedPurchaseFee(returnedPurchaseFee);
+        order.setRefundFee(refundFee);
+        //获取剩余应收
+        order.setSurplusAmount(getUnPayMoney(order, true));
+    }
+
+    /**
+     * 设置收款金额
+     */
+    public void setReceiptedAmount(ShopOrderVo shopOrder) {
+        OrderVo order = orderCommonMapper.getOrderByOrderId(shopOrder.getOrderId());
+        if (null != order.getRebateFlag() && 1 == order.getRebateFlag()) {
+            // 返佣订单
+            double needPayAmount = receiptMapper.countNeedPayAmount(order.getOrderId());
+            order.setReceiptAmount(needPayAmount);
+            // 佣金计算
+            BigDecimal brokerage = BigDecimal.ZERO;
+            List<OrderProductVo> orderProductList = orderCommonMapper.getShopOrderProduct(shopOrder.getShopOrderId());
+            orderProductList.removeIf(Objects::isNull);
+            for (OrderProductVo orderProduct : orderProductList) {
+                // 已退货数量
+                Integer returnedNum = orderCommonMapper.countReturnedNum(orderProduct.getShopOrderId(), orderProduct.getProductId());
+                returnedNum = null != returnedNum ? returnedNum : 0;
+                // 赠品数量
+                int presentNum = orderProduct.getPresentNum() == null ? 0 : orderProduct.getPresentNum();
+                // 折后单价
+                double discountPrice = orderProduct.getDiscountPrice() == null ? 0d : orderProduct.getDiscountPrice();
+                // 单个税费
+                double tax = orderProduct.getAddedValueTax() == null ? 0d : orderProduct.getAddedValueTax();
+                // 成本
+                double costPrice = orderProduct.getCostPrice() == null ? 0d : orderProduct.getCostPrice();
+                // 单个付供应商税费
+                double costTax = orderProduct.getSingleShouldPayTotalTax() == null ? 0d : orderProduct.getSingleShouldPayTotalTax();
+                // (num + presentNum - returnedNum) * ((discountPrice + tax) - (costPrice + costTax));
+                int num = orderProduct.getNum() + presentNum - returnedNum;
+                BigDecimal price = MathUtil.sub(MathUtil.add(discountPrice, tax), MathUtil.add(costPrice, costTax));
+                brokerage = MathUtil.add(brokerage, MathUtil.mul(price, num));
+            }
+            BigDecimal otherFee = MathUtil.add(order.getDiscountFee(), MathUtil.add(shopOrder.getShopOtherFee(), shopOrder.getShopPostFee()));
+            brokerage = MathUtil.sub(brokerage, otherFee);
+            shopOrder.setBrokerage(brokerage.doubleValue());
+        } else {
+            double associateAmount = receiptMapper.countAllPayAssociateAmount(order.getOrderId());
+            order.setReceiptAmount(associateAmount);
+        }
+        shopOrder.setMainOrder(order);
+    }
+
+    /**
+     * 设置收款详情信息
+     */
+    private void setReceiptDetail(ReceiptVo receipt) {
+        if (1 == receipt.getReceiptStatus()) {
+            String time = receipt.getReceiptDate();
+            long diff = DateUtils.pastHour(DateUtils.parseDate(time));
+            if (diff > 12) {
+                receipt.setTipMsg("无人确认");
+            }
+        } else if (2 == receipt.getReceiptStatus()) {
+            String time = receipt.getConfirmDate();
+            long diff = DateUtils.pastHour(DateUtils.parseDate(time));
+            if (diff > 12) {
+                receipt.setTipMsg("无人审核");
+            }
+        }
+        if (2 == receipt.getReceiptStatus() && 1 == receipt.getReceiptType()) {
+            String userName = baseMapper.getUserNameByUserId(receipt.getUserId());
+            receipt.setUserName(userName);
+        }
+        if (null != receipt.getReceiptUserPermissionId()) {
+            String receiptUserName = receiptMapper.getReceiptUserNameById(receipt.getReceiptUserPermissionId());
+            receipt.setReceiptUserName(receiptUserName);
+        }
+        if (null != receipt.getConfirmUserPermissionId()) {
+            String confirmUserName = receiptMapper.getReceiptUserNameById(receipt.getConfirmUserPermissionId());
+            receipt.setConfirmUserName(confirmUserName);
+        }
+        if (null != receipt.getReviewUserPermissionId()) {
+            String reviewUserName = receiptMapper.getReceiptUserNameById(receipt.getReviewUserPermissionId());
+            receipt.setReviewUserName(reviewUserName);
+        }
+        ReceiptTypeVo type = receiptMapper.getReceiptTypeById(receipt.getPayType());
+        receipt.setPayTypeText(type.getType());
+        receipt.setReceiptTypeText(ReceiptType.getTypeDesc(receipt.getReceiptType()));
+        receipt.setReceiptStatusText(ReceiptStatus.getTypeDesc(receipt.getReceiptStatus(), receipt.getReceiptType()));
+    }
+
+    /**
+     * 获取该收款用户下的机构userIds
+     *
+     * @param openid        微信openid
+     * @param receiptStatus 收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【线上支付成功为审核通过】
+     */
+    private List<Integer> getReceiptClubUserIds(String openid, Integer receiptStatus) {
+        // 收款用户
+        ReceiptUserVo receiptUser = receiptMapper.getReceiptUserByOpenid(openid);
+        List<Integer> userIds = null;
+        Integer userType = null;
+        String logTxt = "";
+        if (null != receiptUser) {
+            //用户类型:1协销人员,2客服,3财务,4超级管理员
+            userType = receiptUser.getUserType();
+            if (1 == userType) {
+                // 协销人员
+                Integer spId = receiptMapper.getServiceProviderId(receiptUser.getUnionId());
+                userIds = receiptMapper.getClubUserIdBySpId(spId);
+                logTxt = "协销";
+            } else if (2 == userType) {
+                // 客服,默认协销id:1342
+                userIds = receiptMapper.getClubUserIdBySpId(1342);
+                Integer spId = receiptMapper.getServiceProviderId(receiptUser.getUnionId());
+                if (null != spId && spId > 0) {
+                    userIds.addAll(receiptMapper.getClubUserIdBySpId(1342));
+                }
+                logTxt = "客服";
+            }
+        }
+        if (null == userIds && null != userType && 4 != userType) {
+            userIds = new ArrayList<>();
+            userIds.add(0);
+        }
+        if (null != userType && 2 == userType && null != receiptStatus && 2 == receiptStatus) {
+            // 客服可以查看所有待审核收款
+            userIds = null;
+        }
+        log.info("用户身份:" + logTxt + ">>>用户类型:" + userType + ">>>userIds:" + userIds);
+        return userIds;
+    }
+
+    /**
+     * 检查收款权限
+     * 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+     *
+     * @param permission 0:登录用户不需要权限,其他具体权限
+     * @param headers    HttpHeaders
+     * @return null:无权限/权限用户ReceiptUserVo
+     */
+    private ReceiptUserVo checkPermissions(Integer permission, HttpHeaders headers) {
+        // 用户openid
+        String openid = headers.getFirst("x-openid");
+        if (StringUtils.isNotBlank(openid)) {
+            // 收款用户
+            ReceiptUserVo receiptUser = receiptMapper.getReceiptUserByOpenid(openid);
+            if (null != receiptUser) {
+                // 获取用户权限: 1收款列表访问,2收款详情访问,3收款录入,4收款关联订单,5收款关联返佣,6收款关联供应商退款,7收款审核
+                List<Integer> permissions = receiptMapper.getPermissionsByUserId(receiptUser.getId());
+                if (0 == permission || permissions.contains(permission)) {
+                    receiptUser.setPermissions(permissions);
+                    return receiptUser;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 识别收款短信
+     * 以下方法按照短信采样结果实现(新增短信不兼容)
+     *
+     * @param message 收款短信
+     *                贵公司尾号7297的账户5月23日15时30分往来款收入人民币1800.00元,余额7055.50元。对方户名:哈尔滨爱丽达医疗美容管理有限公司爱建医疗美容诊所。[建设银行]
+     *                【广发银行】尊敬的客户,贵司账号后四位0115 的账户于08日13:55 收入一笔方芳尾数为8067的账户转账1850.00元,手续费0.00元。账户余额:1000000.00元。
+     *                【广发银行】您末四位为0115的企业账户于12日16:09收入人民币5000.00元(深圳同城)。账户余额:8907.21元。
+     *                【中信银行】贵公司尾号7172的中信账号于03月15日17:25,二代支付存入人民币3132.30元,当前余额为人民币4373.07元。
+     *                【中信银行】您尾号0897的中信卡于03月06日11:44,存入人民币2358.00元,当前余额为人民币1000000.00元。
+     *                【中信银行】您尾号0897的中信卡于03月19日13:37,财付通快捷支付存入人民币11348.45元,当前余额为人民币1000000.00元。
+     *                【中信银行】您尾号0897的中信卡于03月14日16:01,支付宝存入人民币5994.06元,当前余额为人民币1000000.00元。
+     *                【中信银行】您尾号0897的中信卡于05月29日14:42,二代支付存入人民币10000.00元,当前余额为人民币13871.67元。
+     */
+    private ResponseJson<ReceiptVo> identifyMessage(String message) throws Exception {
+        if (message.contains("银行")) {
+            // 隐藏余额后去数据库进行全匹配
+            message = hiddenBalance(message);
+            int index;
+            // 查询数据库,是否存在短信记录,存在就直接读取数据并返回
+            String md5Msg = Md5Util.md5(message);
+            ReceiptVo receiptVo = getReceiptByMd5Msg(md5Msg);
+            if (receiptVo != null) {
+                return ResponseJson.success(receiptVo);
+            }
+            // 数据库不存在短信记录,执行下面的操作
+            /*识别收款类别*/
+            index = message.indexOf("银行");
+            String receivableDesc = message.substring(index - 2, index + 2);
+            if (receivableDesc.contains("中信银行")) {
+                index = message.indexOf("尾号");
+                String account = message.substring(index + 2, index + 2 + 4);
+                receivableDesc = receivableDesc + "-" + account;
+                if (message.contains("财付通")) {
+                    receivableDesc += "-财付通";
+                }
+                if (message.contains("支付宝")) {
+                    receivableDesc += "-支付宝";
+                }
+            } else if (receivableDesc.contains("广发银行") || receivableDesc.contains("建设银行")) {
+                // 匹配的第一组数字为银行卡尾号
+                Matcher matcherBank = PATTERN_NUMBER.matcher(message);
+                if (matcherBank.find()) {
+                    receivableDesc = receivableDesc + "-" + matcherBank.group();
+                }
+            }
+            Integer typeId = receiptMapper.getReceivablesTypeId(receivableDesc);
+
+            /*识别收款时间*/
+            String time;
+            String year = DateUtils.getYear();
+            String month = DateUtils.getMonth();
+            List<String> contentTime = new ArrayList();
+            Matcher matcher = PATTERN_NUMBER.matcher(message);
+            while (matcher.find()) {
+                String group = matcher.group();
+                contentTime.add(group);
+            }
+            // 建设银行
+            if (Arrays.asList(1, 18, 19).contains(typeId)) {
+                time = year + "-" + contentTime.get(1) + "-" + contentTime.get(2) + " " + contentTime.get(3) + ":" + contentTime.get(4) + ":" + "00";
+                int difference = DateUtils.differentDays(time);
+                if (difference < 0) {
+                    time = Integer.parseInt(year) - 1 + "-" + contentTime.get(1) + "-" + contentTime.get(2) + " " + contentTime.get(3) + ":" + contentTime.get(4) + ":" + "00";
+                }
+            }
+            // 广发银行
+            else if (Arrays.asList(2, 11).contains(typeId)) {
+                time = year + "-" + month + "-" + contentTime.get(1) + " " + contentTime.get(2) + ":" + "00";
+                int difference = DateUtils.differentDays(time);
+                /* 差距大于30天 减一年, 小于30天 减一个月 */
+                if (difference < -30) {
+                    time = Integer.parseInt(year) - 1 + "-" + month + "-" + contentTime.get(1) + " " + contentTime.get(2) + ":" + contentTime.get(3) + ":" + "00";
+                } else if (difference < 0) {
+                    time = DateUtils.format(DateUtils.addMonth(DateUtils.parseDate(time), -1), "yyyy-MM-dd HH:mm:ss");
+                }
+            } else {
+                time = year + "-" + contentTime.get(1) + "-" + contentTime.get(2) + " " + contentTime.get(3) + ":" + "00";
+                int difference = DateUtils.differentDays(time);
+                if (difference < 0) {
+                    time = Integer.parseInt(year) - 1 + "-" + contentTime.get(1) + "-" + contentTime.get(2) + " " + contentTime.get(3) + ":" + "00";
+                }
+            }
+
+            /*识别收款金额*/
+            double money = 0d;
+            if (typeId == 2) {
+                // 广发银行
+                if (message.contains("入人民币")) {
+                    index = message.indexOf("入人民币");
+                    int index2 = message.indexOf("元", index);
+                    money = Double.parseDouble(message.substring(index + 4, index2));
+                } else if (message.contains("账户转账")) {
+                    index = message.indexOf("账户转账");
+                    int index2 = message.indexOf("元", index);
+                    money = Double.parseDouble(message.substring(index + 4, index2));
+                }
+            } else {
+                if (message.contains("入人民币")) {
+                    index = message.indexOf("入人民币");
+                    int index2 = message.indexOf("元", index);
+                    money = Double.parseDouble(message.substring(index + 4, index2));
+                } else {
+                    throw new Exception("短信识别错误!");
+                }
+            }
+
+            /*识别手续费*/
+            boolean tipFeeFlag = false;
+            double tipFee = 0d;
+            if (TIP_FEE_TYPE.contains(typeId)) {
+                tipFeeFlag = true;
+                tipFee = MathUtil.mul(money, MathUtil.div(1, 1000), 2).doubleValue();
+                // 收款金额 = 短信收款金额+手续费
+                money = MathUtil.add(money, tipFee).doubleValue();
+            }
+
+            /*识别订单标识*/
+            String orderFlag = "";
+            Matcher matcherOrder = PATTERN_ORDER.matcher(message);
+            if (matcherOrder.find()) {
+                orderFlag = matcherOrder.group(1);
+            }
+
+            // 线下收款
+            ReceiptVo receipt = new ReceiptVo();
+            receipt.setPayWay(2);
+            receipt.setPayType(typeId);
+            receipt.setReceiptDate(time);
+            receipt.setReceiptAmount(money);
+            receipt.setHaveFee(tipFeeFlag);
+            receipt.setHandlingFee(tipFee);
+            receipt.setOrderFlag(orderFlag);
+            receipt.setSmsMd5Code(md5Msg);
+            receipt.setSmsContent(message);
+
+            return ResponseJson.success(receipt);
+        }
+        return ResponseJson.error("短信识别错误!", null);
+    }
+
+    /**
+     * 隐藏短信余额
+     */
+    private String hiddenBalance(String message) {
+        message = message.replace(":", ":").trim();
+        int index = message.indexOf("余额");
+        if (index != -1) {
+            int index2 = message.indexOf("元", index);
+            String text1 = message.substring(0, index + 2);
+            String text2 = message.substring(index2);
+            message = text1 + "***" + text2;
+        }
+        return message;
+    }
+
+    /**
+     * 查询数据库,是否存在短信记录,存在就直接读取数据并返回
+     */
+    private ReceiptVo getReceiptByMd5Msg(String md5Msg) {
+        List<ReceiptVo> receiptList = receiptMapper.getReceiptBySmsMd5Code(md5Msg);
+        if (!CollectionUtils.isEmpty(receiptList)) {
+            receiptList.get(0).setHaveFee(TIP_FEE_TYPE.contains(receiptList.get(0).getPayType()));
+            return receiptList.get(0);
+        }
+        return null;
+    }
+
+    /**
+     * 获取未支付金额
+     *
+     * @param order 订单
+     * @param flag  (true:计算未审核金额,false:不计算)
+     */
+    private Double getUnPayMoney(OrderVo order, Boolean flag) {
+        Double unPayMoney = order.getPayableAmount();
+        Double receivedAmount = getReceivedAmount(order, flag);
+        Double returnValue = getReturnValue(order);
+        unPayMoney = MathUtil.sub(unPayMoney, MathUtil.add(receivedAmount, returnValue)).doubleValue();
+        log.info("------------------剩余应收:" + (unPayMoney >= 0d ? unPayMoney : 0d));
+        return unPayMoney >= 0d ? unPayMoney : 0d;
+    }
+
+    /**
+     * 计算已收金额(审核通过金额+(待审核金额))//展示的时候需要加待审核金额  实际计算不需要计算
+     *
+     * @param order                订单
+     * @param isCountUncheckAmount 是否计算未审核金额
+     */
+    private Double getReceivedAmount(OrderVo order, Boolean isCountUncheckAmount) {
+        Double receivedAmount = 0d;
+        if (null != order) {
+            // 获取订单已支付金额(待审核状态的收款也算)
+            Double paidAmount = receiptMapper.getPaidAmountByOrderId(order.getOrderId());
+            receivedAmount += paidAmount;
+            if (!isCountUncheckAmount) {
+                // 获取订单待审核金额
+                Double unCheckAmount = receiptMapper.getUncheckAmount(order.getOrderId());
+                receivedAmount -= unCheckAmount;
+            }
+            if (!isCountUncheckAmount && 1 == order.getReceiptStatus()) {
+                receivedAmount = 0d;
+            }
+            if (receivedAmount < 0d) {
+                receivedAmount = 0d;
+            }
+            if (receivedAmount > order.getPayableAmount()) {
+                receivedAmount = order.getPayableAmount();
+            }
+        }
+        return receivedAmount;
+    }
+
+    /**
+     * 获取退货退款价值(需计算经理折扣存在的情况)(此金额不是指退给用户的金额)
+     *
+     * @param order 订单
+     */
+    private Double getReturnValue(OrderVo order) {
+        // 获取订单已退金额
+        Double returnedAmount = receiptMapper.getReturnedAmount(order.getOrderId());
+        // 经理折扣
+        Double discountFee = null != order.getDiscountFee() ? order.getDiscountFee() : 0d ;
+        // 如果经理折扣大于退款(退货)总金额之和  那么先抵扣经理折扣  退款价值为0
+        if (discountFee >= returnedAmount) {
+            returnedAmount = 0d;
+        } else {
+            // 抵用完经理折扣后才是真实价值
+            returnedAmount = returnedAmount - discountFee;
+        }
+        return returnedAmount;
+    }
+
+}

+ 38 - 0
src/main/java/com/caimei365/order/task/ReceiptMsgTask.java

@@ -0,0 +1,38 @@
+package com.caimei365.order.task;
+
+import com.caimei365.order.service.ReceiptService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 线下收款推送模板消息
+ *
+ * @author : Charles
+ * @date : 2022/2/25
+ */
+@Slf4j
+@Component
+@EnableScheduling
+public class ReceiptMsgTask {
+    @Resource
+    private ReceiptService receiptService;
+
+    /**
+     * 微信收款信息推送
+     * 每天九点钟推送
+     */
+    @Scheduled(cron = "0 0 09 * * ?")
+    public void sendWechatMsgTask() {
+        log.info("-------->微信收款信息定时推送--start<--------------");
+        // 待确认超时
+        receiptService.sendWechatMsgConfirmTimeout();
+        // 待审核超时
+        receiptService.sendWechatMsgAuditTimeout();
+        log.info("-------->微信收款信息定时推送--end<--------------");
+    }
+
+}

+ 785 - 0
src/main/java/com/caimei365/order/utils/DateUtils.java

@@ -0,0 +1,785 @@
+package com.caimei365.order.utils;
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+/**
+ * 日期工具类, 继承org.apache.commons.lang3.time.DateUtils类
+ * @author LG
+ * @date  2016年3月22日
+ * @version 1.0
+ */
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
+
+
+	//fixme lwt  已经与www的 DateUtils 整合
+	/**
+	 * 英文简写(默认)如:2010-12-01
+	 */
+	public static String FORMAT_SHORT = "yyyy-MM-dd";
+
+	/**
+	 * 得到当前日期字符串 格式(yyyy-MM-dd)
+	 */
+	public static String getDate() {
+		return getDate("yyyy-MM-dd");
+	}
+
+	private static String[] parsePatterns = {
+			"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
+			"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
+			"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+	/**
+	 * 得到当前日期字符串 格式(yyyy-MM-dd) pattern可以为:"yyyy-MM-dd" "HH:mm:ss" "E"
+	 */
+	public static String getDate(String pattern) {
+		return DateFormatUtils.format(new Date(), pattern);
+	}
+
+	/**
+	 * 得到日期字符串 默认格式(yyyy-MM-dd) pattern可以为:"yyyy-MM-dd" "HH:mm:ss" "E"
+	 */
+	public static String formatDate(Date date, Object... pattern) {
+		String formatDate = null;
+		if (pattern != null && pattern.length > 0) {
+			formatDate = DateFormatUtils.format(date, pattern[0].toString());
+		} else {
+			formatDate = DateFormatUtils.format(date, "yyyy-MM-dd");
+		}
+		return formatDate;
+	}
+
+	/**
+	 * 得到日期时间字符串,转换格式(yyyy-MM-dd HH:mm:ss)
+	 */
+	public static String formatDateTime(Date date) {
+		return formatDate(date, "yyyy-MM-dd HH:mm:ss");
+	}
+
+	/**
+	 * 得到当前时间字符串 格式(HH:mm:ss)
+	 */
+	public static String getTime() {
+		return formatDate(new Date(), "HH:mm:ss");
+	}
+
+	/**
+	 * 得到当前日期和时间字符串 格式(yyyy-MM-dd HH:mm:ss)
+	 */
+	public static String getDateTime() {
+		return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
+	}
+
+	/**
+	 * 得到当前年份字符串 格式(yyyy)
+	 */
+	public static String getYear() {
+		return formatDate(new Date(), "yyyy");
+	}
+
+	/**
+	 * 得到当前月份字符串 格式(MM)
+	 */
+	public static String getMonth() {
+		return formatDate(new Date(), "MM");
+	}
+
+	/**
+	 * 得到当天字符串 格式(dd)
+	 */
+	public static String getDay() {
+		return formatDate(new Date(), "dd");
+	}
+
+	/**
+	 * 得到当前星期字符串 格式(E)星期几
+	 */
+	public static String getWeek() {
+		return formatDate(new Date(), "E");
+	}
+
+	/**
+	 * 日期型字符串转化为日期 格式
+	 * { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm",
+	 *   "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm",
+	 *   "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm" }
+	 */
+	public static Date parseDate(Object str) {
+		if (str == null){
+			return null;
+		}
+		try {
+			return parseDate(str.toString(), parsePatterns);
+		} catch (ParseException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * 获取过去的天数
+	 * @param date
+	 * @return
+	 */
+	public static long pastDays(Date date) {
+		long t = new Date().getTime()-date.getTime();
+		return t/(24*60*60*1000);
+	}
+
+	/**
+	 * 获取过去的小时
+	 * @param date
+	 * @return
+	 */
+	public static long pastHour(Date date) {
+		long t = new Date().getTime()-date.getTime();
+		return t/(60*60*1000);
+	}
+
+	/**
+	 * 获取过去的分钟
+	 * @param date
+	 * @return
+	 */
+	public static long pastMinutes(Date date) {
+		long t = new Date().getTime()-date.getTime();
+		return t/(60*1000);
+	}
+
+	/**
+	 * 转换为时间(天,时:分:秒.毫秒)
+	 * @param timeMillis
+	 * @return
+	 */
+    public static String formatDateTime(long timeMillis){
+		long day = timeMillis/(24*60*60*1000);
+		long hour = (timeMillis/(60*60*1000)-day*24);
+		long min = ((timeMillis/(60*1000))-day*24*60-hour*60);
+		long s = (timeMillis/1000-day*24*60*60-hour*60*60-min*60);
+		long sss = (timeMillis-day*24*60*60*1000-hour*60*60*1000-min*60*1000-s*1000);
+		return (day>0?day+",":"")+hour+":"+min+":"+s+"."+sss;
+    }
+
+	/**
+	 * 获取两个日期之间的天数
+	 *
+	 * @param before
+	 * @param after
+	 * @return
+	 */
+	public static double getDistanceOfTwoDate(Date before, Date after) {
+		long beforeTime = before.getTime();
+		long afterTime = after.getTime();
+		return (afterTime - beforeTime) / (1000 * 60 * 60 * 24);
+	}
+
+	/**
+	 * 获取某个月的天数
+	 * @param date
+	 * @return
+	 */
+	public static int getDaysOfMonth(Date date) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
+	}
+
+	/**
+	 * 在日期上增加天数
+	 *
+	 * @param date
+	 *            日期
+	 * @param n
+	 *            要增加的天数
+	 * @return
+	 */
+	public static Date addDay(Date date, int n) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.add(Calendar.DATE, n);
+		return cal.getTime();
+	}
+
+//	/**
+//	 * @param args
+//	 * @throws ParseException
+//	 */
+//	public static void main(String[] args) throws ParseException {
+//		System.out.println(formatDate(parseDate("2010/3/6")));
+//		System.out.println(getDate("yyyy年MM月dd日 E"));
+//		long time = new Date().getTime()-parseDate("2012-11-19").getTime();
+//		System.out.println(time/(24*60*60*1000));
+//	}
+
+	/**
+	 * 英文全称 如:2010-12-01 23:15:06
+	 */
+	public static String FORMAT_LONG = "yyyy-MM-dd HH:mm:ss";
+	/**
+	 * 精确到毫秒的完整时间 如:yyyy-MM-dd HH:mm:ss.S
+	 */
+	public static String FORMAT_FULL = "yyyy-MM-dd HH:mm:ss.S";
+	/**
+	 * 中文简写 如:2010年12月01日
+	 */
+	public static String FORMAT_SHORT_CN = "yyyy年MM月dd";
+	/**
+	 * 中文全称 如:2010年12月01日 23时15分06秒
+	 */
+	public static String FORMAT_LONG_CN = "yyyy年MM月dd日  HH时mm分ss秒";
+	/**
+	 * 精确到毫秒的完整中文时间
+	 */
+	public static String FORMAT_FULL_CN = "yyyy年MM月dd日  HH时mm分ss秒SSS毫秒";
+
+	/**
+	 * 获得默认的 date pattern
+	 */
+	public static String getDatePattern() {
+		return FORMAT_LONG;
+	}
+
+	/**
+	 * 根据预设格式返回当前日期
+	 *
+	 * @return
+	 */
+	public static String getNow() {
+		return format(new Date());
+	}
+
+	/**
+	 * 根据用户格式返回当前日期
+	 *
+	 * @param format
+	 * @return
+	 */
+	public static String getNow(String format) {
+		return format(new Date(), format);
+	}
+
+	/**
+	 * 使用预设格式格式化日期
+	 *
+	 * @param date
+	 * @return
+	 */
+	public static String format(Date date) {
+		return format(date, getDatePattern());
+	}
+
+	/**
+	 * 使用用户格式格式化日期
+	 *
+	 * @param date
+	 *            日期
+	 * @param pattern
+	 *            日期格式
+	 * @return
+	 */
+	public static String format(Date date, String pattern) {
+		String returnValue = "";
+		if (date != null) {
+			SimpleDateFormat df = new SimpleDateFormat(pattern);
+			returnValue = df.format(date);
+		}
+		return (returnValue);
+	}
+
+	/**
+	 * 使用预设格式提取字符串日期
+	 *
+	 * @param strDate
+	 *            日期字符串
+	 * @return
+	 */
+	public static Date parse(String strDate) {
+		return parse(strDate, getDatePattern());
+	}
+
+	/**
+	 * 使用用户格式提取字符串日期
+	 *
+	 * @param strDate
+	 *            日期字符串
+	 * @param pattern
+	 *            日期格式
+	 * @return
+	 */
+	public static Date parse(String strDate, String pattern) {
+		SimpleDateFormat df = new SimpleDateFormat(pattern);
+		try {
+			return df.parse(strDate);
+		} catch (ParseException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * 在日期上增加数个整月
+	 *
+	 * @param date
+	 *            日期
+	 * @param n
+	 *            要增加的月数
+	 * @return
+	 */
+	public static Date addMonth(Date date, int n) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.add(Calendar.MONTH, n);
+		return cal.getTime();
+	}
+
+	/**
+	 * 在日期上增加描述
+	 *
+	 * @param date
+	 *            日期
+	 * @param n
+	 *            要增加的天数
+	 * @return
+	 */
+	public static Date addSecond(Date date, int n) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.add(Calendar.SECOND, n);
+		return cal.getTime();
+	}
+
+	/**
+	 * 获取时间戳
+	 */
+	public static String getTimeString() {
+		SimpleDateFormat df = new SimpleDateFormat(FORMAT_FULL);
+		Calendar calendar = Calendar.getInstance();
+		return df.format(calendar.getTime());
+	}
+
+	/**
+	 * 获取日期年份
+	 *
+	 * @param date
+	 *            日期
+	 * @return
+	 */
+	public static String getYear(Date date) {
+		return format(date).substring(0, 4);
+	}
+
+	/**
+	 * 按默认格式的字符串距离今天的天数
+	 *
+	 * @param date
+	 *            日期字符串
+	 * @return
+	 */
+	public static int countDays(String date) {
+		long t = Calendar.getInstance().getTime().getTime();
+		Calendar c = Calendar.getInstance();
+		c.setTime(parse(date));
+		long t1 = c.getTime().getTime();
+		return (int) (t / 1000 - t1 / 1000) / 3600 / 24;
+	}
+
+	/**
+	 * 按用户格式字符串距离今天的天数(超过24小时才可以使用)
+	 *
+	 * @param date
+	 *            日期字符串
+	 * @param format
+	 *            日期格式
+	 * @return
+	 */
+	public static int countDays(String date, String format) {
+		long t = Calendar.getInstance().getTime().getTime();
+		Calendar c = Calendar.getInstance();
+		c.setTime(parse(date, format));
+		long t1 = c.getTime().getTime();
+		return (int) (t / 1000 - t1 / 1000) / 3600 / 24;
+	}
+
+	public static String firstDayOfMonth(Date date){
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		calendar.set(Calendar.DAY_OF_MONTH, calendar
+				.getActualMinimum(Calendar.DAY_OF_MONTH));
+		calendar.set(Calendar.HOUR_OF_DAY, 0);
+		calendar.set(Calendar.SECOND,0);
+		calendar.set(Calendar.MINUTE,0);
+		return DateUtils.format(calendar.getTime());
+	}
+
+	public static String lastDayOfMonth(Date date){
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		calendar.set(Calendar.DAY_OF_MONTH, calendar
+				.getActualMaximum(Calendar.DAY_OF_MONTH));
+		calendar.set(Calendar.HOUR_OF_DAY, 23);
+		calendar.set(Calendar.SECOND,59);
+		calendar.set(Calendar.MINUTE,59);
+		return DateUtils.format(calendar.getTime());
+	}
+
+	public static int currentMonth(){
+		Calendar calendar = Calendar.getInstance();
+		return calendar.get(Calendar.MONTH)+1;
+	}
+
+	public static int currentYear(){
+		Calendar calendar = Calendar.getInstance();
+		return calendar.get(Calendar.YEAR);
+	}
+
+	public static int currentMonthLastDay(){
+		Calendar calendar = Calendar.getInstance();
+		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+		return calendar.get(Calendar.DAY_OF_MONTH);
+	}
+
+	public static int LastDayOfMonth(Date date){
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+		return calendar.get(Calendar.DAY_OF_MONTH);
+	}
+
+	public static int getDay4Date(Date date){
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		return calendar.get(Calendar.DAY_OF_MONTH);
+	}
+
+	public static boolean afterNow(Date date){
+		return new Date().before(date);
+	}
+
+//	/**
+//	 *
+//	 * @param agrs
+//	 * @throws ParseException
+//	 */
+//	public static void main(String[] agrs) throws ParseException {
+//		//System.out.println(DateUtils.format(DateUtils.parse("2015-5-19 10:49:29"), "yyyy年MM月"));
+//		System.out.println(DateUtils.firstDayOfMonth(parse("2015-02-01",FORMAT_SHORT)));
+//		System.out.println(DateUtils.lastDayOfMonth(parse("2015-02-01",FORMAT_SHORT)));
+//		System.out.println(currentYear());
+//		System.out.println(currentMonthLastDay());
+//	}
+
+	// 获取上周的开始时间
+	@SuppressWarnings("unused")
+	public static Date getBeginDayOfLastWeek() {
+		Date date = new Date();
+		if (date == null) {
+			return null;
+		}
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		int dayofweek = cal.get(Calendar.DAY_OF_WEEK);
+		if (dayofweek == 1) {
+			dayofweek += 7;
+		}
+		cal.add(Calendar.DATE, 2 - dayofweek - 7);
+		return getDayStartTime(cal.getTime());
+	}
+
+	// 获取上周的结束时间
+	public static Date getEndDayOfLastWeek() {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(getBeginDayOfLastWeek());
+		cal.add(Calendar.DAY_OF_WEEK, 6);
+		Date weekEndSta = cal.getTime();
+		return getDayEndTime(weekEndSta);
+	}
+
+	// 获取某个日期的开始时间
+	public static Timestamp getDayStartTime(Date d) {
+		Calendar calendar = Calendar.getInstance();
+		if (null != d) {
+			calendar.setTime(d);
+		}
+		calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
+				calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
+		calendar.set(Calendar.MILLISECOND, 0);
+		return new Timestamp(calendar.getTimeInMillis());
+	}
+
+	// 获取某个日期的结束时间
+	public static Timestamp getDayEndTime(Date d) {
+		Calendar calendar = Calendar.getInstance();
+		if (null != d) {
+			calendar.setTime(d);
+		}
+		calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
+				calendar.get(Calendar.DAY_OF_MONTH), 23, 59, 59);
+		calendar.set(Calendar.MILLISECOND, 999);
+		return new Timestamp(calendar.getTimeInMillis());
+	}
+
+	// 获取上月的开始时间
+	public static Date getBeginDayOfLastMonth() {
+		Calendar calendar = Calendar.getInstance();
+		calendar.set(getNowYear(), getNowMonth() - 2, 1);
+		return getDayStartTime(calendar.getTime());
+	}
+
+	// 获取上月的结束时间
+	public static Date getEndDayOfLastMonth() {
+		Calendar calendar = Calendar.getInstance();
+		calendar.set(getNowYear(), getNowMonth() - 2, 1);
+		int day = calendar.getActualMaximum(5);
+		calendar.set(getNowYear(), getNowMonth() - 2, day);
+		return getDayEndTime(calendar.getTime());
+	}
+
+	// 获取今年是哪一年
+	public static Integer getNowYear() {
+		Date date = new Date();
+		GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
+		gc.setTime(date);
+		return Integer.valueOf(gc.get(1));
+	}
+
+	// 获取本月是哪一月
+	public static int getNowMonth() {
+		Date date = new Date();
+		GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
+		gc.setTime(date);
+		return gc.get(2) + 1;
+	}
+
+	// 获取昨天的开始时间
+	public static Date getBeginDayOfYesterday() {
+		Calendar cal = new GregorianCalendar();
+		cal.setTime(getDayBegin());
+		cal.add(Calendar.DAY_OF_MONTH, -1);
+		return cal.getTime();
+	}
+
+	// 获取昨天的结束时间
+	public static Date getEndDayOfYesterDay() {
+		Calendar cal = new GregorianCalendar();
+		cal.setTime(getDayEnd());
+		cal.add(Calendar.DAY_OF_MONTH, -1);
+		return cal.getTime();
+	}
+
+	// 获取当天的开始时间
+	public static Date getDayBegin() {
+		Calendar cal = new GregorianCalendar();
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return cal.getTime();
+	}
+
+	// 获取当天的结束时间
+	public static Date getDayEnd() {
+		Calendar cal = new GregorianCalendar();
+		cal.set(Calendar.HOUR_OF_DAY, 23);
+		cal.set(Calendar.MINUTE, 59);
+		cal.set(Calendar.SECOND, 59);
+		return cal.getTime();
+	}
+
+	/**
+	 * @Author ye.qin
+	 * @Description //TODO 返回两个时间字符串的时间差(秒)
+	 * @Date 2018\12\29 0029 15:56
+	 * @Param
+	 * @return
+	 */
+	public static int betweenDate(String endTime,String startTime,String YMDHMS){
+		Date time1 = DateUtils.parse(endTime,YMDHMS);
+		Date time2 = DateUtils.parse(startTime,YMDHMS);
+		int a = (int) (time1.getTime() / 1000);
+		int b = (int) (time2.getTime() / 1000);
+		System.out.print(a-b);
+		return a-b;
+	}
+
+	/**
+	 * @Author ye.qin
+	 * @Description //TODO 获取指定格式的时间
+	 * @Date 2018\12\29 0029 16:26
+	 * @Param
+	 * @return
+	 */
+	public static String getTargetTime(String time,String YMDHMS,String targetFormat){
+		Date a = parse(time,YMDHMS);
+		String b = format(a,targetFormat);
+		return b;
+	}
+
+    /**
+     * date2比date1多的天数
+     * @param date
+     * @return
+     */
+    public static int differentDays(String date)
+    {
+        Calendar cal1 = Calendar.getInstance();
+        cal1.setTime(parseDate(date));
+
+        Calendar cal2 = Calendar.getInstance();
+        cal2.setTime(new Date());
+        int day1= cal1.get(Calendar.DAY_OF_YEAR);
+        int day2 = cal2.get(Calendar.DAY_OF_YEAR);
+
+        int year1 = cal1.get(Calendar.YEAR);
+        int year2 = cal2.get(Calendar.YEAR);
+        if(year1 != year2)
+        {
+            int timeDistance = 0 ;
+            for(int i = year1 ; i < year2 ; i ++)
+            {
+                if(i%4==0 && i%100!=0 || i%400==0)    //闰年
+                {
+                    timeDistance += 366;
+                }
+                else    //不是闰年
+                {
+                    timeDistance += 365;
+                }
+            }
+
+            return timeDistance + (day2-day1) ;
+        }
+        else
+        {
+            return day2-day1;
+        }
+    }
+
+	/**
+	 * @Author ye.qin
+	 * @Description //TODO 格式化文章时间
+	 * @Date 2019\1\14 0014 15:48
+	 * @Param
+	 * @return
+	 */
+	public static String getArticleTime(String deployTime, String YMDHMS) {
+		final String TARGETTIME = "yyyy年MM月dd日";
+		final String HOURMINUTE = "HH:mm";
+		String time1 = "";
+		String currentTime = DateUtils.format(new Date(),YMDHMS);
+		int minuteFlag = DateUtils.betweenDate(currentTime,deployTime,YMDHMS);
+		int dayFlag = DateUtils.differentDays(deployTime);
+		if(minuteFlag > 0){
+			if(minuteFlag < 3*60){
+				time1 = "刚刚";
+			}else if(minuteFlag < 5*60){
+				time1= "3分钟前";
+			}else if(minuteFlag < 10*60){
+				time1 = "5分钟前";
+			}else if(minuteFlag < 20*60){
+				time1 = "10分钟前";
+			}else if(minuteFlag < 30*60){
+				time1 = "20分钟前";
+			}else if(minuteFlag < 60*60){
+				time1 = "30分钟前";
+			}else if(dayFlag == 0){
+				time1 = "今天 "+DateUtils.getTargetTime(deployTime,YMDHMS,HOURMINUTE);
+			}else if(dayFlag == 1){
+				time1 = "昨天 "+DateUtils.getTargetTime(deployTime,YMDHMS,HOURMINUTE);
+			}else{
+				time1 = DateUtils.getTargetTime(deployTime,YMDHMS,TARGETTIME);
+			}
+
+		}
+		return time1;
+	}
+
+	public static String getDatePoor(Date endDate, Date nowDate) {
+
+		long nd = 1000 * 24 * 60 * 60;
+		long nh = 1000 * 60 * 60;
+		long nm = 1000 * 60;
+		// long ns = 1000;
+		// 获得两个时间的毫秒时间差异
+		long diff = endDate.getTime() - nowDate.getTime();
+		// 计算差多少天
+		long day = diff / nd;
+		// 计算差多少小时
+		long hour = diff % nd / nh;
+		// 计算差多少分钟
+		long min = diff % nd % nh / nm;
+		// 计算差多少秒//输出结果
+		// long sec = diff % nd % nh % nm / ns;
+		return day + "天" + hour + "小时" + min + "分钟";
+	}
+
+	public static String getHourPoor(Date endDate, Date nowDate) {
+
+
+		long nh = 1000 * 60 * 60;
+		long nm = 1000 * 60;
+		// long ns = 1000;
+		// 获得两个时间的毫秒时间差异
+		long diff = endDate.getTime() - nowDate.getTime();
+
+		// 计算差多少小时
+		long hour = diff  / nh;
+		// 计算差多少分钟
+		long min = diff  % nh / nm;
+		// 计算差多少秒//输出结果
+		// long sec = diff % nd % nh % nm / ns;
+		return  hour + "小时" + min + "分钟";
+	}
+
+	public static String getGroupEndTime(Date endTime,String YMDHMS) {
+		final String HOURMINUTE = "HH:mm";
+		String time = formatDate(endTime,YMDHMS);
+		String time1 = "";
+		String currentTime = DateUtils.format(new Date(),YMDHMS);
+		int minuteFlag = DateUtils.betweenDate(time,currentTime,YMDHMS);
+		int dayFlag = DateUtils.differentDays(time);
+		if(minuteFlag > 0){
+
+			if(minuteFlag < 60*60*24*3){
+				time1 = getHourPoor(endTime,new Date());
+			}else{
+				time1 = "剩余"+(-dayFlag)+"天";
+			}
+		}
+		return time1;
+	}
+
+	/**
+	 * @param nowTime   当前时间
+	 * @param startTime 开始时间
+	 * @param endTime   结束时间
+	 * @return
+	 * @author sunran   判断当前时间在时间区间内
+	 */
+	public static boolean isEffectiveDate(Date nowTime, Date startTime, Date endTime) {
+		if (nowTime.getTime() == startTime.getTime()
+				|| nowTime.getTime() == endTime.getTime()) {
+			return true;
+		}
+
+		Calendar date = Calendar.getInstance();
+		date.setTime(nowTime);
+
+		Calendar begin = Calendar.getInstance();
+		begin.setTime(startTime);
+
+		Calendar end = Calendar.getInstance();
+		end.setTime(endTime);
+
+		if (date.after(begin) && date.before(end)) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+}

+ 75 - 3
src/main/java/com/caimei365/order/utils/RequestUtil.java

@@ -1,11 +1,16 @@
 package com.caimei365.order.utils;
 
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
+import org.springframework.util.StringUtils;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import java.io.*;
 import java.net.URL;
 import java.net.URLConnection;
 import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
 import java.util.List;
 import java.util.Map;
 
@@ -113,4 +118,71 @@ public class RequestUtil {
         return result.toString();
     }
 
+    /**
+     * 向指定 URL 发送请求
+     * @param requestUrl 发送请求的 URL
+     * @param requestMethod 请求方法GET/POST
+     * @param jsonString json字符串参数
+     * @return 远程资源的响应结果
+     */
+    public static String httpRequest(String requestUrl, String requestMethod, String jsonString) {
+        StringBuffer buffer = new StringBuffer();
+        try {
+            // 创建SSLContext对象,并使用我们指定的信任管理器初始化
+            TrustManager[] tm = {
+                    new javax.net.ssl.X509TrustManager(){
+                        @Override
+                        public void checkClientTrusted(X509Certificate[] chain, String authType) {
+                        }
+                        @Override
+                        public void checkServerTrusted(X509Certificate[] chain, String authType) {
+                        }
+                        @Override
+                        public X509Certificate[] getAcceptedIssuers() {
+                            return null;
+                        }
+                    }
+            };
+            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
+            sslContext.init(null, tm, new java.security.SecureRandom());
+            // 从上述SSLContext对象中得到SSLSocketFactory对象
+            SSLSocketFactory ssf = sslContext.getSocketFactory();
+
+            URL url = new URL(requestUrl);
+            HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
+            httpUrlConn.setSSLSocketFactory(ssf);
+            httpUrlConn.setDoOutput(true);
+            httpUrlConn.setDoInput(true);
+            httpUrlConn.setUseCaches(false);
+            // 设置请求方式(GET/POST)
+            httpUrlConn.setRequestMethod(requestMethod);
+            if ("GET".equalsIgnoreCase(requestMethod)) {
+                httpUrlConn.connect();
+            }
+            // 当有数据需要提交时
+            if (StringUtils.hasLength(jsonString)) {
+                OutputStream outputStream = httpUrlConn.getOutputStream();
+                // 注意编码格式,防止中文乱码
+                outputStream.write(jsonString.getBytes(StandardCharsets.UTF_8));
+                outputStream.close();
+            }
+
+            // 将返回的输入流转换成字符串
+            InputStream inputStream = httpUrlConn.getInputStream();
+            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
+            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+            String str = null;
+            while ((str = bufferedReader.readLine()) != null) {
+                buffer.append(str);
+            }
+            bufferedReader.close();
+            inputStreamReader.close();
+            // 释放资源
+            inputStream.close();
+            httpUrlConn.disconnect();
+            return buffer.toString();
+        } catch (Exception ignored) {}
+        return null;
+    }
+
 }

+ 14 - 8
src/main/resources/mapper/BaseMapper.xml

@@ -179,17 +179,17 @@
         LIMIT 1
     </select>
     <select id="getUserMoney" resultType="java.lang.Double">
-        SELECT userMoney
+        SELECT IFNULL(userMoney, 0)
         FROM user
         WHERE userID = #{userId}
     </select>
     <select id="getAbleUserMoney" resultType="java.lang.Double">
-        SELECT ableUserMoney
+        SELECT IFNULL(ableUserMoney, 0)
         FROM user
         WHERE userID = #{userId}
     </select>
     <select id="getUserBeans" resultType="java.lang.Integer">
-        SELECT userBeans
+        SELECT IFNULL(userBeans, 0)
         FROM user
         WHERE userID = #{userId}
     </select>
@@ -264,10 +264,10 @@
     </insert>
     <insert id="insertOrderReceiptRelation" keyColumn="id" keyProperty="id"
             parameterType="com.caimei365.order.model.po.OrderReceiptRelationPo" useGeneratedKeys="true">
-        INSERT INTO cm_receipt_order_relation (relationType, receiptId, associateAmount, orderId, couponRecordId, vipRecordId,authVipRecordId, delFlag,
+        INSERT INTO cm_receipt_order_relation (relationType, receiptId, associationType, associateAmount, orderId, couponRecordId, vipRecordId, authVipRecordId, delFlag,
                                                mbOrderId, orderRequestNo, splitStatus, productId)
-        VALUES (#{relationType}, #{receiptId}, #{associateAmount}, #{orderId},#{couponRecordId}, #{vipRecordId}, #{authVipRecordId}, #{delFlag}, #{mbOrderId},
-                #{orderRequestNo}, #{splitStatus}, #{productId})
+        VALUES (#{relationType}, #{receiptId}, #{associationType}, #{associateAmount}, #{orderId}, #{couponRecordId}, #{vipRecordId}, #{authVipRecordId}, #{delFlag},
+                #{mbOrderId}, #{orderRequestNo}, #{splitStatus}, #{productId})
     </insert>
     <insert id="insertBeansHistory" parameterType="com.caimei365.order.model.po.UserBeansHistoryPo">
         INSERT INTO user_beans_history (userId, type, beansType, orderId, num, pushStatus, addTime, delFlag)
@@ -288,9 +288,15 @@
         SET status = #{clubStatus}
         WHERE userID = #{userId}
     </update>
+    <update id="updateUserMoney">
+        UPDATE USER
+        SET userMoney     = #{userMoney},
+            ableUserMoney = #{ableUserMoney}
+        WHERE userID = #{userId}
+    </update>
     <insert id="insertBalanceRecord" parameterType="com.caimei365.order.model.po.BalanceRecordPo">
-        INSERT INTO cm_user_balance_record (userId, type, balanceType, addDate, amount, orderId, remark, delFlag)
-        VALUES (#{userId}, #{type}, #{balanceType}, #{addDate}, #{amount}, #{orderId}, #{remark}, #{delFlag})
+        INSERT INTO cm_user_balance_record (userId, type, balanceType, addDate, amount, orderId, receiptId, remark, delFlag)
+        VALUES (#{userId}, #{type}, #{balanceType}, #{addDate}, #{amount}, #{orderId}, #{receiptId}, #{remark}, #{delFlag})
     </insert>
     <select id="getSvipUserIdByUserId" resultType="java.lang.Integer">
         select userId

+ 54 - 1
src/main/resources/mapper/OrderCommonMapper.xml

@@ -308,6 +308,7 @@
         payTotalFee,
         payableAmount,
         balancePayFee,
+        discountFee,
         status,
         paySuccessCounter,
         confirmTime,
@@ -318,6 +319,58 @@
         FROM cm_order
         WHERE orderID = #{orderId}
     </select>
+    <select id="getOrderByShopOrderId" resultType="com.caimei365.order.model.vo.OrderVo">
+        SELECT
+           co.orderID AS orderId,
+           co.shopOrderIds,
+           co.orderSource,
+           co.orderNo,
+           co.organizeID AS organizeId,
+           co.userID AS userId,
+           co.clubID AS clubId,
+           co.buyUserID AS buyUserId,
+           co.orderTime AS orderTime,
+           co.updateDate AS updateDate,
+           co.delFlag,
+           co.userBeans,
+           co.orderType,
+           co.orderSubmitType,
+           co.confirmFlag,
+           co.onlinePayFlag,
+           co.splitFlag,
+           co.payFlag,
+           co.receiptStatus,
+           co.payStatus,
+           co.zeroCostFlag,
+           co.sendOutStatus,
+           co.refundType,
+           co.affirmPaymentFlag,
+           co.productCount,
+           co.presentCount,
+           co.promotionalGiftsCount,
+           co.hasActProduct,
+           co.promotionFullReduction,
+           co.secondHandOrderFlag,
+           co.invoiceFlag,
+           co.freePostFlag AS postageFlag,
+           co.freight AS postage,
+           co.productTotalFee,
+           co.orderTotalFee,
+           co.payTotalFee,
+           co.payableAmount,
+           co.balancePayFee,
+           co.discountFee,
+           co.status,
+           co.paySuccessCounter,
+           co.confirmTime,
+           co.payTime,
+           co.rebateFlag,
+           co.clauseID AS clauseId,
+           co.clauseName
+        FROM cm_shop_order cso
+        LEFT JOIN cm_order co ON cso.orderID = co.orderID
+        WHERE cso.shopOrderID = #{shopOrderId}
+    </select>
     <select id="getShopOrderListByOrderId" resultType="com.caimei365.order.model.vo.ShopOrderVo">
         SELECT
         shopOrderID AS shopOrderId,
@@ -514,4 +567,4 @@
     <select id="finduserBean" resultType="java.lang.Integer">
         select userBeans from cm_order where orderID = #{orderId}
     </select>
-</mapper>
+</mapper>

+ 1 - 4
src/main/resources/mapper/PayOrderMapper.xml

@@ -134,9 +134,6 @@
         set payStatus = 1
         where id = #{recordId}
     </update>
-    <update id="bugFix">
-        UPDATE USER SET userMoney = ableUserMoney WHERE userId = #{userId}
-    </update>
     <update id="updateAuthVipInfo">
         UPDATE auth_vip_user set authUserId=#{userId}, beginTime=#{beginTime}, endTime=#{endTime}, delFlag=#{delFlag}, updateTime=#{updateTime}
         WHERE authUserId = #{userId}
@@ -380,4 +377,4 @@
         AND delFlag = '0' AND payStatus = '0'
         ORDER BY id DESC LIMIT 1
     </select>
-</mapper>
+</mapper>

+ 881 - 0
src/main/resources/mapper/ReceiptMapper.xml

@@ -0,0 +1,881 @@
+<?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.caimei365.order.mapper.ReceiptMapper">
+    <insert id="insertReceipt" keyColumn="id" keyProperty="id"  parameterType="com.caimei365.order.model.po.ReceiptPo" useGeneratedKeys="true">
+        INSERT INTO cm_discern_receipt(
+            payWay,
+            payType,
+            receiptType,
+            receiptStatus,
+            smsContent,
+            smsMd5Code,
+            receiptAmount,
+            handlingFee,
+            confirmType,
+            receiptUserPermissionID,
+            confirmUserPermissionID,
+            reviewUserPermissionID,
+            cancelUserPermissionID,
+            noOrderReason,
+            reviewReason,
+            rebateRemarks,
+            cancelReason,
+            receiptDate,
+            confirmDate,
+            reviewDate,
+            cancelDate,
+            updateDate,
+            delFlag
+        ) VALUES (
+             #{payWay},
+             #{payType},
+             #{receiptType},
+             #{receiptStatus},
+             #{smsContent},
+             #{smsMd5Code},
+             #{receiptAmount},
+             #{handlingFee},
+             #{confirmType},
+             #{receiptUserPermissionId},
+             #{confirmUserPermissionId},
+             #{reviewUserPermissionId},
+             #{cancelUserPermissionId},
+             #{noOrderReason},
+             #{reviewReason},
+             #{rebateRemarks},
+             #{cancelReason},
+             #{receiptDate},
+             #{confirmDate},
+             #{reviewDate},
+             #{cancelDate},
+             #{updateDate},
+             #{delFlag}
+         )
+    </insert>
+    <update id="updateReceipt" parameterType="com.caimei365.order.model.po.ReceiptPo">
+        update cm_discern_receipt
+        <set >
+            <if test="payWay != null" >
+                payWay = #{payWay},
+            </if>
+            <if test="payType != null" >
+                payType = #{payType},
+            </if>
+            <if test="receiptType != null" >
+                receiptType = #{receiptType},
+            </if>
+            <if test="receiptStatus != null" >
+                receiptStatus = #{receiptStatus},
+            </if>
+            <if test="smsContent != null" >
+                smsContent = #{smsContent},
+            </if>
+            <if test="smsMd5Code != null" >
+                smsMd5Code = #{smsMd5Code},
+            </if>
+            <if test="orderFlag != null" >
+                orderFlag = #{orderFlag},
+            </if>
+            <if test="receiptAmount != null" >
+                receiptAmount = #{receiptAmount},
+            </if>
+            <if test="handlingFee != null" >
+                handlingFee = #{handlingFee},
+            </if>
+            <if test="confirmType != null" >
+                confirmType = #{confirmType},
+            </if>
+            <if test="confirmUserPermissionId != null" >
+                confirmUserPermissionID = #{confirmUserPermissionId},
+            </if>
+            <if test="reviewUserPermissionId != null" >
+                reviewUserPermissionID = #{reviewUserPermissionId},
+            </if>
+            <if test="cancelUserPermissionId != null" >
+                cancelUserPermissionID = #{cancelUserPermissionId},
+            </if>
+            <if test="noOrderReason != null" >
+                noOrderReason = #{noOrderReason},
+            </if>
+            <if test="reviewReason != null" >
+                reviewReason = #{reviewReason},
+            </if>
+            <if test="cancelReason != null" >
+                cancelReason = #{cancelReason},
+            </if>
+            <if test="receiptDate != null" >
+                receiptDate = #{receiptDate},
+            </if>
+            <if test="confirmDate != null" >
+                confirmDate = #{confirmDate},
+            </if>
+            <if test="reviewDate != null" >
+                reviewDate = #{reviewDate},
+            </if>
+            <if test="cancelDate != null" >
+                cancelDate = #{cancelDate},
+            </if>
+            <if test="updateDate != null" >
+                updateDate = #{updateDate},
+            </if>
+            <if test="delFlag != null" >
+                delFlag = #{delFlag},
+            </if>
+            <if test="rebateRemarks != null" >
+                rebateRemarks = #{rebateRemarks},
+            </if>
+        </set>
+        where id = #{id}
+    </update>
+    <update id="updateOpenidByMobile">
+        UPDATE cm_receipt_user SET openid=#{openid},unionId=#{unionId} WHERE mobile = #{mobile}
+    </update>
+    <update id="deleteReceiptRelation">
+        UPDATE cm_receipt_order_relation SET delFlag = '1' WHERE receiptID = #{receiptId}
+    </update>
+    <update id="updateShopOrderPayStatus">
+        UPDATE cm_shop_order SET payStatus = #{payStatus} WHERE shopOrderID = #{shopOrderId}
+    </update>
+    <update id="updateShopRebateAmount">
+        UPDATE shop s SET s.rebateAmount = (IFNULL(s.rebateAmount, 0) + #{amount}), s.ableRebateAmount = (IFNULL(s.ableRebateAmount, 0) + #{amount})
+        WHERE s.shopID = #{shopId}
+    </update>
+    <select id="getReceiptUserByOpenid" resultType="com.caimei365.order.model.vo.ReceiptUserVo">
+        SELECT DISTINCT id, user_type AS userType, name, mobile, openid, unionId, password
+        FROM cm_receipt_user
+        WHERE openid = #{openid} AND del_flag = 0
+    </select>
+    <select id="getReceiptUserByMobile" resultType="com.caimei365.order.model.vo.ReceiptUserVo">
+        SELECT DISTINCT id, user_type AS userType, name, mobile, openid, unionId, password
+        FROM cm_receipt_user
+        WHERE mobile = #{mobile} AND del_flag = 0
+    </select>
+    <select id="getReceiptUserNameById" resultType="java.lang.String">
+        SELECT DISTINCT name FROM cm_receipt_user WHERE id = #{id}
+    </select>
+    <select id="getPermissionsByUserId" resultType="java.lang.Integer">
+        SELECT permission_id FROM cm_receipt_user_permission WHERE user_id = #{userId}
+    </select>
+    <select id="getReceiptType" resultType="com.caimei365.order.model.vo.ReceiptTypeVo">
+        SELECT `id`,`type` FROM cm_receipt_type ORDER BY id
+    </select>
+    <select id="getReceiptTypeById" resultType="com.caimei365.order.model.vo.ReceiptTypeVo">
+        SELECT `id`,`type` FROM cm_receipt_type WHERE id = #{id}
+    </select>
+    <select id="getReceiptTypeStrById" resultType="java.lang.String">
+        SELECT `type` FROM cm_receipt_type WHERE id = #{id}
+    </select>
+    <select id="getReceivablesTypeId" resultType="java.lang.Integer">
+        SELECT DISTINCT IFNULL(id, 0) FROM cm_receipt_type WHERE type=#{type}
+    </select>
+    <select id="getPaidAmountByOrderId" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(cror.associateAmount), 0)
+        FROM cm_receipt_order_relation cror
+        LEFT JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+        <where>
+            <if test="orderId != null and orderId != ''">
+                AND cror.orderID = #{orderId} AND cror.relationType = 2
+            </if>
+            AND cdr.receiptStatus in(2,3)
+            AND cdr.payWay != '3'
+            AND cdr.delFlag = '0'
+            AND cror.delFlag = '0'
+        </where>
+    </select>
+    <select id="getUncheckAmount" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(cror.associateAmount), 0)
+        FROM cm_receipt_order_relation cror
+        LEFT JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+        <where>
+            <if test="orderId != null and orderId != ''">
+                AND cror.orderID = #{orderId} AND cror.relationType = 2
+            </if>
+            AND cdr.receiptStatus = '2'
+            AND cdr.delFlag = '0'
+            AND cror.delFlag = '0'
+        </where>
+    </select>
+    <select id="getReturnedAmount" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(returnedPurchaseFee), 0) FROM cm_returned_purchase
+        <where>
+            <if test="orderId != null and orderId != ''">
+                AND orderID = #{orderId}
+            </if>
+            AND status = '2'
+        </where>
+    </select>
+    <select id="getReceiptNumByOrderId" resultType="java.lang.Integer">
+        SELECT IFNULL(SUM(a.num),0) FROM cm_logistics_record a
+            LEFT JOIN cm_logistics_batch b ON a.logisticsBatchID = b.id
+        WHERE b.orderID = #{orderId} AND b.status = '1'
+    </select>
+    <select id="getReturnAndCancelNum" resultType="java.lang.Integer">
+        SELECT IFNULL(SUM(a.actualReturnedNum + a.actualCancelNum),0)
+        FROM cm_returned_purchase_product a
+            LEFT JOIN cm_returned_purchase b ON a.returnedID = b.id
+        WHERE b.orderID = #{orderId} AND b.status = '2' AND b.delFlag = '0'
+    </select>
+    <select id="getServiceProviderId" resultType="java.lang.Integer">
+        SELECT s.serviceProviderID FROM serviceprovider s
+             LEFT JOIN USER u ON s.userID = u.userID
+        WHERE s.unionId = #{unionId} AND u.validFlag = 1
+        LIMIT 1
+    </select>
+    <select id="getClubUserIdBySpId" resultType="java.lang.Integer">
+        SELECT userID FROM club WHERE spID = #{spId}
+    </select>
+    <select id="getReceiptBySmsMd5Code" resultType="com.caimei365.order.model.vo.ReceiptVo">
+        SELECT DISTINCT
+            cdr.id,
+            cdr.payWay,
+            cdr.payType,
+            cdr.receiptType,
+            cdr.receiptStatus,
+            cdr.receiptAmount,
+            cdr.smsContent,
+            cdr.smsMd5Code,
+            cdr.receiptDate,
+            cdr.confirmDate,
+            cdr.handlingFee,
+            cdr.orderFlag,
+            cdr.noOrderReason,
+            cdr.reviewReason,
+            IFNULL((
+                SELECT SUM(b.associateAmount) FROM cm_receipt_order_relation b WHERE b.receiptID = cdr.id AND b.delFlag = '0'
+            ), 0) AS associateAmount
+        FROM cm_discern_receipt cdr
+                 LEFT JOIN cm_receipt_order_relation cror ON (cror.receiptID = cdr.id AND cror.delFlag = '0')
+        WHERE cdr.smsMd5Code = #{smsMd5Code} AND cdr.delFlag = '0'
+    </select>
+    <select id="getReceiptList" resultType="com.caimei365.order.model.vo.ReceiptVo" parameterType="com.caimei365.order.model.bo.ReceiptParamsBo">
+        SELECT DISTINCT
+            cdr.id,
+            cdr.payWay,
+            cdr.payType,
+            cdr.receiptType,
+            cdr.receiptStatus,
+            cdr.receiptAmount,
+            cdr.smsContent,
+            cdr.smsMd5Code,
+            cdr.receiptDate,
+            cdr.confirmDate,
+            cdr.handlingFee,
+            cdr.orderFlag,
+            cdr.noOrderReason,
+            cdr.reviewReason,
+            CASE WHEN cror.relationType = 2 THEN co.organizeID
+                 WHEN cror.relationType = 1 THEN cso.organizeID
+                 ELSE '' END AS organizeId,
+            CASE WHEN cror.relationType = 2 THEN co.userID
+                 WHEN cror.relationType = 1 THEN cso.userID
+                 ELSE '' END AS userId,
+            IFNULL((
+                SELECT SUM(b.associateAmount) FROM cm_receipt_order_relation b WHERE b.receiptID = cdr.id AND b.delFlag = '0'
+            ), 0) AS associateAmount
+        FROM cm_discern_receipt cdr
+            LEFT JOIN cm_receipt_order_relation cror ON (cror.receiptID = cdr.id AND cror.delFlag = '0')
+            LEFT JOIN cm_order co ON (cror.orderID = co.orderID AND cror.relationType = 2)
+            LEFT JOIN cm_shop_order cso ON (cror.orderID = cso.shopOrderID AND cror.relationType = 1)
+        WHERE cdr.delFlag = '0'
+        <if test="id != null and id != ''">
+            AND cdr.id = #{id}
+        </if>
+        <if test="receiptType != null and receiptType != ''">
+            AND cdr.receiptType = #{receiptType}
+        </if>
+        <if test="smsMd5Code != null and smsMd5Code != ''">
+            AND cdr.smsMd5Code = #{smsMd5Code}
+        </if>
+        <if test="startDate != null  and startDate != ''">
+            AND cdr.receiptDate <![CDATA[  >  ]]> #{startDate}
+        </if>
+        <if test="endDate != null  and endDate != ''">
+            AND cdr.receiptDate <![CDATA[  <  ]]> #{endDate}
+        </if>
+        <if test="receiptStatusArr != null">
+            AND cdr.receiptStatus in
+            <foreach collection="receiptStatusArr" open="(" close=")" separator="," item="val">
+                #{val}
+            </foreach>
+        </if>
+        <if test="userIds != null and userIds.size() > 0">
+            AND ((co.userID IN
+                <foreach collection="userIds" open="(" close=")" separator="," item="val">
+                    #{val}
+                </foreach>
+                AND cror.relationType = 2 )
+            OR (cso.userID IN
+                <foreach collection="userIds" open="(" close=")" separator="," item="val">
+                    #{val}
+                </foreach>
+                AND cror.relationType = 1)
+            OR cror.orderID IS NULL)
+        </if>
+        ORDER BY cdr.updateDate desc
+    </select>
+    <select id="getReceiptDetail" resultType="com.caimei365.order.model.vo.ReceiptVo">
+        SELECT DISTINCT
+        cdr.id,
+        cdr.payWay,
+        cdr.payType,
+        cdr.receiptType,
+        cdr.receiptStatus,
+        cdr.receiptAmount,
+        cdr.smsContent,
+        cdr.smsMd5Code,
+        cdr.receiptDate,
+        cdr.confirmDate,
+        cdr.reviewDate,
+        cdr.cancelDate,
+        cdr.handlingFee,
+        cdr.orderFlag,
+        cdr.noOrderReason,
+        cdr.cancelReason,
+        cdr.confirmType,
+        cdr.reviewReason,
+        receiptUserPermissionID AS receiptUserPermissionId,
+        confirmUserPermissionID AS confirmUserPermissionId,
+        reviewUserPermissionID AS reviewUserPermissionId,
+        cancelUserPermissionID AS cancelUserPermissionId,
+        CASE WHEN cror.relationType = 2 THEN co.organizeID
+        WHEN cror.relationType = 1 THEN cso.organizeID
+        ELSE '' END AS organizeId,
+        CASE WHEN cror.relationType = 2 THEN co.userID
+        WHEN cror.relationType = 1 THEN cso.userID
+        ELSE '' END AS userId,
+        IFNULL((
+        SELECT SUM(b.associateAmount) FROM cm_receipt_order_relation b WHERE b.receiptID = cdr.id AND b.delFlag = '0'
+        ), 0) AS associateAmount
+        FROM cm_discern_receipt cdr
+        LEFT JOIN cm_receipt_order_relation cror ON (cror.receiptID = cdr.id AND cror.delFlag = '0')
+        LEFT JOIN cm_order co ON (cror.orderID = co.orderID AND cror.relationType = 2)
+        LEFT JOIN cm_shop_order cso ON (cror.orderID = cso.shopOrderID AND cror.relationType = 1)
+        WHERE cdr.delFlag = '0' AND cdr.id = #{id}
+    </select>
+    <select id="getReceiptOrders" resultType="com.caimei365.order.model.vo.OrderVo">
+        SELECT
+            IFNULL(o.orderSeen,0) AS orderSeen,
+            o.orderID AS orderId,
+            o.shopOrderIds,
+            o.orderSource,
+            o.orderNo,
+            o.userID AS userId,
+            o.clubID AS clubId,
+            o.organizeID AS organizeId,
+            o.buyUserID AS buyUserId,
+            o.orderTime AS orderTime,
+            o.updateDate AS updateDate,
+            o.delFlag,
+            o.userBeans,
+            o.orderType,
+            o.orderSubmitType,
+            o.confirmFlag,
+            o.onlinePayFlag,
+            o.splitFlag,
+            o.payFlag,
+            o.receiptStatus,
+            o.payStatus,
+            o.zeroCostFlag,
+            o.sendOutStatus,
+            o.refundType,
+            o.affirmPaymentFlag,
+            o.productCount,
+            o.presentCount,
+            o.promotionalGiftsCount,
+            o.hasActProduct,
+            o.promotionFullReduction,
+            o.svipFullReduction,
+            o.secondHandOrderFlag,
+            o.invoiceFlag,
+            o.freePostFlag AS postageFlag,
+            o.freight AS postage,
+            o.productTotalFee,
+            o.orderTotalFee,
+            o.payTotalFee,
+            o.payableAmount,
+            o.balancePayFee,
+            o.discountFee,
+            o.couponAmount,
+            o.status,
+            o.confirmTime,
+            o.payTime,
+            o.rebateFlag,
+            o.clauseID AS clauseId,
+            o.clauseName,
+        IFNULL(u.userName,'') AS userName
+        FROM cm_order o
+        INNER JOIN cm_receipt_order_relation cror ON o.orderID = cror.orderID
+        INNER JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+        LEFT JOIN USER u ON o.userID = u.userID
+        WHERE cdr.id = #{receiptId}
+            AND o.delFlag = '0'
+            AND cror.delFlag = '0'
+            AND cdr.delFlag = '0'
+        ORDER BY o.orderTime DESC
+    </select>
+    <select id="getOrderListByParams" resultType="com.caimei365.order.model.vo.OrderVo">
+        SELECT DISTINCT
+        IFNULL(o.orderSeen,0) AS orderSeen,
+        o.orderID AS orderId,
+        o.shopOrderIds,
+        o.orderSource,
+        o.orderNo,
+        o.userID AS userId,
+        o.clubID AS clubId,
+        o.organizeID AS organizeId,
+        o.buyUserID AS buyUserId,
+        o.orderTime AS orderTime,
+        o.updateDate AS updateDate,
+        o.delFlag,
+        o.userBeans,
+        o.orderType,
+        o.orderSubmitType,
+        o.confirmFlag,
+        o.onlinePayFlag,
+        o.splitFlag,
+        o.payFlag,
+        o.receiptStatus,
+        o.payStatus,
+        o.zeroCostFlag,
+        o.sendOutStatus,
+        o.refundType,
+        o.affirmPaymentFlag,
+        o.productCount,
+        o.presentCount,
+        o.promotionalGiftsCount,
+        o.hasActProduct,
+        o.promotionFullReduction,
+        o.svipFullReduction,
+        o.secondHandOrderFlag,
+        o.invoiceFlag,
+        o.freePostFlag AS postageFlag,
+        o.freight AS postage,
+        o.productTotalFee,
+        o.orderTotalFee,
+        o.payTotalFee,
+        o.payableAmount,
+        o.balancePayFee,
+        o.discountFee,
+        o.couponAmount,
+        o.status,
+        o.confirmTime,
+        o.payTime,
+        o.rebateFlag,
+        o.clauseID AS clauseId,
+        o.clauseName,
+        <if test="organizeId == 0 or organizeId == null">
+            IFNULL(u.userName, u.name) AS userName,
+        </if>
+        <if test="organizeId > 0">
+            IFNULL(u.name, u.userName) AS userName,
+        </if>
+        '0' AS haveReturning,
+        '0' AS haveReceipting
+        FROM cm_order o
+        LEFT JOIN user u ON o.userID = u.userID
+        LEFT JOIN cm_order_product cop ON o.orderID = cop.orderID
+        <where>
+            <if test="excludeOrderId != null and excludeOrderId != ''">
+                AND o.orderID != #{excludeOrderId}
+            </if>
+            <if test="orderId != null and orderId != ''">
+                AND o.orderID = #{orderId}
+            </if>
+            AND o.delFlag = '0'
+            AND o.organizeID = #{organizeId}
+            AND o.receiptStatus IN
+            <foreach collection="receiptStatusArr" open="(" close=")" item="receiptStatus" separator=",">
+                #{receiptStatus}
+            </foreach>
+            AND o.status IN
+            <foreach collection="orderStatusList" open="(" close=")" item="status" separator=",">
+                #{status}
+            </foreach>
+            <if test="userIds != null and userIds.size() > 0">
+                AND o.userID IN
+                <foreach collection="userIds" open="(" close=")" separator="," item="val">
+                    #{val}
+                </foreach>
+            </if>
+            <if test="userName != null and userName != ''">
+                AND (u.userName LIKE CONCAT('%',#{userName},'%') OR u.name LIKE CONCAT('%',#{userName},'%'))
+            </if>
+            AND o.rebateFlag = 0
+            <if test="orderType == null or orderType == 0">
+                AND cop.productID NOT IN(6060, 6061, 6062, 6063, 6064,6065, 6066, 6067, 6068, 6069)
+            </if>
+            <if test="orderType != null and orderType == 1">
+                AND cop.productID IN(6060, 6061, 6062, 6063, 6064,6065, 6066, 6067, 6068, 6069)
+            </if>
+        </where>
+        <if test="orderId != null and orderId != ''">
+            ORDER BY (CASE WHEN o.orderID=#{orderId} THEN 1 ELSE 2 END),o.orderTime DESC
+        </if>
+        <if test="orderId == null or orderId == ''">
+            ORDER BY o.orderTime DESC
+        </if>
+    </select>
+    <select id="getOrderListByOrderIds" resultType="com.caimei365.order.model.vo.OrderVo">
+        SELECT DISTINCT
+            IFNULL(o.orderSeen,0) AS orderSeen,
+            o.orderID AS orderId,
+            o.shopOrderIds,
+            o.orderSource,
+            o.orderNo,
+            o.userID AS userId,
+            o.clubID AS clubId,
+            o.organizeID AS organizeId,
+            o.buyUserID AS buyUserId,
+            o.orderTime AS orderTime,
+            o.updateDate AS updateDate,
+            o.delFlag,
+            o.userBeans,
+            o.orderType,
+            o.orderSubmitType,
+            o.confirmFlag,
+            o.onlinePayFlag,
+            o.splitFlag,
+            o.payFlag,
+            o.receiptStatus,
+            o.payStatus,
+            o.zeroCostFlag,
+            o.sendOutStatus,
+            o.refundType,
+            o.affirmPaymentFlag,
+            o.productCount,
+            o.presentCount,
+            o.promotionalGiftsCount,
+            o.hasActProduct,
+            o.promotionFullReduction,
+            o.svipFullReduction,
+            o.secondHandOrderFlag,
+            o.invoiceFlag,
+            o.freePostFlag AS postageFlag,
+            o.freight AS postage,
+            o.productTotalFee,
+            o.orderTotalFee,
+            o.payTotalFee,
+            o.payableAmount,
+            o.balancePayFee,
+            o.discountFee,
+            o.couponAmount,
+            o.status,
+            o.confirmTime,
+            o.payTime,
+            o.rebateFlag,
+            o.clauseID AS clauseId,
+            o.clauseName
+        FROM cm_order o
+        WHERE o.delFlag = '0' AND o.orderID IN
+        <foreach collection="orderIdS" open="(" close=")" separator="," item="orderId">
+            #{orderId}
+        </foreach>
+        order by o.payableAmount, o.orderID
+    </select>
+    <select id="countHaveReturning" resultType="java.lang.Integer">
+        SELECT IFNULL(COUNT(id), 0) FROM cm_returned_purchase WHERE orderID = #{orderID} AND status = 1 AND delFlag = 0
+    </select>
+    <select id="countHaveReceipting" resultType="java.lang.Integer">
+        SELECT IFNULL(COUNT(cror.id), 0) FROM cm_receipt_order_relation cror
+            INNER JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+        WHERE cror.orderID = #{orderID} AND cror.delFlag = 0 AND cdr.delFlag = 0 AND cdr.receiptStatus = 2
+    </select>
+    <select id="getOnlineReceiptId" resultType="java.lang.Integer">
+        SELECT
+            cdr.id
+        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.payWay = '1'
+        LIMIT 1
+    </select>
+    <select id="getRefundFeeByOrderId" resultType="com.caimei365.order.model.vo.ReturnedPurchaseVo">
+        SELECT
+            id,
+            returnedPurchaseFee,
+            refundFee
+        FROM cm_returned_purchase
+        WHERE orderID = #{orderId} AND STATUS = '2'
+    </select>
+    <select id="getShopOrderListByReceiptId" resultType="com.caimei365.order.model.vo.ShopOrderVo">
+        SELECT DISTINCT
+            cso.shopOrderId AS shopOrderId,
+            cso.shopOrderNo,
+            cso.orderID AS orderId,
+            cso.orderNo,
+            cso.shopID AS shopId,
+            cso.note,
+            cso.userID AS userId,
+            cso.clubID AS clubId,
+            cso.spID AS spId,
+            cso.orderPromotionsId,
+            cso.promotionFullReduction,
+            cso.brokerage,
+            cso.canRefundAmount,
+            cso.itemCount,
+            cso.totalAmount,
+            cso.productAmount,
+            cso.needPayAmount,
+            cso.shopProductAmount,
+            cso.shopPostFee,
+            cso.shopTaxFee,
+            cso.shouldPayShopAmount,
+            cso.payedShopAmount,
+            cso.orderTime,
+            cso.orderSubmitType,
+            cso.payStatus,
+            cso.sendOutStatus,
+            cso.splitFlag,
+            cso.shopOtherFee,
+            s.name AS shopName
+        FROM cm_receipt_order_relation cror
+            LEFT JOIN cm_shop_order cso ON  cso.shopOrderID = cror.orderID
+            LEFT JOIN shop s ON s.shopID = cso.shopID
+        WHERE cror.delFlag = '0' AND  cror.relationType = '1' AND cror.receiptID = #{receiptId}
+    </select>
+    <select id="getRefundShopOrders" resultType="com.caimei365.order.model.vo.ShopOrderVo">
+        SELECT DISTINCT
+        cso.shopOrderId AS shopOrderId,
+        cso.shopOrderNo,
+        cso.orderID AS orderId,
+        cso.orderNo,
+        cso.shopID AS shopId,
+        cso.note,
+        cso.userID AS userId,
+        cso.clubID AS clubId,
+        cso.spID AS spId,
+        cso.orderPromotionsId,
+        cso.promotionFullReduction,
+        cso.brokerage,
+        cso.canRefundAmount,
+        cso.itemCount,
+        cso.totalAmount,
+        cso.productAmount,
+        cso.needPayAmount,
+        cso.shopProductAmount,
+        cso.shopPostFee,
+        cso.shopTaxFee,
+        cso.shouldPayShopAmount,
+        cso.payedShopAmount,
+        cso.orderTime,
+        cso.orderSubmitType,
+        cso.payStatus,
+        cso.sendOutStatus,
+        cso.splitFlag,
+        cso.shopOtherFee,
+        s.name AS shopName,
+        IFNULL((SELECT SUM(refundAmount) FROM cm_refund_shop_record WHERE shopOrderID = cso.shopOrderID AND delFlag = 0), 0) AS shopRefundAmount
+        FROM cm_shop_order cso
+        LEFT JOIN shop s ON s.shopID = cso.shopID
+        LEFT JOIN cm_receipt_order_relation cror ON cso.shopOrderID = cror.orderID AND cror.relationType = 1
+        LEFT JOIN cm_discern_receipt cdr ON cdr.id = cror.receiptID AND cdr.receiptType = 5 AND cdr.receiptStatus = 2
+        LEFT JOIN user u ON u.userID = cso.userID
+        WHERE cso.delFlag = 0
+        AND (cror.delFlag = 0 OR cror.id IS NULL)
+        <if test="shopName != null">
+            AND cso.orderID = #{orderId}
+        </if>
+        <if test="shopName != null and shopName != ''">
+            AND s.name LIKE CONCAT('%',#{shopName},'%')
+        </if>
+        <if test="userName != null and userName != ''">
+            AND u.userName LIKE CONCAT('%',#{userName},'%')
+        </if>
+        <if test="confirmedType == 2">
+            AND cdr.receiptStatus = 2
+        </if>
+        GROUP BY cso.shopOrderID
+        <if test="confirmedType != 2">
+            HAVING IFNULL(cso.payedShopAmount, 0) > IFNULL((SELECT SUM(refundAmount) FROM cm_refund_shop_record WHERE shopOrderID = cso.shopOrderID AND delFlag = 0), 0)
+        </if>
+        ORDER BY cso.orderTime DESC
+    </select>
+    <select id="getRebateShopOrders" resultType="com.caimei365.order.model.vo.ShopOrderVo">
+        SELECT DISTINCT
+        cso.shopOrderId AS shopOrderId,
+        cso.shopOrderNo,
+        cso.orderID AS orderId,
+        cso.orderNo,
+        cso.shopID AS shopId,
+        cso.note,
+        cso.userID AS userId,
+        cso.clubID AS clubId,
+        cso.spID AS spId,
+        cso.orderPromotionsId,
+        cso.promotionFullReduction,
+        cso.brokerage,
+        cso.canRefundAmount,
+        cso.itemCount,
+        cso.totalAmount,
+        cso.productAmount,
+        cso.needPayAmount,
+        cso.shopProductAmount,
+        cso.shopPostFee,
+        cso.shopTaxFee,
+        cso.shouldPayShopAmount,
+        cso.payedShopAmount,
+        cso.orderTime,
+        cso.orderSubmitType,
+        cso.payStatus,
+        cso.sendOutStatus,
+        cso.splitFlag,
+        cso.shopOtherFee,
+        s.name AS shopName,
+        IF((SELECT (crpp.id) FROM cm_returned_purchase_product crpp LEFT JOIN cm_returned_purchase crp ON crp.id = crpp.returnedID
+        WHERE crpp.shopOrderID = cso.shopOrderID AND crp.status = '1' AND  crp.delFlag = 0 LIMIT 1) > 0, 1, 0) AS haveReturning
+        FROM cm_shop_order cso
+        LEFT JOIN shop s ON s.shopID = cso.shopID
+        LEFT JOIN cm_order co ON co.orderID = cso.orderID
+        <where>
+            <if test="shopName != null and shopName != ''">
+                AND s.name LIKE CONCAT('%', #{shopName}, '%')
+            </if>
+            AND cso.delFlag = '0'
+            AND co.delFlag = '0'
+            AND cso.shopOrderID NOT IN (
+                SELECT DISTINCT cror.orderID FROM cm_receipt_order_relation cror LEFT JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+                WHERE cror.delFlag = '0' AND cror.relationType = '1' AND cdr.receiptStatus = '2' AND cror.orderID IS NOT NULL
+                )
+            AND cso.orderID NOT IN (
+                SELECT DISTINCT cror.orderID FROM cm_receipt_order_relation cror
+                WHERE cror.delFlag = '0' AND cror.relationType = '2'  AND cror.orderID IS NOT NULL
+                )
+            AND co.receiptStatus != '3'
+            AND co.rebateFlag = 1
+        </where>
+        ORDER BY cso.shopOrderID DESC
+    </select>
+    <select id="getShopOrderById" resultType="com.caimei365.order.model.vo.ShopOrderVo">
+        SELECT
+            shopOrderID AS shopOrderId,
+            shopOrderNo,
+            orderID AS orderId,
+            orderNo,
+            shopID AS shopId,
+            note,
+            userID AS userId,
+            clubID AS clubId,
+            spID AS spId,
+            orderPromotionsId,
+            promotionFullReduction,
+            brokerage,
+            canRefundAmount,
+            itemCount,
+            totalAmount,
+            productAmount,
+            needPayAmount,
+            shopProductAmount,
+            shopPostFee,
+            shopTaxFee,
+            shouldPayShopAmount,
+            payedShopAmount,
+            outStoreNum,
+            IFNULL(presentNum,0) AS presentNum,
+            orderTime,
+            orderSubmitType,
+            payStatus,
+            sendOutStatus,
+            splitFlag
+        FROM cm_shop_order
+        WHERE shopOrderID = #{shopOrderId} AND delFlag = '0'
+    </select>
+    <select id="countAllPayAssociateAmount" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(cror.associateAmount), 0) FROM cm_receipt_order_relation cror
+        LEFT JOIN cm_discern_receipt cdr on cror.receiptID = cdr.id
+        WHERE cror.delFlag = '0' AND cdr.delFlag = '0' AND cdr.payWay != '3' AND cdr.receiptStatus in(2,3)
+        <if test="orderID != null and orderID != ''">
+            AND cror.orderID = #{orderId} AND cror.relationType = 2
+        </if>
+    </select>
+    <select id="countNeedPayAmount" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(cso.needPayAmount), 0) FROM cm_receipt_order_relation cror
+            LEFT JOIN  cm_discern_receipt cdr ON cdr.id = cror.receiptID
+            LEFT JOIN cm_shop_order cso ON cso.shopOrderID = cror.orderID
+        WHERE cror.delFlag = '0' AND cror.relationType = '1' AND cdr.receiptType = 3
+          AND cdr.delFlag = '0' AND cdr.receiptStatus = '2'
+          AND cror.orderID IN (SELECT shopOrderID FROM cm_shop_order cso1 WHERE cso1.orderID = #{orderId})
+    </select>
+    <select id="getRebatePayTypeList" resultType="java.lang.Integer">
+        SELECT cdr.payType
+        FROM cm_receipt_order_relation cror
+            LEFT JOIN  cm_discern_receipt cdr ON cdr.id = cror.receiptID
+        WHERE cror.relationType = '1' AND cror.delFlag = '0'
+          AND cror.orderID = #{shopOrderId} and cdr.receiptType = '3'
+    </select>
+    <select id="getOpenidListByPermission" resultType="java.lang.String">
+        SELECT DISTINCT cru.openid FROM cm_receipt_user cru
+        LEFT JOIN cm_receipt_user_permission crup ON crup.user_id = cru.id
+        WHERE crup.permission_id IN
+        <foreach collection="permissions" open="(" close=")" item="permission" separator=",">
+            #{permission}
+        </foreach>
+        AND cru.del_flag = 0
+    </select>
+    <select id="countAssociateAmountById" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(associateAmount), 0) FROM cm_receipt_order_relation WHERE receiptID = #{receiptId}
+    </select>
+    <select id="getRefundShopAmount" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(refundAmount), 0) FROM  cm_refund_shop_record WHERE shopOrderID = #{shopOrderId} AND delFlag = 0
+    </select>
+    <select id="getShopIdList" resultType="java.lang.Integer">
+        SELECT DISTINCT shopID FROM cm_shop_order WHERE shopOrderID IN
+        <foreach collection="shopOrderIdArr" open="(" close=")" item="shopOrderId" separator=",">
+            #{shopOrderId}
+        </foreach>
+    </select>
+    <select id="getSubShopOrderList" resultType="java.lang.Integer">
+        SELECT cso2.shopOrderID FROM cm_shop_order cso1
+            LEFT JOIN cm_order co ON co.orderID = cso1.orderID
+            LEFT JOIN cm_shop_order cso2 ON cso2.orderID = co.orderID
+        WHERE cso1.shopOrderID = #{shopOrderId}
+    </select>
+    <select id="getSubShopOrderRebate" resultType="java.lang.Integer">
+        SELECT cror.orderID FROM cm_receipt_order_relation cror
+        WHERE  cror.delFlag = '0' AND  cror.relationType = '1' AND cror.orderID IN
+        <foreach collection="shopOrderIdList" open="(" close=")" item="shopOrderId" separator=",">
+            #{shopOrderId}
+        </foreach>
+        AND cror.orderID IS NOT NULL
+    </select>
+    <insert id="insertRefundShop" keyColumn="id" keyProperty="id" parameterType="com.caimei365.order.model.po.RefundShopPo" useGeneratedKeys="true">
+        INSERT INTO cm_refund_shop(shopID, operator, operatTime, refundAmount, refundType, remark, refundWay, refundBalanceAmount)
+        VALUES (#{shopId}, #{operator}, #{operateTime}, #{refundAmount}, #{refundType}, #{remark}, #{refundWay}, #{refundBalanceAmount})
+    </insert>
+    <insert id="insertRefundShopRecord" keyColumn="id" keyProperty="id" parameterType="com.caimei365.order.model.po.RefundShopRecordPo" useGeneratedKeys="true">
+        INSERT INTO cm_refund_shop_record(shopID, shopOrderID, shopOrderNo, refundAmount, refundType, refundTime, refundShopID)
+        VALUES (#{shopId}, #{shopOrderId}, #{shopOrderNo}, #{refundAmount}, #{refundType}, #{refundTime}, #{refundShopId})
+    </insert>
+    <insert id="insertRebateRecord" keyColumn="id" keyProperty="id" parameterType="com.caimei365.order.model.po.RebateRecordPo" useGeneratedKeys="true">
+        INSERT  INTO cm_rebate_record(type, shopId, receiptID, shopOrderId, rebateAmount, operatTime)
+        VALUES (#{type}, #{shopId}, #{receiptId}, #{shopOrderId}, #{rebateAmount}, #{operateTime})
+    </insert>
+    <select id="getOrderReceiptRelationList" resultType="com.caimei365.order.model.po.OrderReceiptRelationPo">
+        SELECT
+        cror.id,
+        cror.relationType,
+        cror.receiptId,
+        cror.associateAmount,
+        cror.orderId,
+        cror.delFlag,
+        cror.mbOrderId,
+        cror.orderRequestNo,
+        cror.splitStatus
+        FROM cm_receipt_order_relation cror
+        WHERE cror.receiptID = #{receiptId}
+        ORDER BY cror.id DESC
+    </select>
+    <select id="getPendingAuditCount" resultType="java.lang.Integer">
+        SELECT COUNT(*) 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,4)
+    </select>
+
+</mapper>