Kaynağa Gözat

Merge branch 'balance_func'

# Conflicts:
#	netflix-common/src/main/java/com/cyksj/common/constant/Constant.java
#	netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonView.java
#	netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
#	netflix-service/src/main/java/com/cyksj/task/Scheduler.java
#	netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java
zoujiajian 3 yıl önce
ebeveyn
işleme
adbb6f6034
43 değiştirilmiş dosya ile 1412 ekleme ve 867 silme
  1. 2 0
      netflix-common/src/main/java/com/cyksj/common/constant/Constant.java
  2. 12 0
      netflix-dao/src/main/java/com/cyksj/mapper/UserMapper.java
  3. 8 0
      netflix-dao/src/main/java/com/cyksj/mapper/channel/ChannelPopularizeMapper.java
  4. 2 0
      netflix-dao/src/main/java/com/cyksj/model/entity/DistributeWaitingSendPoints.java
  5. 19 0
      netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java
  6. 5 0
      netflix-dao/src/main/java/com/cyksj/model/entity/User.java
  7. 2 1
      netflix-dao/src/main/java/com/cyksj/model/entity/UserTicketClearedRecord.java
  8. 83 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/CorpWxOrderDonView.java
  9. 213 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonBackView.java
  10. 9 86
      netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonView.java
  11. 3 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/UserView.java
  12. 22 0
      netflix-dao/src/main/java/com/cyksj/model/request/ChangeRelationReq.java
  13. 5 0
      netflix-dao/src/main/java/com/cyksj/model/request/OrderPayRequest.java
  14. 9 3
      netflix-dao/src/main/java/com/cyksj/model/request/OrderRefundReq.java
  15. 0 31
      netflix-dao/src/main/java/com/cyksj/model/request/ZfbOrderRefundReq.java
  16. 32 0
      netflix-dao/src/main/java/com/cyksj/model/views/EmptyGroupsRelationView.java
  17. 3 0
      netflix-dao/src/main/java/com/cyksj/model/views/UserDistributeOrdersDetailView.java
  18. 5 0
      netflix-dao/src/main/java/com/cyksj/model/views/UserPersonalView.java
  19. 2 2
      netflix-dao/src/main/java/com/cyksj/model/views/YHLXUserDetailView.java
  20. 3 4
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpServiceImpl.java
  21. 6 0
      netflix-service/src/main/java/com/cyksj/service/coupon/CouponFontService.java
  22. 21 0
      netflix-service/src/main/java/com/cyksj/service/coupon/impl/CouponFontServiceImpl.java
  23. 18 0
      netflix-service/src/main/java/com/cyksj/service/mange/CmsOrderDonService.java
  24. 3 3
      netflix-service/src/main/java/com/cyksj/service/mange/coupon/impl/CouponCommonServiceImpl.java
  25. 453 0
      netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java
  26. 5 18
      netflix-service/src/main/java/com/cyksj/service/order/OrderDonService.java
  27. 224 566
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
  28. 2 2
      netflix-service/src/main/java/com/cyksj/service/register/RegisterOrderDonService.java
  29. 6 6
      netflix-service/src/main/java/com/cyksj/service/register/impl/RegisterOrderDonServiceImpl.java
  30. 31 30
      netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationClearServiceImpl.java
  31. 12 2
      netflix-service/src/main/java/com/cyksj/service/user/UserBenefitsService.java
  32. 2 0
      netflix-service/src/main/java/com/cyksj/service/user/UserService.java
  33. 27 5
      netflix-service/src/main/java/com/cyksj/service/user/impl/UserBenefitsServiceImpl.java
  34. 7 0
      netflix-service/src/main/java/com/cyksj/service/user/impl/UserServiceImpl.java
  35. 6 4
      netflix-service/src/main/java/com/cyksj/task/Scheduler.java
  36. 2 1
      netflix-web/src/main/java/com/cyksj/config/SaTokenConfigure.java
  37. 0 1
      netflix-web/src/main/java/com/cyksj/web/controller/cps/CpsPopularizeController.java
  38. 108 30
      netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java
  39. 3 8
      netflix-web/src/main/java/com/cyksj/web/controller/manage/distribute/DistributeController.java
  40. 3 3
      netflix-web/src/main/java/com/cyksj/web/controller/manage/register/CmsRegisterController.java
  41. 9 52
      netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java
  42. 11 9
      netflix-web/src/main/java/com/cyksj/web/controller/user/DistributePopularizeController.java
  43. 14 0
      netflix-web/src/main/java/com/cyksj/web/controller/user/UserController.java

+ 2 - 0
netflix-common/src/main/java/com/cyksj/common/constant/Constant.java

@@ -114,4 +114,6 @@ public interface Constant {
 	 * 修改密码短息通知过滤平台
 	 */
 	List<Long> CHANGE_ACCOUNT_PWD_FILTER_GIDS = List.of(18l, 26l);
+
+	String ZK_MOBILE = "18268325725";
 }

+ 12 - 0
netflix-dao/src/main/java/com/cyksj/mapper/UserMapper.java

@@ -35,4 +35,16 @@ public interface UserMapper extends BaseMapper<User> {
 
     @Update("update user set points = points - #{deductPoints} where id = #{id} and points = #{originalPoints}")
     Integer subPointsNegative(@Param("id") Long userId, @Param("originalPoints") BigDecimal originalPoints, @Param("deductPoints") BigDecimal deductPoints);
+
+    @Update("update user set balance = balance - #{deduct} where id = #{userId} and balance = #{userBalance} and balance >= #{deduct}")
+    Integer deductBalance(@Param("userId") Long userId, @Param("deduct") BigDecimal deduct, @Param("userBalance") BigDecimal userBalance);
+
+    @Update("update user set balance = balance + #{returnBalance} where id = #{userId} and balance = #{userBalance}")
+    Integer returnBalance(@Param("userId") Long userId, @Param("returnBalance") BigDecimal returnBalance, @Param("userBalance") BigDecimal userBalance);
+
+    @Update("update user set balance = balance + #{addBalance} where id = #{userId} and balance = #{userBalance}")
+    Integer addUserBalance(@Param("userId") Long userId, @Param("userBalance") BigDecimal userBalance, @Param("addBalance") BigDecimal addBalance);
+
+    @Update("update user set balance = balance - #{subBalance} where id = #{userId} and balance = #{userBalance}")
+    Integer deductUserBalance(@Param("userId") Long userId, @Param("userBalance") BigDecimal userBalance, @Param("subBalance") BigDecimal subBalance);
 }

+ 8 - 0
netflix-dao/src/main/java/com/cyksj/mapper/channel/ChannelPopularizeMapper.java

@@ -2,10 +2,18 @@ package com.cyksj.mapper.channel;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.ChannelPopularize;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
 
 /**
  * @author chan
  * @date 2022/12/13 2:29 PM
  */
 public interface ChannelPopularizeMapper extends BaseMapper<ChannelPopularize> {
+	@Select("select id from channel_popularize where channel_id = #{channelId}")
+	List<Long> getPopularizeIdByChannelId(Long channelId);
+
+	@Select("select c.name from (select channel_id from channel_popularize where id = #{popularizeId}) cp inner join channel c on c.id = cp.channel_id")
+	String selectChannelByPid(Long popularizeId);
 }

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/DistributeWaitingSendPoints.java

@@ -29,6 +29,8 @@ public class DistributeWaitingSendPoints extends BaseEntity{
 
 	private BigDecimal points;
 
+	private BigDecimal refundPoints;
+
 	/**
 	 * 退款状态不再发
 	 */

+ 19 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java

@@ -1,5 +1,6 @@
 package com.cyksj.model.entity;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import lombok.*;
 
 import java.io.Serializable;
@@ -52,6 +53,11 @@ public class OrderDon extends BaseEntity implements Serializable {
      */
     private BigDecimal refundMoney;
 
+    /**
+     * 退款余额
+     */
+    private BigDecimal refundBalance;
+
     /**
      * 退款原因
      */
@@ -177,6 +183,18 @@ public class OrderDon extends BaseEntity implements Serializable {
      */
     private Boolean isKickOut;
 
+    /**
+     * 余额支付
+     */
+    private BigDecimal balance;
+
+    /**
+     * 付款说明
+     */
+    @TableField(exist = false)
+    private String payDesc;
+
+
     public enum Status {
         /**
          * 订单状态
@@ -210,6 +228,7 @@ public class OrderDon extends BaseEntity implements Serializable {
         LOTTERY("lottery","抽奖"),
         EQUIPMENT("equipment", "实物设备分销"),
         PAY_EQUIPMENT("pay_equipment", "自购设备"),
+        BALANCE("balance", "余额支付")
         ;
         String type;
         String desc;

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/User.java

@@ -53,6 +53,11 @@ public class User extends BaseEntity implements Serializable{
 
     private BigDecimal activePoints;
 
+    /**
+     * 余额 分
+     */
+    private BigDecimal balance;
+
     /**
      * 推广id
      */

+ 2 - 1
netflix-dao/src/main/java/com/cyksj/model/entity/UserTicketClearedRecord.java

@@ -43,7 +43,8 @@ public class UserTicketClearedRecord extends BaseEntity{
 		distribute("分销权益"),
 		pay_equipment("购买实物权益"),
 		specific("抽奖"),
-		black("拉黑")
+		black("拉黑"),
+		change_ticket("更换车票"),
 		;
 		String desc;
 

+ 83 - 0
netflix-dao/src/main/java/com/cyksj/model/manage/views/CorpWxOrderDonView.java

@@ -0,0 +1,83 @@
+package com.cyksj.model.manage.views;
+
+import com.cyksj.model.entity.OrderDon;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: CorpWxOrderDonView
+ *创建者: JavaZou
+ *创建时间:2023/4/28 10:19
+ */
+@Getter
+@Setter
+@SearchBean(tables = "order_don o left join goods_don_sku sku on o.sku_id = sku.id " +
+		"left join goods_don g on g.id = sku.goods_id " +
+		"left join groups_relation gr on gr.user_id = o.user_id")
+public class CorpWxOrderDonView {
+	@DbField("o.id")
+	private Long id;
+
+	@DbField("o.relation_id")
+	private Long relationId;
+
+	@DbField("g.type")
+	private Integer goodsDonType;
+
+	@DbField("g.second_type")
+	private Integer secondType;
+
+	@DbField("o.order_no")
+	private String orderNo;
+
+	@DbField("g.logo")
+	private String logo;
+
+	@DbField("g.title")
+	private String title;
+
+	@DbField("gr.account")
+	private String account;
+
+	@DbField("g.id")
+	private Long goodsId;
+
+	@DbField("sku.spec_val")
+	private String specVal;
+
+	@DbField("o.user_id")
+	private Long userId;
+
+	@DbField("o.money")
+	private BigDecimal money;
+
+	@DbField("o.balance")
+	private BigDecimal balance;
+
+	@DbField("o.refund_money")
+	private BigDecimal refundMoney;
+
+	@DbField("o.status")
+	private OrderDon.Status status;
+
+	@DbField("o.type")
+	private OrderDon.Type type;
+
+	@DbField("o.order_type")
+	private Integer orderType;
+
+	@DbField("o.num")
+	private Integer num;
+
+	@DbField("o.created_time")
+	private Date createdTime;
+
+	@DbField("o.pay_time")
+	private Date payTime;
+}

+ 213 - 0
netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonBackView.java

@@ -0,0 +1,213 @@
+package com.cyksj.model.manage.views;
+
+import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.entity.OrderDonTransport;
+import com.cyksj.model.entity.OrderDonWaybill;
+import com.cyksj.model.views.GoodsDonSkuView;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: OrderDonBackView
+ *创建者: JavaZou
+ *创建时间:2023/4/26 17:49
+ */
+@Getter
+@Setter
+@SearchBean(tables = "order_don o left join goods_don_sku sku on o.sku_id = sku.id " +
+		"left join goods_don g on g.id = sku.goods_id " +
+		"left join user u on o.user_id = u.id " +
+		"left join groups_relation gr on gr.id = o.relation_id " +
+		"left join order_don_transport odt on odt.order_id = o.id"
+)
+public class OrderDonBackView {
+	@DbField("o.id")
+	private Long id;
+
+	@DbIgnore
+	private String channel;
+
+	@DbField("o.relation_id")
+	private Long relationId;
+
+	@DbField("gr.groups_id")
+	private Long groupsId;
+
+	@DbField("gr.account")
+	private String account;
+
+	/**
+	 * 主账号
+	 */
+	@DbIgnore
+	private String gtAccount;
+
+	@DbField("g.type")
+	private Integer goodsDonType;
+
+	@DbField("g.second_type")
+	private Integer secondType;
+
+	@DbField("o.order_no")
+	private String orderNo;
+
+	@DbField("o.transaction_id")
+	private String transactionId;
+
+	@DbField("g.logo")
+	private String logo;
+
+	@DbField("g.title")
+	private String title;
+
+	@DbField("g.id")
+	private Long goodsId;
+
+	@DbField("sku.is_benefits")
+	private Boolean isBenefits;
+
+	@DbField("sku.benefits_list")
+	@JsonIgnore
+	private String benefitsList;
+
+	@DbField("sku.spec_val")
+	private String specVal;
+
+	@DbField("sku.months")
+	private Integer months;
+
+	@DbField("o.user_id")
+	private Long userId;
+
+	@DbField("u.nickname")
+	private String nickname;
+
+	@DbField("case when u.login_phone != '' then 'phone' when u.email != '' then 'email' when u.open_id != '' then 'wechat' else 'wechat' end")
+	private String register;
+
+	/**
+	 * 登录注册手机号
+	 */
+	@DbField("u.login_phone")
+	private String loginPhone;
+
+	/**
+	 * 登录注册邮箱
+	 */
+	@DbField("u.email")
+	private String email;
+
+	@DbField("o.money")
+	private BigDecimal money;
+
+	@DbField("o.balance")
+	private BigDecimal balance;
+
+	@DbField("o.refund_money")
+	private BigDecimal refundMoney;
+
+	@DbField("o.status")
+	private OrderDon.Status status;
+
+	@DbField("o.type")
+	private OrderDon.Type type;
+
+	@DbField("o.order_type")
+	private Integer orderType;
+
+	@DbField("o.source")
+	private OrderDon.Source source;
+
+	@DbField("o.num")
+	private Integer num;
+
+	@DbField("o.created_time")
+	private Date createdTime;
+
+	@DbField("o.coupon_user_id")
+	private Long couponUserId;
+
+	@DbIgnore
+	private String couponName;
+
+	@DbField("o.coupon_money")
+	private BigDecimal couponMoney;
+
+	@DbField("o.popularize_id")
+	private Long popularizeId;
+
+	@DbField("o.pay_time")
+	private Date payTime;
+
+	@DbField("o.ex_code")
+	private String exCode;
+
+	@DbField("o.sub_node")
+	private String subNode;
+
+	/**
+	 * 收货人
+	 */
+	@DbField("odt.receiver")
+	private String receiver;
+
+	/**
+	 * 手机号
+	 */
+	@DbField("odt.phone")
+	private String phone;
+
+	/**
+	 * 省份
+	 */
+	@DbField("odt.province")
+	private String province;
+
+	/**
+	 * 城市
+	 */
+	@DbField("odt.city")
+	private String city;
+
+	/**
+	 * 区/县
+	 */
+	@DbField("odt.district")
+	private String district;
+
+	/**
+	 * 详细地址
+	 */
+	@DbField("odt.address")
+	private String address;
+
+	@DbIgnore
+	private OrderDonWaybill waybill;
+
+	@DbIgnore
+	private OrderDonTransport transport;
+
+	@DbIgnore
+	private String beforeServiceName;
+
+	@DbIgnore
+	private String afterServiceName;
+
+	/**
+	 * 是否可领取实物购买权益
+	 */
+	@DbIgnore
+	private Boolean isHasBenefits;
+
+	@DbIgnore
+	private List<GoodsDonSkuView> benefitsViews;
+}

+ 9 - 86
netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonView.java

@@ -1,7 +1,6 @@
 package com.cyksj.model.manage.views;
 
 import com.cyksj.model.entity.OrderDon;
-import com.cyksj.model.entity.OrderDonTransport;
 import com.cyksj.model.entity.OrderDonWaybill;
 import com.cyksj.model.views.GoodsDonSkuView;
 import com.ejlchina.searcher.bean.DbField;
@@ -23,38 +22,15 @@ import java.util.List;
 @Setter
 @SearchBean(tables = "order_don o left join goods_don_sku sku on o.sku_id = sku.id " +
         "left join goods_don g on g.id = sku.goods_id " +
-        "left join user u on o.user_id = u.id " +
-        "left join groups_relation gr on gr.id = o.relation_id " +
-        "left join channel_popularize cp on o.popularize_id = cp.id " +
-        "left join channel c on c.id = cp.channel_id " +
-        "left join order_don_transport odt on odt.order_id = o.id"
-)
+        "left join order_don_transport odt on odt.order_id = o.id")
 public class OrderDonView {
 
     @DbField("o.id")
     private Long id;
 
-    @DbField("c.id")
-    private Long channelId;
-
-    @DbField("c.name")
-    private String channel;
-
     @DbField("o.relation_id")
     private Long relationId;
 
-    @DbField("gr.groups_id")
-    private Long groupsId;
-
-    @DbField("gr.account")
-    private String account;
-
-    /**
-     * 主账号
-     */
-    @DbIgnore
-    private String gtAccount;
-
     @DbField("g.type")
     private Integer goodsDonType;
 
@@ -64,9 +40,6 @@ public class OrderDonView {
     @DbField("o.order_no")
     private String orderNo;
 
-    @DbField("o.transaction_id")
-    private String transactionId;
-
     @DbField("g.logo")
     private String logo;
 
@@ -92,36 +65,22 @@ public class OrderDonView {
     @DbField("sku.months")
     private Integer months;
 
-    @DbField("u.id")
+    @DbField("o.user_id")
     private Long userId;
 
-    @DbField("u.nickname")
-    private String nickname;
-
-    @DbField("case when u.login_phone != '' then 'phone' when u.email != '' then 'email' when u.open_id != '' then 'wechat' else 'wechat' end")
-    private String register;
-
-    /**
-     * 登录注册手机号
-     */
-    @DbField("u.login_phone")
-    private String loginPhone;
-
-    /**
-     * 登录注册邮箱
-     */
-    @DbField("u.email")
-    private String email;
-
     @DbField("o.money")
     private BigDecimal money;
 
-    @DbField("o.coupon_money")
-    private BigDecimal couponMoney;
-
     @DbField("o.refund_money")
+    @JsonIgnore
     private BigDecimal refundMoney;
 
+    @DbField("o.balance")
+    private BigDecimal balance;
+
+    @DbField("o.coupon_money")
+    private BigDecimal couponMoney;
+
     @DbField("o.refund_desc")
     private String refundDesc;
 
@@ -131,39 +90,12 @@ public class OrderDonView {
     @DbField("o.type")
     private OrderDon.Type type;
 
-    @DbField("o.order_type")
-    private Integer orderType;
-
-    @DbField("o.source")
-    private OrderDon.Source source;
-
-    @DbField("o.label_source")
-    private String labelSource;
-
-    @DbField("o.num")
-    private Integer num;
-
     @DbField("o.created_time")
     private Date createdTime;
 
-    @DbField("o.coupon_user_id")
-    private Long couponUserId;
-
-    @DbIgnore
-    private String couponName;
-
-    @DbField("cp.id")
-    private Long popularizeId;
-
     @DbField("o.pay_time")
     private Date payTime;
 
-    @DbField("o.ex_code")
-    private String exCode;
-
-    @DbField("o.sub_node")
-    private String subNode;
-
     /**
      * 收货人
      */
@@ -203,15 +135,6 @@ public class OrderDonView {
     @DbIgnore
     private OrderDonWaybill waybill;
 
-    @DbIgnore
-    private OrderDonTransport transport;
-
-    @DbIgnore
-    private String beforeServiceName;
-
-    @DbIgnore
-    private String afterServiceName;
-
     /**
      * 是否可领取实物购买权益
      */

+ 3 - 0
netflix-dao/src/main/java/com/cyksj/model/manage/views/UserView.java

@@ -61,6 +61,9 @@ public class UserView {
     @DbField("u.points")
     private BigDecimal points;
 
+    @DbField("u.balance")
+    private BigDecimal balance;
+
     @DbField("case when u.login_phone != '' then 'phone' when u.email != '' then 'email' when u.open_id != '' then 'wechat' else 'wechat' end")
     private String register;
 

+ 22 - 0
netflix-dao/src/main/java/com/cyksj/model/request/ChangeRelationReq.java

@@ -0,0 +1,22 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+/*
+ *项目名: netflix
+ *文件名: ChangeRelationReq
+ *创建者: JavaZou
+ *创建时间:2023/6/5 14:08
+ */
+@Getter
+@Setter
+public class ChangeRelationReq {
+	@NotNull(message = "订单id为空")
+	private Long orderId;
+
+	@NotNull(message = "新车队id为空")
+	private Long groupId;
+}

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/request/OrderPayRequest.java

@@ -89,4 +89,9 @@ public class OrderPayRequest {
      * 订单来源
      */
     private OrderDon.Source source;
+
+    /**
+     * 余额支付
+     */
+    private BigDecimal balance;
 }

+ 9 - 3
netflix-dao/src/main/java/com/cyksj/model/request/WxOrderRefundReq.java → netflix-dao/src/main/java/com/cyksj/model/request/OrderRefundReq.java

@@ -8,13 +8,13 @@ import java.math.BigDecimal;
 
 /*
  *项目名: netflix
- *文件名: WxOrderRefundReq
+ *文件名: OrderRefundReq
  *创建者: JavaZou
- *创建时间:2022/11/8 16:51
+ *创建时间:2023/5/31 12:14
  */
 @Getter
 @Setter
-public class WxOrderRefundReq {
+public class OrderRefundReq {
 	@NotNull(message = "订单id不为空")
 	private Long orderId;
 
@@ -28,4 +28,10 @@ public class WxOrderRefundReq {
 	 * 退款原因
 	 */
 	private String refundDesc;
+
+	/**
+	 * 退款类型
+	 * 订单金额、余额
+	 */
+	private String refundType;
 }

+ 0 - 31
netflix-dao/src/main/java/com/cyksj/model/request/ZfbOrderRefundReq.java

@@ -1,31 +0,0 @@
-package com.cyksj.model.request;
-
-import lombok.Getter;
-import lombok.Setter;
-
-import javax.validation.constraints.NotNull;
-import java.math.BigDecimal;
-
-/*
- *项目名: netflix
- *文件名: ZfbOrderRefundReq
- *创建者: JavaZou
- *创建时间:2022/11/8 16:51
- */
-@Getter
-@Setter
-public class ZfbOrderRefundReq {
-	@NotNull(message = "订单id不为空")
-	private Long orderId;
-
-	/**
-	 * 退款金额
-	 */
-	@NotNull(message = "退款金额不为空")
-	private BigDecimal refundMoney;
-
-	/**
-	 * 退款原因
-	 */
-	private String refundDesc;
-}

+ 32 - 0
netflix-dao/src/main/java/com/cyksj/model/views/EmptyGroupsRelationView.java

@@ -0,0 +1,32 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: EmptyGroupsRelationView
+ *创建者: JavaZou
+ *创建时间:2023/6/5 14:01
+ */
+@Getter
+@Setter
+@SearchBean(tables = "account a inner join groups_trips gt on gt.account_id = a.id")
+public class EmptyGroupsRelationView {
+	@DbField("a.id")
+	private Long accountId;
+
+	@DbField("a.account")
+	private String account;
+
+	@DbField("gt.id")
+	private Long skuId;
+
+	@DbField("gt.id")
+	private Long groupsId;
+
+	@DbField("gt.available_num")
+	private Integer availableNum;
+}

+ 3 - 0
netflix-dao/src/main/java/com/cyksj/model/views/UserDistributeOrdersDetailView.java

@@ -44,6 +44,9 @@ public class UserDistributeOrdersDetailView {
 	@DbField("TRUNCATE(dp.points/1000,2)")
 	private BigDecimal reMoney = BigDecimal.ZERO;
 
+	@DbField("if(send_status = 'success',TRUNCATE(dp.refund_points/1000,2),0)")
+	private BigDecimal rdMoney;
+
 	@DbField("dp.send_status")
 	private String sendStatus;
 

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/views/UserPersonalView.java

@@ -29,6 +29,11 @@ public class UserPersonalView {
 	 */
 	private BigDecimal activePoints;
 
+	/**
+	 * 我的余额
+	 */
+	private BigDecimal balance;
+
 	/**
 	 * 推广积分
 	 */

+ 2 - 2
netflix-dao/src/main/java/com/cyksj/model/views/YHLXUserDetailView.java

@@ -2,7 +2,7 @@ package com.cyksj.model.views;
 
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.cyksj.model.entity.Address;
-import com.cyksj.model.manage.views.OrderDonView;
+import com.cyksj.model.manage.views.CorpWxOrderDonView;
 import com.ejlchina.searcher.bean.DbField;
 import com.ejlchina.searcher.bean.DbIgnore;
 import com.ejlchina.searcher.bean.SearchBean;
@@ -60,5 +60,5 @@ public class YHLXUserDetailView {
 	 */
 	@DbIgnore
 	@TableField(exist = false)
-	private List<OrderDonView> orderDonViews;
+	private List<CorpWxOrderDonView> orderDonViews;
 }

+ 3 - 4
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpServiceImpl.java

@@ -19,11 +19,10 @@ import com.cyksj.mapper.corp.CorpUserAuthMapper;
 import com.cyksj.mapper.corp.CorpUserMapper;
 import com.cyksj.model.dto.WxCorpEncryptDto;
 import com.cyksj.model.entity.*;
-import com.cyksj.model.manage.views.OrderDonView;
+import com.cyksj.model.manage.views.CorpWxOrderDonView;
 import com.cyksj.model.request.corp.CorpXmlMessage;
 import com.cyksj.model.request.corp.CorpXmlOutMessage;
 import com.cyksj.model.response.CorpUserInfo;
-import com.cyksj.model.views.RenewalView;
 import com.cyksj.model.views.TicketView;
 import com.cyksj.model.views.YHLXUserDetailView;
 import com.cyksj.redis.RedisService;
@@ -347,8 +346,8 @@ public class CorpServiceImpl implements CorpService {
 				return true;
 			}).collect(Collectors.toList());
 			view.setTicketViews(tkCollect);
-			List<OrderDonView> orderDonViews = beanSearcher.searchAll(OrderDonView.class, MapUtils.builder()
-					.field(OrderDonView::getUserId, view.getUserId()).op(Operator.Equal)
+			List<CorpWxOrderDonView> orderDonViews = beanSearcher.searchAll(CorpWxOrderDonView.class, MapUtils.builder()
+					.field(CorpWxOrderDonView::getUserId, view.getUserId()).op(Operator.Equal)
 					.build());
 			view.setOrderDonViews(orderDonViews);
 		}

+ 6 - 0
netflix-service/src/main/java/com/cyksj/service/coupon/CouponFontService.java

@@ -3,6 +3,7 @@ package com.cyksj.service.coupon;
 import cn.hutool.core.date.DateTime;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.cyksj.model.entity.CouponUser;
+import com.cyksj.model.entity.OrderDon;
 import com.cyksj.model.request.CouponCollectReq;
 import com.cyksj.model.views.CouponUserView;
 
@@ -44,4 +45,9 @@ public interface CouponFontService {
 	 * 更新优惠券有效时间
 	 */
 	void updateCouponUserValidTime(Long couponId, CouponUser couponUser);
+
+	/**
+	 * 归还优惠券
+	 */
+	void updateOrderDonCouponStatus(OrderDon orderDon);
 }

+ 21 - 0
netflix-service/src/main/java/com/cyksj/service/coupon/impl/CouponFontServiceImpl.java

@@ -11,6 +11,7 @@ import com.cyksj.mapper.manage.coupon.*;
 import com.cyksj.model.entity.Coupon;
 import com.cyksj.model.entity.CouponActive;
 import com.cyksj.model.entity.CouponUser;
+import com.cyksj.model.entity.OrderDon;
 import com.cyksj.model.request.CouponCollectReq;
 import com.cyksj.model.views.*;
 import com.cyksj.service.coupon.CouponFontService;
@@ -269,4 +270,24 @@ public class CouponFontServiceImpl implements CouponFontService {
 			}
 		});
 	}
+
+	@Override
+	public void updateOrderDonCouponStatus(OrderDon orderDon) {
+		log.info("订单:{}退款,优惠券返还start...", orderDon.getId());
+		CouponUser couponUser = couponUserMapper.selectById(orderDon.getCouponUserId());
+		if (CouponUser.Channel.exCode == couponUser.getChannel()) {
+			//兑换码兑换的优惠券退款时,直接删除
+			couponUserMapper.deleteById(couponUser.getId());
+		} else {
+			CouponUserView couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
+					.field(CouponUserView::getCouponId, couponUser.getCouponId()).op(Operator.Equal)
+					.field(CouponUserView::getUserId, orderDon.getUserId()).op(Operator.Equal)
+					.build());
+			couponUserView.setCouponUserStatus(CouponUser.Status.unused);
+			this.setCouponUserStatus(couponUserView, DateTime.now());
+			couponUser.setStatus(couponUserView.getCouponUserStatus());
+			couponUserMapper.updateById(couponUser);
+		}
+		log.info("订单:{}退款,优惠券返还end...", orderDon.getId());
+	}
 }

+ 18 - 0
netflix-service/src/main/java/com/cyksj/service/mange/CmsOrderDonService.java

@@ -6,9 +6,12 @@ import com.cyksj.model.entity.OrderDonTransport;
 import com.cyksj.model.entity.OrderDonWaybill;
 import com.cyksj.model.excel.ExcelOrderDonInfo;
 import com.cyksj.model.manage.request.ReqSetLogisticsPost;
+import com.cyksj.model.request.ChangeRelationReq;
 import com.cyksj.model.request.ClickOutReq;
+import com.cyksj.model.request.OrderRefundReq;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author chan
@@ -26,4 +29,19 @@ public interface CmsOrderDonService extends IService<OrderDon> {
     OrderDonWaybill setWaybill(ReqSetLogisticsPost reqSetLogisticsPost);
 
 	void kickOutRelationId(long adminId, ClickOutReq req);
+
+	/**
+	 * 订单统一退款
+	 */
+	void unifiedRefund(OrderRefundReq refundReq) throws Exception;
+
+	/**
+	 * 微信退款回调
+	 */
+	void wxRefundNotify(Map<String, String> map) throws Exception;
+
+	/**
+	 * 更换车票
+	 */
+	void changeRelation(ChangeRelationReq req);
 }

+ 3 - 3
netflix-service/src/main/java/com/cyksj/service/mange/coupon/impl/CouponCommonServiceImpl.java

@@ -218,10 +218,10 @@ public class CouponCommonServiceImpl implements CouponCommonService {
 				goodsIds.forEach(goodsId -> {
 					BigDecimal price = goodsDonMapper.selectMinPriceSkuByGoodsId(goodsId);
 					if (price == null) {
-						throw BusinessRuntimeException.getInstance("实物商品不存在");
+						throw BusinessRuntimeException.getInstance("商品不存在");
 					}
 					if (coupon.getReduceMoney().compareTo(price) >= 0) {
-						throw BusinessRuntimeException.getInstance("可减金额必须小于实物最小规格金额");
+						throw BusinessRuntimeException.getInstance("可减金额必须小于最小规格金额");
 					}
 				});
 			}
@@ -242,7 +242,7 @@ public class CouponCommonServiceImpl implements CouponCommonService {
 			} else {
 				CouponDistributePopularize couponDistributePopularize = couponDistributePopularizeMapper.selectOne(Wrappers.lambdaQuery(CouponDistributePopularize.class).eq(CouponDistributePopularize::getExCode, coupon.getExCode()).eq(CouponDistributePopularize::getDeleted, true).select(CouponDistributePopularize::getId).last("limit 1"));
 				if (couponDistributePopularize != null) {
-					throw BusinessRuntimeException.getInstance("优惠券兑换码与推荐管兑换码设置重复");
+					throw BusinessRuntimeException.getInstance("优惠券兑换码与推广者兑换码设置重复");
 				}
 			}
 		}

+ 453 - 0
netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java

@@ -1,29 +1,62 @@
 package com.cyksj.service.mange.impl;
 
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.domain.AlipayTradeRefundModel;
+import com.alipay.api.request.AlipayTradeRefundRequest;
+import com.alipay.api.response.AlipayTradeRefundResponse;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.cyksj.common.constant.Constant;
+import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.common.util.Codec;
 import com.cyksj.common.util.Jsons;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.common.util.Xmls;
+import com.cyksj.config.WeChatConfig;
+import com.cyksj.config.zfb.AliPayClientFactory;
+import com.cyksj.dto.RedisKey;
 import com.cyksj.mapper.*;
+import com.cyksj.mapper.channel.ShopConfigMapper;
 import com.cyksj.mapper.manage.cms.CmsUserMapper;
+import com.cyksj.mapper.manage.distribute.DistributeWaitingSendPointsMapper;
+import com.cyksj.mapper.manage.distribute.UserDistributeMapper;
 import com.cyksj.mapper.stock.GoodsSkuStockRelateMapper;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.excel.ExcelOrderDonInfo;
 import com.cyksj.model.manage.request.ReqSetLogisticsPost;
+import com.cyksj.model.request.ChangeRelationReq;
 import com.cyksj.model.request.ClickOutReq;
+import com.cyksj.model.request.OrderRefundReq;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.coupon.CouponFontService;
+import com.cyksj.service.distribute.equipment.EquipmentDistributeService;
 import com.cyksj.service.mange.CmsOrderDonService;
 import com.cyksj.service.mange.stock.GoodsDonStockService;
+import com.cyksj.service.market.MarketFrontService;
 import com.cyksj.service.order.OrderCommonService;
 import com.cyksj.service.order.UserRealOrderBenefitsService;
+import com.cyksj.service.relation.GroupRelationClearService;
 import com.cyksj.service.template.TemplateCommonService;
+import com.cyksj.service.user.UserBenefitsService;
+import com.cyksj.service.wechat.WeChatService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.dao.DuplicateKeyException;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 
 /**
@@ -57,6 +90,38 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
 
     private final GoodsDonMapper goodsDonMapper;
 
+    private final RedisService redisService;
+
+    private final UserBenefitsService userBenefitsService;
+
+    private final DistributeWaitingSendPointsMapper distributeWaitingSendPointsMapper;
+
+    private final RefundOrderDonMapper refundOrderDonMapper;
+
+    private final UserDistributeMapper userDistributeMapper;
+
+    private final OrderDonMapper orderDonMapper;
+
+    private final MarketFrontService marketFrontService;
+
+    private final EquipmentDistributeService equipmentDistributeService;
+
+    private final CouponFontService couponFontService;
+
+    private final WeChatService weChatService;
+
+    private final WeChatConfig weChatConfig;
+
+    private final ShopConfigMapper shopConfigMapper;
+
+    private final GroupRelationClearService relationClearService;
+
+    private final GroupsRelationMapper relationMapper;
+
+    private final GroupsMapper groupsMapper;
+
+    private final GoodsDonSkuMapper skuMapper;
+
     private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
 
     @Override
@@ -162,4 +227,392 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
             }
         }
     }
+
+    @Override
+    @Transactional(rollbackFor = Throwable.class)
+    public void unifiedRefund(OrderRefundReq refundReq) throws Exception {
+        OrderDon orderDon = orderDonMapper.selectById(refundReq.getOrderId());
+        GoodsDonSku sku = goodsDonSkuMapper.selectById(orderDon.getSkuId());
+        GoodsDon goodsDon = goodsDonMapper.selectById(sku.getGoodsId());
+        if (StrUtil.isEmpty(refundReq.getRefundType())) {
+            refundReq.setRefundType("money");
+        }
+        String refundType = refundReq.getRefundType();
+        //校验订单是否符合退款
+        checkOrderRefund(orderDon, goodsDon, sku, 2, refundReq);
+        OrderDon.Type type = orderDon.getType();
+        String outNo = StrUtil.EMPTY;
+        if (type == OrderDon.Type.WeChat && !refundType.equals(OrderDon.Type.BALANCE.getType())) {
+            outNo = wxRefund(refundReq, orderDon);
+        }
+        if (type == OrderDon.Type.ALI_PAY && !refundType.equals(OrderDon.Type.BALANCE.getType())) {
+            outNo = zfbRefund(refundReq, orderDon);
+        }
+        orderDon.setRefundDesc(refundReq.getRefundDesc());
+        //记录退款信息
+        commonRefund(orderDon, goodsDon, sku, outNo, refundType);
+        //修改订单状态
+        orderDon.setStatus(OrderDon.Status.refund);
+        orderDonMapper.updateById(orderDon);
+    }
+
+    public String wxRefund(OrderRefundReq refundReq, OrderDon orderDon) throws Exception {
+        if (orderDon.getMoney().compareTo(BigDecimal.ZERO) <=0){
+            return StrUtil.EMPTY;
+        }
+        long time = System.currentTimeMillis();
+        //退款订单号
+        String outRefundNo = Codec.DoDigest.custom()
+                .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
+                .setStringData(time + String.valueOf(orderDon.getId()))
+                .toHexString();
+        weChatService.refundWxOrder(weChatConfig.getAppid(),
+                Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.WX_DEFAULT_APP_ID),
+                orderDon.getTransactionId(),
+                outRefundNo,
+                orderDon.getMoney(),
+                refundReq.getRefundMoney(),
+                weChatConfig.getDomain() + "manage/order/wx/refund/notify");
+        //记录退款信息
+        return outRefundNo;
+    }
+
+    @Override
+    public void wxRefundNotify(Map<String, String> map) throws Exception {
+        // 对退款数据 解密
+        String md5Key = Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5).setStringData(shopConfigMapper.getByAppId(map.get("mch_id")).getSecret()).toHexString();
+        String reqInfoXml = Codec.DoCipher.custom().setAlgorithm(Codec.DoCipher.Trans.AES_ECB_PKCS7Padding).setBase64Data(map.get("req_info")).setStringKey(md5Key).ofDecrypt().toText();
+        log.info("=== 退款数据解密: \n {}", reqInfoXml);
+        Map<String, String> reqInfoMap = Xmls.toMap(reqInfoXml);
+        // 退款失败
+        if (StrUtil.equals("CHANGE", reqInfoMap.get("refund_status"))) {
+            throw BusinessRuntimeException.getInstance("退款异常.");
+        } else if (StrUtil.equals("REFUNDCLOSE", reqInfoMap.get("refund_status"))) {
+            throw BusinessRuntimeException.getInstance("退款关闭.");
+        }
+        /* 此处处理业务逻辑 */
+        String outRefundNo = reqInfoMap.get("out_refund_no");
+        String transactionId = reqInfoMap.get("transaction_id");
+        String refundFee = reqInfoMap.get("refund_fee");
+        OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getTransactionId, transactionId).last("limit 1"));
+        if (orderDon == null) {
+            log.error("退款回调,transactionId:{}订单不存在", transactionId);
+            return;
+        }
+        log.info("微信退款: 退款单号[{}] 退款成功.", outRefundNo);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Throwable.class)
+    public void changeRelation(ChangeRelationReq req) {
+        Long orderId = req.getOrderId();
+        Long groupId = req.getGroupId();
+        OrderDon orderDon = this.getById(orderId);
+        Assert.notNull(orderDon, "订单不存在");
+        if (orderDon.getRelationId() == 0) throw BusinessRuntimeException.getInstance("车票不存在");
+        GroupsRelation relation = relationMapper.selectById(orderDon.getRelationId());
+        Assert.notNull(relation, "车票已不存在");
+        //清除原先车票
+        relationClearService.clearTicket(relation, UserTicketClearedRecord.Source.change_ticket);
+        //移入新车队
+        LambdaQueryWrapper<GroupsTrips> queryWrapper = Wrappers.lambdaQuery(GroupsTrips.class)
+                .eq(GroupsTrips::getSkuId, orderDon.getSkuId())
+                .eq(GroupsTrips::getId, groupId)
+                .gt(GroupsTrips::getAvailableNum, 0).last("limit 1")
+                .ne(GroupsTrips::getStatus, GroupsTrips.Status.down);
+        GroupsTrips groups = groupsMapper.selectOne(queryWrapper);
+        if (groups == null) throw BusinessRuntimeException.getInstance("该新车位人员已满");
+
+        //更新新车队车位
+        GroupsRelation newRelation = getRelationThisTrips(orderDon.getUserId(), groupId, 1);
+        newRelation.setUserId(relation.getUserId());
+        newRelation.setStartTime(relation.getStartTime());
+        newRelation.setExpiryTime(relation.getExpiryTime());
+        newRelation.setCustomerService(relation.getCustomerService());
+        newRelation.setStatus(GroupsRelation.Status.validity);
+        if ((newRelation.getStartTime() == null || newRelation.getExpiryTime() == null) && groups.getStatus() != GroupsTrips.Status.waiting) {
+            List<OrderDon> orderDonList = orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getUserId, relation.getUserId()).eq(OrderDon::getRelationId, relation.getId()).eq(OrderDon::getStatus, OrderDon.Status.hasPayment));
+            orderDonList.forEach((relationOrder) -> {
+                //如果续费增加时间,如果首次则设置当前时间为初始时间
+                Date expiryTime = Optional.ofNullable(newRelation.getExpiryTime()).orElse(new Date());
+                GoodsDonSku donSku = skuMapper.selectById(relationOrder.getSkuId());
+                Date newDate = DateUtil.offset(expiryTime, DateField.MONTH, donSku.getMonths() * relationOrder.getNum());
+                newRelation.setExpiryTime(newDate);
+                newRelation.setStartTime(newRelation.getStartTime() == null ? new Date() : null);
+                relationMapper.updateById(newRelation);
+            });
+        }
+    }
+
+    public String zfbRefund(OrderRefundReq refundReq, OrderDon orderDon) throws Exception {
+        if (orderDon.getMoney().compareTo(BigDecimal.ZERO) <=0){
+            return StrUtil.EMPTY;
+        }
+        AlipayTradeRefundResponse response;
+        //支付宝退款
+        AlipayClient alipayClient = AliPayClientFactory.getAlipayClient(Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.ZFB_DEFAULT_APP_ID));
+        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
+        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
+        //支付宝交易号
+        model.setTradeNo(orderDon.getTransactionId());
+        model.setRefundAmount(refundReq.getRefundMoney().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_EVEN).toString());
+        model.setQueryOptions(List.of("gmt_refund_pay"));
+        long time = System.currentTimeMillis();
+        //退款订单号
+        String outRefundNo = Codec.DoDigest.custom()
+                .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
+                .setStringData(time + String.valueOf(orderDon.getId()))
+                .toHexString();
+        //退款请求号
+        model.setOutRequestNo(outRefundNo);
+        request.setBizModel(model);
+        String shopId = orderDon.getShopId();
+        ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class).eq(ShopConfig::getAppId, orderDon.getShopId()));
+        if (shopId != null && shopConfig != null && shopConfig.getIsCert()) {
+            response = alipayClient.certificateExecute(request);
+        } else {
+            response = alipayClient.execute(request);
+        }
+        if (!response.isSuccess()) throw BusinessRuntimeException.getInstance("退款失败,e:{0}", response.getSubMsg());
+//			AliPayRefundRep aliPayRefundRep = Jsons.parseObject(response.getBody(), AliPayRefundRep.class);
+//			AliPayRefundRep.AlipayTradeRefundResponse res = aliPayRefundRep.getResponse();
+//			orderDon.setRefundMoney(res.getRefundFee().multiply(BigDecimal.valueOf(100)));
+        return outRefundNo;
+    }
+
+    /**
+     * 校验订单是否符合退款操作
+     */
+    private void checkOrderRefund(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku, Integer orderPayType, OrderRefundReq refundReq) {
+        if (orderDon == null) {
+            throw BusinessRuntimeException.getInstance("订单不存在");
+        }
+        String refundType = refundReq.getRefundType();
+        if (orderDon.getType() == OrderDon.Type.BALANCE && !OrderDon.Type.BALANCE.getType().equals(refundType)) {
+            throw BusinessRuntimeException.getInstance("余额支付的订单不支持金额退款");
+        }
+        if (orderDon.getType() != OrderDon.Type.BALANCE && orderDon.getType() != OrderDon.Type.WeChat && orderPayType == 1) {
+            throw BusinessRuntimeException.getInstance("该订单不是微信支付的订单");
+        }
+        if (orderDon.getType() != OrderDon.Type.BALANCE && orderDon.getType() != OrderDon.Type.ALI_PAY && orderPayType == 2) {
+            throw BusinessRuntimeException.getInstance("该订单不是支付宝支付的订单");
+        }
+        if (OrderDon.Status.refund == orderDon.getStatus()) {
+            throw BusinessRuntimeException.getInstance("该订单已退款");
+        }
+        if (OrderDon.Status.noPayment == orderDon.getStatus() || OrderDon.Status.close == orderDon.getStatus()) {
+            throw BusinessRuntimeException.getInstance("订单{0}状态不支持退款", orderDon.getStatus());
+        }
+        if (goodsDon.getType() == 1) {
+            //虚拟原订单是否已过期
+            Integer months = sku.getMonths();
+            Integer num = orderDon.getNum();
+            DateTime now = DateTime.now();
+            DateTime endTime = DateUtil.offsetMonth(orderDon.getCreatedTime(), months * num);
+            if (now.isAfter(endTime)) {
+                throw BusinessRuntimeException.getInstance("该订单车票已过期");
+            }
+            if (orderDon.getOrderType() == 1) {
+                //是否有其他付款订单是否过期
+                OrderDon otherOrder = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+                        .eq(OrderDon::getRelationId, orderDon.getRelationId())
+                        .eq(OrderDon::getUserId, orderDon.getUserId())
+                        .ne(OrderDon::getId, orderDon.getId())
+                        .notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
+                        .eq(OrderDon::getOrderType, 2)
+                        .orderByDesc(OrderDon::getId)
+                        .last("limit 1"));
+                if (otherOrder != null) {
+                    throw BusinessRuntimeException.getInstance("该笔订单存在续费订单,请先退掉续费订单");
+                }
+            }
+        }
+        BigDecimal refundMoney = refundReq.getRefundMoney();
+        BigDecimal money = orderDon.getMoney();
+        //余额退款
+        if (OrderDon.Type.BALANCE.getType().equals(refundType)) {
+            BigDecimal balance = orderDon.getBalance();
+            if (OrderDon.Type.BALANCE == orderDon.getType() && refundMoney.compareTo(balance) > 0) {
+                throw BusinessRuntimeException.getInstance("退款余额大于订单支付余额");
+            }
+            if (OrderDon.Type.BALANCE != orderDon.getType() && refundMoney.compareTo(money) > 0) {
+                throw BusinessRuntimeException.getInstance("退款余额大于订单支付金额");
+            }
+            orderDon.setRefundBalance(refundMoney);
+            orderDon.setRefundMoney(BigDecimal.ZERO);
+        } else {
+            //退金额
+            if (refundMoney.compareTo(money) > 0) {
+                throw BusinessRuntimeException.getInstance("退款金额大于订单支付金额");
+            }
+            orderDon.setRefundMoney(refundMoney);
+        }
+        if (!redisService.setNx(String.format("%s", refundReq.getOrderId()), refundReq.getOrderId(), 10l)) {
+            throw BusinessRuntimeException.getInstance("请勿重复点击,正在退款中..");
+        }
+    }
+
+    /**
+     * 退款后 处理关联
+     */
+    public void commonRefund(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku, String outNo, String refundType) {
+        TASK_POOL.execute(() -> {
+            refundRecord(orderDon, goodsDon, sku, outNo, refundType);
+            //退款一次
+            goodsDonStockService.upStockRelate(null, orderDon.getId(), "refund");
+            try {
+                //虚物订单退款,清出车队,车票变为已过期
+                orderCommonService.clearUnrealGoodsDetail(orderDon, goodsDon.getType(), sku.getMonths());
+            } catch (Exception e) {
+                log.error("清除车票错误:{}", StringUtil.getErrorText(e));
+            }
+            //若存在优惠券使用,退还优惠券
+            if (orderDon.getCouponUserId() != 0) {
+                couponFontService.updateOrderDonCouponStatus(orderDon);
+            }
+            //若存在余额支付,退还余额
+            if (orderDon.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+                userBenefitsService.addUserBalance(orderDon.getUserId(), orderDon.getBalance());
+            }
+            try {
+                marketFrontService.subSpecificChanceNum(orderDon.getUserId(), orderDon.getId(), orderDon.getGoodsId(), orderDon.getUpdateTime());
+                if (goodsDon.getType() == 2) {
+                    equipmentDistributeService.subDistributeBenefitsChance(orderDon.getUserId(), orderDon.getId());
+                    //扣除用户自身实物权益
+                    userRealOrderBenefitsService.deductUserRealGoodsBenefits(orderDon.getId());
+                }
+            } catch (Exception e) {
+                log.error("wx减少抽奖机会错误,orderId:{},error:{}", orderDon.getId(), e);
+            }
+            if (OrderDon.Type.BALANCE.getType().equals(refundType)) {
+                userBenefitsService.addUserBalance(orderDon.getUserId(), orderDon.getRefundBalance());
+            } else {
+                //余额退款不需要扣除积分
+                //分销提成积分扣除
+                deductDsPoints(orderDon);
+            }
+        });
+    }
+
+    /**
+     * 退款 分销提成积分扣除
+     */
+    public void deductDsPoints(OrderDon orderDon) {
+        if (orderDon.getIsDistribute()) {
+            DistributeWaitingSendPoints distributeWaitingSendPoints = distributeWaitingSendPointsMapper.selectOne(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class).eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId()).last("limit 1"));
+            if (distributeWaitingSendPoints != null) {
+                BigDecimal money = orderDon.getMoney();
+                BigDecimal refundMoney = orderDon.getRefundMoney();
+                //已发送 发送中 部分 全额退款
+                DistributeWaitingSendPoints.Status sendStatus = distributeWaitingSendPoints.getSendStatus();
+                BigDecimal points = distributeWaitingSendPoints.getPoints();
+                if (sendStatus == DistributeWaitingSendPoints.Status.waiting) {
+                    if (refundMoney.compareTo(money) == 0) {
+                        //待发送状态 全额退款
+                        distributeWaitingSendPoints.setSendStatus(DistributeWaitingSendPoints.Status.close);
+                        distributeWaitingSendPoints.setRefundPoints(points);
+                        distributeWaitingSendPoints.setPoints(BigDecimal.ZERO);
+                        distributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints);
+                        return;
+                    }
+                    Integer thisGoodsRate = Optional.ofNullable(userDistributeMapper.getDistributeGoodsRateByUidAndGid(orderDon.getUserId(), orderDon.getGoodsId())).orElse(0);
+                    if (thisGoodsRate > 0) {
+                        BigDecimal needSubPoints = refundMoney.multiply(BigDecimal.valueOf(thisGoodsRate).divide(BigDecimal.valueOf(100))).multiply(BigDecimal.valueOf(10));
+                        distributeWaitingSendPoints.setRefundPoints(needSubPoints);
+                        //剩余积分
+                        distributeWaitingSendPoints.setPoints(points.subtract(needSubPoints));
+                        distributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints);
+                    }
+                } else if (sendStatus == DistributeWaitingSendPoints.Status.success) {
+                    //已发送的提成 部分退款 全额退款 需要扣除用户积分
+                    //扣除固定推广者积分
+                    if (refundMoney.compareTo(money) == 0) {
+                        userBenefitsService.deductDistributePoints(orderDon.getId(), distributeWaitingSendPoints.getSharedId(), points);
+                        distributeWaitingSendPoints.setRefundPoints(points);
+                        distributeWaitingSendPoints.setPoints(BigDecimal.ZERO);
+                        distributeWaitingSendPoints.setSendStatus(DistributeWaitingSendPoints.Status.close);
+                        distributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints);
+                        return;
+                    }
+                    Integer thisGoodsRate = Optional.ofNullable(userDistributeMapper.getDistributeGoodsRateByUidAndGid(orderDon.getUserId(), orderDon.getGoodsId())).orElse(0);
+                    if (thisGoodsRate > 0) {
+                        BigDecimal needSubPoints = refundMoney.multiply(BigDecimal.valueOf(thisGoodsRate).divide(BigDecimal.valueOf(100))).multiply(BigDecimal.valueOf(10));
+                        userBenefitsService.deductDistributePoints(orderDon.getId(), distributeWaitingSendPoints.getSharedId(), needSubPoints);
+                        //部分退款
+                        distributeWaitingSendPoints.setRefundPoints(needSubPoints);
+                        distributeWaitingSendPoints.setPoints(points.subtract(needSubPoints));
+                        distributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints);
+                    }
+
+                }
+            }
+        }
+    }
+
+    /**
+     * 记录退款信息
+     */
+    private void refundRecord(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku, String outRefundNo, String refundType) {
+        RefundOrderDon refund = new RefundOrderDon();
+        refund.setOrderId(orderDon.getId());
+        refund.setOutRefundNo(outRefundNo);
+        refund.setTotalFee(orderDon.getMoney());
+        refund.setRefundDesc(orderDon.getRefundDesc());
+        //退款金额
+        refund.setRefundFee(orderDon.getRefundMoney());
+        refund.setUserId(orderDon.getUserId());
+        refund.setGoodsId(orderDon.getGoodsId());
+        refund.setGoodsTitle(goodsDon == null ? null : goodsDon.getTitle());
+        refund.setSkuId(orderDon.getSkuId());
+        refund.setSpecVal(sku == null ? null : String.format("%s%s", sku.getSpecVal(), sku.getPrice().divide(BigDecimal.valueOf(100))));
+        refund.setRefundMethod(orderDon.getType().name());
+        refund.setGoodsType(goodsDon == null ? 0 : goodsDon.getType());
+        //退款方式
+        if (StrUtil.isNotBlank(refundType) && refundType.equals(OrderDon.Type.BALANCE.getType())) {
+            refund.setRefundMethod(refundType);
+            refund.setRefundFee(orderDon.getRefundBalance());
+        }
+        refundOrderDonMapper.insert(refund);
+    }
+
+    /**
+     * 寻找该车队空余车位
+     */
+    public GroupsRelation getRelationThisTrips(Long userId, Long groupsId, Integer retryNum) {
+        //寻找快满编车队进行占座
+        GroupsRelation relation = relationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+                .eq(GroupsRelation::getGroupsId, groupsId)
+                .eq(GroupsRelation::getStatus, "none")
+                .eq(GroupsRelation::getUserId, 0)
+                .orderByDesc(GroupsRelation::getNum)
+                .last("limit 1")
+        );
+        if (relation == null) {
+            throw BusinessRuntimeException.getInstance("该车队人员已满,请选择另一车队");
+        }
+        Long relationId = relation.getId();
+        Object value = redisService.get(RedisKey.GROUPS_RELATION_NUM_KEY + relationId);
+        if (value != null) {
+            relation = relationMapper.selectById(relationId);
+            if (relation != null && relation.getUserId() == 0l) {
+                redisService.del(RedisKey.GROUPS_RELATION_NUM_KEY + relationId);
+            }
+            if (relation != null && relation.getUserId() != 0l) {
+                if (retryNum == 7) throw BusinessRuntimeException.getInstance("该车队人员已满,请选择另一车队");
+                retryNum++;
+                getRelationThisTrips(userId, groupsId, retryNum);
+            }
+        }
+        if (!redisService.setNx(RedisKey.GROUPS_RELATION_NUM_KEY + relationId, userId, 60 * 5L)) {
+            if (retryNum == 7) throw BusinessRuntimeException.getInstance("该车队人员已满,请选择另一车队");
+            retryNum++;
+            getRelationThisTrips(userId, groupsId, retryNum);
+        }
+        int count = groupsMapper.decrAvailableNum(relation.getGroupsId());
+        if (count == 0) {
+            log.error("车位异常 groupId:{}", groupsId);
+            throw new BusinessRuntimeException("车位异常");
+        }
+        return relation;
+    }
 }

+ 5 - 18
netflix-service/src/main/java/com/cyksj/service/order/OrderDonService.java

@@ -6,7 +6,6 @@ import com.alipay.api.response.AlipayFundTransCommonQueryResponse;
 import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
 import com.alipay.api.response.AlipayTradeQueryResponse;
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.cyksj.dto.Result;
 import com.cyksj.model.dto.AliPayParams;
 import com.cyksj.model.dto.H5JsPayParams;
 import com.cyksj.model.entity.OrderDetailView;
@@ -14,8 +13,6 @@ import com.cyksj.model.entity.OrderDon;
 import com.cyksj.model.entity.RealGoodsInterestUser;
 import com.cyksj.model.request.OrderPayRequest;
 import com.cyksj.model.request.TransferAccountsReq;
-import com.cyksj.model.request.WxOrderRefundReq;
-import com.cyksj.model.request.ZfbOrderRefundReq;
 
 import java.util.Date;
 import java.util.List;
@@ -65,21 +62,6 @@ public interface OrderDonService extends IService<OrderDon> {
      */
     void aliPayNotify(Map<String, String> paramsMap) throws Exception;
 
-    /**
-     * 微信退款
-     */
-    Result<String> wxRefund(WxOrderRefundReq wxOrderRefundReq) throws Exception;
-
-    /**
-     * 微信退款回调
-     */
-    void wxRefundNotify(Map<String, String> map) throws Exception;
-
-    /**
-     * 支付宝退款
-     */
-    Result<String> zfbRefund(ZfbOrderRefundReq zfbOrderRefundReq) throws Exception;
-
     List<RealGoodsInterestUser> selectNoPaymentRealGoods();
 
     Integer updateHasPaymentInterestRealGoods();
@@ -107,4 +89,9 @@ public interface OrderDonService extends IService<OrderDon> {
      * 支付宝退款订单详情
      */
     AlipayTradeFastpayRefundQueryResponse getZfbRefundDetail(String shopId, String orderNo) throws AlipayApiException;
+
+    /**
+     * 0元订单确认
+     */
+    void zeroOrderConfirm(Long orderId);
 }

+ 224 - 566
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -16,7 +16,6 @@ import com.alipay.api.request.*;
 import com.alipay.api.response.AlipayFundTransCommonQueryResponse;
 import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
 import com.alipay.api.response.AlipayTradeQueryResponse;
-import com.alipay.api.response.AlipayTradeRefundResponse;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -30,15 +29,12 @@ import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
 import com.cyksj.common.util.Codec;
 import com.cyksj.common.util.Jsons;
 import com.cyksj.common.util.StringUtil;
-import com.cyksj.common.util.Xmls;
 import com.cyksj.config.WeChatConfig;
 import com.cyksj.config.zfb.AliPayClientFactory;
 import com.cyksj.config.zfb.BaseZfbConfig;
 import com.cyksj.config.zfb.ZfbProductCode;
 import com.cyksj.dto.RedisKey;
-import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayApiCode;
-import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.*;
 import com.cyksj.mapper.channel.ShopConfigMapper;
 import com.cyksj.mapper.manage.coupon.CouponDistributePopularizeMapper;
@@ -57,8 +53,9 @@ import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.AccountView;
 import com.cyksj.model.manage.views.GroupsRelationView;
 import com.cyksj.model.manage.views.OrderDonView;
-import com.cyksj.model.request.*;
-import com.cyksj.model.response.AliPayRefundRep;
+import com.cyksj.model.request.OrderAttach;
+import com.cyksj.model.request.OrderPayRequest;
+import com.cyksj.model.request.TransferAccountsReq;
 import com.cyksj.model.views.CouponNewUserView;
 import com.cyksj.model.views.CouponUserView;
 import com.cyksj.model.views.GoodsDonSkuView;
@@ -77,7 +74,7 @@ import com.cyksj.service.relation.GroupRelationClearService;
 import com.cyksj.service.relation.GroupRelationFrontService;
 import com.cyksj.service.sys.SysConfigService;
 import com.cyksj.service.template.TemplateCommonService;
-import com.cyksj.service.user.UserPointsService;
+import com.cyksj.service.user.UserBenefitsService;
 import com.cyksj.service.wechat.WeChatService;
 import com.cyksj.task.JobManager;
 import com.ejlchina.searcher.BeanSearcher;
@@ -93,7 +90,10 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.util.*;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 
 import static com.cyksj.enums.GatewayApiCode.GROUP_INDEX_ALREADY_BE_USER;
 import static com.cyksj.enums.GatewayApiCode.GROUP_INDEX_NOT_FIND;
@@ -107,9 +107,7 @@ import static com.cyksj.enums.GatewayApiCode.GROUP_INDEX_NOT_FIND;
 @RequiredArgsConstructor
 public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> implements OrderDonService {
 
-    private static final Long EXPIRY_TIME = 1000 * 60 * 5L;
-
-    private static final GlobalThreadPoolTaskExecutor THREAD_POOL_TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
+	private static final Long EXPIRY_TIME = 1000 * 60 * 5L;
 
 	private static final Sequence SEQUENCE = new Sequence(0);
 
@@ -207,7 +205,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private final OrderCommonService orderCommonService;
 
-	private final UserPointsService userPointsService;
+	private final UserBenefitsService userBenefitsService;
 
 	private final List<String> noChekGoodsTemp = List.of("Apple One", "Youtube", "Spotify");
 
@@ -222,7 +220,6 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		}
 		GoodsDonSku sku = goodsDonSkuMapper.selectById(payRequest.getSkuId());
 		GoodsDon goodsDon = goodsDonMapper.selectById(sku.getGoodsId());
-		Boolean isNew = false;
 		if (goodsDon == null || !goodsDon.getStatus()) {
 			throw new BusinessRuntimeException("商品已下架");
 		}
@@ -242,54 +239,9 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			checkChatGPTPlusPurchaseLimit(user.getId(), goodsDon.getId(), goodsDon.getTitle(), sku.getId(), sku.getSpecVal());
 			payOpenId = user.getOpenId();
 			if (goodsDon.getType() == 1) {
-				GroupsRelation relation = null;
-				//锁定座位
-				if (payRequest.getRelationId() != null && payRequest.getRelationId() > 0) {
-					relation = groupsRelationMapper.selectById(payRequest.getRelationId());
-					if (relation == null) {
-						throw new BusinessRuntimeException(GROUP_INDEX_NOT_FIND.getCode(), GROUP_INDEX_NOT_FIND.getMsg());
-					}
-					if (relation.getUserId() != null && relation.getUserId() > 0L) {
-						throw new BusinessRuntimeException(GROUP_INDEX_ALREADY_BE_USER.getCode(), GROUP_INDEX_ALREADY_BE_USER.getMsg());
-					}
-
-				} else {
-					//寻找差不多过期时间规格的车位
-					relation = groupRelationFrontService.getSimilarExpiredGroupRelation(sku.getId(), sku.getMonths(), null);
-					if (relation == null) {
-						//TODO 如果没指定座位,等待逻辑确认
-						LambdaQueryWrapper<GroupsTrips> queryWrapper = Wrappers.lambdaQuery(GroupsTrips.class)
-								.eq(GroupsTrips::getSkuId, payRequest.getSkuId())
-								.gt(GroupsTrips::getAvailableNum, 0).last("limit 1")
-								.ne(GroupsTrips::getStatus, GroupsTrips.Status.down)
-								.orderByAsc(GroupsTrips::getAvailableNum);
-						GroupsTrips groups = groupsMapper.selectOne(queryWrapper);
-						if (groups == null) {
-							relation = createNewGroupsTrips(sku);
-							isNew = true;
-						} else {
-							//寻找快满编车队进行占座
-							relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
-									.eq(GroupsRelation::getGroupsId, groups.getId())
-									.eq(GroupsRelation::getStatus, "none")
-									.eq(GroupsRelation::getUserId, 0)
-									.orderByAsc(GroupsRelation::getNum)
-									.last("limit 1")
-							);
-							if (relation == null) {
-								throw new BusinessRuntimeException(GROUP_INDEX_NOT_FIND.getCode(), GROUP_INDEX_NOT_FIND.getMsg());
-							}
-						}
-					}
-				}
-
-				//锁定座位
-				setRelation(relation.getId(), user.getId(), goodsDon.getId(), payRequest.getIsLoginPopularize());
-				relation.setUserId(user.getId());
-				relation.setStatus(GroupsRelation.Status.locking);
-				groupsRelationMapper.updateById(relation);
-				relationId = relation.getId();
-			}else {
+				//获取座位
+				relationId = getRelation(payRequest, sku);
+			} else {
 
 			}
 		}
@@ -309,36 +261,12 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		orderDon.setRealMoney(payRequest.getDonMoney());
 
 		//渠道链接id
-		if(payRequest.getPopularizeId() != null){
+		if (payRequest.getPopularizeId() != null) {
 			orderDon.setPopularizeId(payRequest.getPopularizeId());
 		}
-		//存在优惠券
-		if (couponUser != null) {
-			Long couponId = couponUser.getCouponId();
-			Coupon coupon = couponMapper.getCouponById(couponId);
-			BigDecimal couponMoney = null;
-			if (coupon.getType() == Coupon.Type.discount) {
-				BigDecimal discountMoney = payRequest.getDonMoney().multiply(coupon.getDiscount());
-				orderDon.setMoney(discountMoney);
-				couponMoney = payRequest.getDonMoney().subtract(discountMoney);
-			} else if (coupon.getType() == Coupon.Type.reduce) {
-				if (goodsDon.getType() == Constant.realGoodsType.get(0)) {
-					BigDecimal reduceCondition = coupon.getReduceCondition();
-					if (reduceCondition.compareTo(BigDecimal.ZERO) > 0 && reduceCondition.compareTo(payRequest.getDonMoney()) > 0) {
-						throw BusinessRuntimeException.getInstance(String.format("满%s可用", reduceCondition.divide(BigDecimal.valueOf(100))));
-					}
-				}
-				BigDecimal subtractMoney = payRequest.getDonMoney().subtract(coupon.getReduceMoney());
-				orderDon.setMoney(subtractMoney);
-				couponMoney = payRequest.getDonMoney().subtract(subtractMoney);
-			}
-			orderDon.setCouponMoney(couponMoney);
-			orderDon.setCouponUserId(couponUser.getId());
-			orderDon.setDsPopularizeId(couponUser.getPopularizeId());
-		}
-		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) < 0) {
-			throw BusinessRuntimeException.getInstance("支付金额小于0");
-		}
+		//扣除使用优惠券、余额的订单金额
+		deductOrderMoney(orderDon, couponUser, payRequest.getBalance(), user);
+
 		orderDon.setSkuId(payRequest.getSkuId());
 		orderDon.setRelationId(relationId);
 		orderDon.setNum(payRequest.getNum());
@@ -373,29 +301,17 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				throw BusinessRuntimeException.getInstance("车位与车队规格不一致");
 			}
 		}
+		orderDon.setPayDesc(goodsDon.getPayDesc());
 		this.saveOrUpdate(orderDon);
 
 		if (Constant.realGoodsType.contains(goodsDon.getType()) && !goodsDon.getId().equals(27l)) {
 			saveTransport(payRequest.getAddressId(), orderDon.getId());
 		}
-		if (isNew) {
-			//新增空车队发送模板消息
-			TASK_POOL.execute(() -> {
-				try {
-					List<CustomerService> customerServices = customerServiceMapper.selectList(null);
-					for (CustomerService customerService : customerServices) {
-						log.info("发送客服{}空车位通知消息", customerService.getNickName());
-						templateCommonService.emptySendMsgNotify(orderDon, customerService.getOpenId());
-					}
-				} catch (Exception e) {
-					log.error("发送{}空车位模板消息至客服失败:{}", orderDon.getGoodsId(), e);
-				}
+		if (orderDon.getStatus() == OrderDon.Status.noPayment) {
+			jobManager.addJob(EXPIRY_TIME, () -> {
+				closeOrder(orderDon.getId());
 			});
 		}
-		jobManager.addJob(EXPIRY_TIME, () -> {
-			closeOrder(orderDon.getId());
-		});
-
 		return orderDon;
 	}
 
@@ -413,7 +329,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		//获取微信默认商户
 		String shopId = sysConfigService.getDefaultShopConfigByType("wx");
 
-		ShopConfig shopConfig = shopConfigMapper.getByChannelPopularizeId(orderDon.getPopularizeId(),"wx");
+		ShopConfig shopConfig = shopConfigMapper.getByChannelPopularizeId(orderDon.getPopularizeId(), "wx");
 		if (shopConfig != null) {
 			shopId = shopConfig.getAppId();
 		}
@@ -451,7 +367,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		//获取微信默认商户
 		String shopId = sysConfigService.getDefaultShopConfigByType("wx");
 
-		ShopConfig shopConfig = shopConfigMapper.getByChannelPopularizeId(orderDon.getPopularizeId(),"wx");
+		ShopConfig shopConfig = shopConfigMapper.getByChannelPopularizeId(orderDon.getPopularizeId(), "wx");
 		if (shopConfig != null) {
 			shopId = shopConfig.getAppId();
 		}
@@ -570,7 +486,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		//购买Midjourney课程 赠送Midjourney车票
 		if (goodsId != null && goodsId.equals(27l)) {
 			order.setStatus(OrderDon.Status.complete);
-			TASK_POOL.execute(()->sendMidjourneyTicketIfCourse(order.getUserId()));
+			TASK_POOL.execute(() -> sendMidjourneyTicketIfCourse(order.getUserId()));
 		}
 		this.updateById(order);
 		//配置了特定奖池,增加抽奖机会
@@ -618,7 +534,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				GroupsRelation groupsRelation = groupsRelationMapper.selectById(orderDon.getRelationId());
 				if (groupsRelation != null) {
 					if (!groupsRelation.getUserId().equals(orderDon.getUserId())) {
-						TASK_POOL.execute(()->{
+						TASK_POOL.execute(() -> {
 							orderDon.setStatus(OrderDon.Status.close);
 							updateById(orderDon);
 						});
@@ -654,7 +570,11 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				}
 				//返还优惠券
 				if (orderDon.getCouponUserId() != 0) {
-					updateOrderDonCouponStatus(orderDon);
+					couponFontService.updateOrderDonCouponStatus(orderDon);
+				}
+				//返还余额
+				if (orderDon.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+					userBenefitsService.addUserBalance(orderDon.getUserId(), orderDon.getBalance());
 				}
 			}
 		}
@@ -688,16 +608,16 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 		GoodsDonSku sku = goodsDonSkuMapper.selectById(renewSkuId);
 
-        GroupsRelation relation = groupsRelationMapper.selectById(relationId);
-        if (relation == null) {
-            throw new BusinessRuntimeException("");
-        }
+		GroupsRelation relation = groupsRelationMapper.selectById(relationId);
+		if (relation == null) {
+			throw new BusinessRuntimeException("");
+		}
 
 		//todo 多账号同一用户绑定 续费
 		if (!relation.getUserId().equals(user.getId())) {
 			checkIsSameUser(relation.getUserId(), userId, user.getOpenId(), user.getLoginPhone());
 		}
-        GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
+		GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
 		if (groupsTrips == null) {
 			throw BusinessRuntimeException.getInstance("该车队不存在,请联系客服");
 		}
@@ -771,30 +691,19 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			}
 		}
 		orderDon.setRealMoney(orderDon.getMoney());
-		//存在优惠券
-		if (couponUser != null) {
-			Long couponId = couponUser.getCouponId();
-			Coupon coupon = couponMapper.getCouponById(couponId);
-			BigDecimal couponMoney = null;
-			if (coupon.getType() == Coupon.Type.discount) {
-				BigDecimal discountMoney = orderDon.getMoney().multiply(coupon.getDiscount());
-				orderDon.setMoney(discountMoney);
-				couponMoney = orderDon.getRealMoney().subtract(discountMoney);
-			} else if (coupon.getType() == Coupon.Type.reduce) {
-				BigDecimal subtractMoney = orderDon.getMoney().subtract(coupon.getReduceMoney());
-				orderDon.setMoney(subtractMoney);
-				couponMoney = orderDon.getRealMoney().subtract(subtractMoney);
-			}
-			orderDon.setCouponMoney(couponMoney);
-			orderDon.setCouponUserId(couponUser.getId());
-		}
-
+		//扣除使用优惠券、余额的订单金额
+		deductOrderMoney(orderDon, couponUser, payRequest.getBalance(), user);
 		this.saveOrUpdate(orderDon);
-
-		jobManager.addJob(EXPIRY_TIME, () -> {
-			closeOrder(orderDon.getId());
-		});
-
+		//订单金额为0 且订单为已完成 更新虚物车票
+		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) {
+			orderDon.setStatus(goodsDon.getType() == 1 && goodsDon.getSecondType() == 2 ? OrderDon.Status.complete : OrderDon.Status.hasPayment);
+			updateGroupsTicket(orderDon, goodsDon, sku);
+		}
+		if (orderDon.getStatus() == OrderDon.Status.noPayment) {
+			jobManager.addJob(EXPIRY_TIME, () -> {
+				closeOrder(orderDon.getId());
+			});
+		}
 		return orderDon;
 	}
 
@@ -825,7 +734,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		String appId = sysConfigService.getDefaultShopConfigByType("zfb");
 
 		if (orderDon.getPopularizeId() != null) {
-			ShopConfig shopConfig = shopConfigMapper.getByChannelPopularizeId(orderDon.getPopularizeId(),"zfb");
+			ShopConfig shopConfig = shopConfigMapper.getByChannelPopularizeId(orderDon.getPopularizeId(), "zfb");
 			if (shopConfig != null) {
 				appId = shopConfig.getAppId();
 			}
@@ -851,7 +760,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			orderDon.setShopId(null);
 		}
 		String appId = params.get("app_id");
-		boolean signVerified = AlipaySignature.rsaCheckV1(params,shopConfigMapper.getByAppId(appId).getPublicKey(), BaseZfbConfig.charset, BaseZfbConfig.signType);
+		boolean signVerified = AlipaySignature.rsaCheckV1(params, shopConfigMapper.getByAppId(appId).getPublicKey(), BaseZfbConfig.charset, BaseZfbConfig.signType);
 		if (!signVerified) {
 			log.error("支付宝回调校验签名错误,自定义入参:{}", params.get("body"));
 			throw BusinessRuntimeException.getInstance("支付宝回调校验签名错误");
@@ -890,13 +799,8 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			return;
 		}
 		if (!orderDon.getIsNoLogin()) {
-			if (orderDon.getOrderType() == 2 && orderDon.getIsChangeTicket()) {
-				//将奈飞月付车票更换成其他规格的车票
-				changeTicketAndRecord(orderDon.getPreSkuId(), orderDon.getRelationId(), orderDon.getId(), orderDon.getNum(), sku, orderDon.getUserId());
-			} else {
-				//修改车位状态并发送模板消息
-				updateCarParkAndSendMsg(goodsDon, sku, orderDon);
-			}
+			//更新座位信息
+			updateGroupsTicket(orderDon, goodsDon, sku);
 			if (orderDon.getMoney().compareTo(BigDecimal.ZERO) > 0 && orderDon.getIsDistribute()) {
 				TASK_POOL.execute(() -> distributeWaitingSendPointsRecord(orderDon));
 			}
@@ -916,7 +820,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		//购买Midjourney课程 赠送Midjourney车票
 		if (goodsId != null && goodsId.equals(27l)) {
 			orderDon.setStatus(OrderDon.Status.complete);
-			TASK_POOL.execute(()->sendMidjourneyTicketIfCourse(orderDon.getUserId()));
+			TASK_POOL.execute(() -> sendMidjourneyTicketIfCourse(orderDon.getUserId()));
 		}
 		this.updateById(orderDon);
 		//配置了特定奖池,增加抽奖机会
@@ -936,356 +840,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		});
 	}
 
-	@Override
-	@Transactional(rollbackFor = Throwable.class)
-	public Result<String> wxRefund(WxOrderRefundReq wxOrderRefundReq) throws Exception {
-		OrderDon orderDon = orderDonMapper.selectById(wxOrderRefundReq.getOrderId());
-		GoodsDonSku sku = goodsDonSkuMapper.selectById(orderDon.getSkuId());
-		GoodsDon goodsDon = goodsDonMapper.selectById(sku.getGoodsId());
-		if (OrderDon.Type.EX_CODE == orderDon.getType()) {
-			wxOrderRefundReq.setRefundMoney(BigDecimal.ZERO);
-		}
-		if (OrderDon.Type.EX_CODE != orderDon.getType() && orderDon.getType() != OrderDon.Type.WeChat) {
-			throw BusinessRuntimeException.getInstance("该订单不是微信支付的订单");
-		}
-		//校验订单是否符合退款
-		checkOrderRefund(orderDon, goodsDon, sku);
-		if (wxOrderRefundReq.getRefundMoney().compareTo(orderDon.getMoney()) > 0) {
-			throw BusinessRuntimeException.getInstance("退款金额大于订单金额");
-		}
-		if (!redisService.setNx(String.format("%s", wxOrderRefundReq.getOrderId()), wxOrderRefundReq.getOrderId(), 10l)) {
-			throw BusinessRuntimeException.getInstance("请勿重复点击,正在退款中..");
-		}
-		String outRefundNo = "";
-		if (orderDon.getType() != OrderDon.Type.EX_CODE) {
-			long time = System.currentTimeMillis();
-			//退款订单号
-			outRefundNo = Codec.DoDigest.custom()
-					.setAlgorithm(Codec.DoDigest.Algorithm.MD5)
-					.setStringData(time + String.valueOf(orderDon.getId()))
-					.toHexString();
-			weChatService.refundWxOrder(weChatConfig.getAppid(),
-					Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.WX_DEFAULT_APP_ID),
-					orderDon.getTransactionId(),
-					outRefundNo,
-					orderDon.getMoney(),
-					wxOrderRefundReq.getRefundMoney(),
-					weChatConfig.getDomain() + "applets/order/wx/refund/notify");
-		}
-		//修改订单状态
-		orderDon.setStatus(OrderDon.Status.refund);
-		orderDon.setRefundMoney(wxOrderRefundReq.getRefundMoney());
-		orderDon.setRefundDesc(wxOrderRefundReq.getRefundDesc());
-		//记录退款信息
-		final String outNo = outRefundNo;
-		THREAD_POOL_TASK_EXECUTOR.execute(()->{
-			refundRecord(orderDon, goodsDon, sku, outNo);
-			goodsDonStockService.upStockRelate(null, orderDon.getId(),"refund");
-		});
-		try {
-			//虚物订单退款,清出车队,车票变为已过期
-			orderCommonService.clearUnrealGoodsDetail(orderDon, goodsDon.getType(), sku.getMonths());
-		} catch (Exception e) {
-			log.error("清除车票错误:{}", StringUtil.getErrorText(e));
-		}
-		//若存在优惠券使用,退还优惠券
-		if (orderDon.getCouponUserId() != 0) {
-			redisService.set(String.format("%s-%s", orderDon.getId(), orderDon.getCouponUserId()), orderDon.getId(), 120l);
-			updateOrderDonCouponStatus(orderDon);
-		}
-		orderDonMapper.updateById(orderDon);
-		return GatewayResponse.SUCCESS.newBuilder().toResult("退款成功");
-	}
-
-	@Override
-	@Transactional(rollbackFor = Throwable.class)
-	public Result<String> zfbRefund(ZfbOrderRefundReq zfbOrderRefundReq) throws Exception {
-		OrderDon orderDon = orderDonMapper.selectById(zfbOrderRefundReq.getOrderId());
-		GoodsDonSku sku = goodsDonSkuMapper.selectById(orderDon.getSkuId());
-		GoodsDon goodsDon = goodsDonMapper.selectById(sku.getGoodsId());
-		if (OrderDon.Type.EX_CODE == orderDon.getType()) {
-			zfbOrderRefundReq.setRefundMoney(BigDecimal.ZERO);
-		}
-		if (OrderDon.Type.EX_CODE != orderDon.getType() && orderDon.getType() != OrderDon.Type.ALI_PAY) {
-			throw BusinessRuntimeException.getInstance("该订单不是支付宝支付的订单");
-		}
-		//校验订单是否符合退款
-		checkOrderRefund(orderDon, goodsDon, sku);
-		if (zfbOrderRefundReq.getRefundMoney().compareTo(orderDon.getMoney()) > 0) {
-			throw BusinessRuntimeException.getInstance("退款金额大于订单金额");
-		}
-		if (!redisService.setNx(String.format("%s", zfbOrderRefundReq.getOrderId()), zfbOrderRefundReq.getOrderId(), 10l)) {
-			throw BusinessRuntimeException.getInstance("请勿重复点击,正在退款中..");
-		}
-		AlipayTradeRefundResponse response = null;
-		if (OrderDon.Type.EX_CODE == orderDon.getType()) {
-			response = new AlipayTradeRefundResponse();
-		}
-		String outRefundNo = "";
-		if (OrderDon.Type.EX_CODE != orderDon.getType()) {
-			//支付宝退款
-			AlipayClient alipayClient = AliPayClientFactory.getAlipayClient(Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.ZFB_DEFAULT_APP_ID));
-			AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
-			AlipayTradeRefundModel model = new AlipayTradeRefundModel();
-			//支付宝交易号
-			model.setTradeNo(orderDon.getTransactionId());
-			model.setRefundAmount(zfbOrderRefundReq.getRefundMoney().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_EVEN).toString());
-			model.setQueryOptions(List.of("gmt_refund_pay"));
-			long time = System.currentTimeMillis();
-			//退款订单号
-			outRefundNo = Codec.DoDigest.custom()
-					.setAlgorithm(Codec.DoDigest.Algorithm.MD5)
-					.setStringData(time + String.valueOf(orderDon.getId()))
-					.toHexString();
-			//退款请求号
-			model.setOutRequestNo(outRefundNo);
-			request.setBizModel(model);
-			String shopId = orderDon.getShopId();
-			ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class).eq(ShopConfig::getAppId, orderDon.getShopId()));
-			if (shopId != null && shopConfig != null && shopConfig.getIsCert()) {
-				response = alipayClient.certificateExecute(request);
-			} else {
-				response = alipayClient.execute(request);
-			}
-		}
-		final String outNo = outRefundNo;
-		if (response.isSuccess()) {
-			//修改订单状态
-			orderDon.setStatus(OrderDon.Status.refund);
-			if (OrderDon.Type.EX_CODE != orderDon.getType()) {
-				AliPayRefundRep aliPayRefundRep = Jsons.parseObject(response.getBody(), AliPayRefundRep.class);
-				AliPayRefundRep.AlipayTradeRefundResponse res = aliPayRefundRep.getResponse();
-				orderDon.setRefundMoney(res.getRefundFee().multiply(BigDecimal.valueOf(100)));
-			} else orderDon.setRefundMoney(BigDecimal.ZERO);
-			orderDon.setRefundDesc(zfbOrderRefundReq.getRefundDesc());
-			//记录退款信息
-			THREAD_POOL_TASK_EXECUTOR.execute(()->{
-				refundRecord(orderDon, goodsDon, sku, outNo);
-				goodsDonStockService.upStockRelate(null, orderDon.getId(),"refund");
-				try {
-					marketFrontService.subSpecificChanceNum(orderDon.getUserId(), orderDon.getId(), orderDon.getGoodsId(), orderDon.getUpdateTime());
-					if (goodsDon.getType() == 2) {
-						equipmentDistributeService.subDistributeBenefitsChance(orderDon.getUserId(), orderDon.getId());
-						//扣除用户自身实物权益
-						userRealOrderBenefitsService.deductUserRealGoodsBenefits(orderDon.getId());
-					}
-				} catch (Exception e) {
-					log.error("zfb减少抽奖机会错误,orderId:{},error:{}", orderDon.getId(), e);
-				}
-				if (orderDon.getIsDistribute()) {
-					DistributeWaitingSendPoints distributeWaitingSendPoints = distributeWaitingSendPointsMapper.selectOne(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class).eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId()).last("limit 1"));
-					if (distributeWaitingSendPoints != null) {
-						Integer success = distributeWaitingSendPointsMapper.updateOrderStatus(orderDon.getId(), DistributeWaitingSendPoints.Status.close);
-						//是否积分已经发送
-						if (success != 1 && orderDon.getIsFixedDistribute()) {
-							//扣除固定推广者积分
-							BigDecimal refundMoney = orderDon.getRefundMoney();
-							BigDecimal money = orderDon.getMoney();
-							BigDecimal needSubPoints = distributeWaitingSendPoints.getPoints();
-							if (refundMoney.compareTo(money) == 0) {
-								userPointsService.deductDistributePoints(orderDon.getId(), distributeWaitingSendPoints.getSharedId(), needSubPoints);
-
-								distributeWaitingSendPointsMapper.update(null, Wrappers.lambdaUpdate(DistributeWaitingSendPoints.class)
-										.set(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.close)
-										.eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId())
-										.ne(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.close));
-							} else {
-								Integer thisGoodsRate = Optional.ofNullable(userDistributeMapper.getDistributeGoodsRateByUidAndGid(orderDon.getUserId(), orderDon.getGoodsId())).orElse(0);
-								if (thisGoodsRate > 0) {
-									needSubPoints = refundMoney.multiply(BigDecimal.valueOf(thisGoodsRate).divide(BigDecimal.valueOf(100))).multiply(BigDecimal.valueOf(10));
-									userPointsService.deductDistributePoints(orderDon.getId(), distributeWaitingSendPoints.getSharedId(), needSubPoints);
-
-									distributeWaitingSendPointsMapper.update(null, Wrappers.lambdaUpdate(DistributeWaitingSendPoints.class)
-											.set(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.close)
-											.eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId())
-											.ne(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.close));
-								}
-							}
-						}
-					}
-				}
-			});
-			try {
-				//虚物订单退款,清出车队,车票变为已过期
-				orderCommonService.clearUnrealGoodsDetail(orderDon, goodsDon.getType(), sku.getMonths());
-			} catch (Exception e) {
-				log.error("清除车票错误:{}", StringUtil.getErrorText(e));
-			}
-			if (orderDon.getCouponUserId() != 0) {
-				//zfb订单退款,返还优惠券
-				updateOrderDonCouponStatus(orderDon);
-			}
-			orderDonMapper.updateById(orderDon);
-			return GatewayResponse.SUCCESS.newBuilder().toResult("退款成功");
-		}
-		throw BusinessRuntimeException.getInstance("退款失败,e:{0}", response.getSubMsg());
-	}
-
-	/**
-	 * 校验订单是否符合退款操作
-	 * @param orderDon
-	 */
-	private void checkOrderRefund(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku) {
-		if (orderDon == null) {
-			throw BusinessRuntimeException.getInstance("订单不存在");
-		}
-//		if (orderDon.getType() == OrderDon.Type.EX_CODE) {
-//			throw BusinessRuntimeException.getInstance("兑换码订单不支持退款");
-//		}
-		if (OrderDon.Status.refund == orderDon.getStatus()) {
-			throw BusinessRuntimeException.getInstance("该订单已退款");
-		}
-		if (OrderDon.Status.noPayment == orderDon.getStatus() || OrderDon.Status.close == orderDon.getStatus()) {
-			throw BusinessRuntimeException.getInstance("订单{0}状态不支持退款", orderDon.getStatus());
-		}
-		if (goodsDon.getType() == 1) {
-			//虚拟原订单是否已过期
-			Integer months = sku.getMonths();
-			Integer num = orderDon.getNum();
-			DateTime now = DateTime.now();
-			DateTime endTime = DateUtil.offsetMonth(orderDon.getCreatedTime(), months * num);
-			if (now.isAfter(endTime)) {
-				throw BusinessRuntimeException.getInstance("该订单车票已过期");
-			}
-			if (orderDon.getOrderType() == 1) {
-				//是否有其他付款订单是否过期
-				OrderDon otherOrder = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
-						.eq(OrderDon::getRelationId, orderDon.getRelationId())
-						.eq(OrderDon::getUserId, orderDon.getUserId())
-						.ne(OrderDon::getId, orderDon.getId())
-						.notIn(OrderDon::getStatus,  Constant.noOrderAllStatus)
-						.eq(OrderDon::getOrderType, 2)
-						.orderByDesc(OrderDon::getId)
-						.last("limit 1"));
-				if (otherOrder != null) {
-					throw BusinessRuntimeException.getInstance("该笔订单存在续费订单,请先退掉续费订单");
-				}
-			}
-		}
-		//续费订单
-//		if (orderDon.getOrderType() == 2 && OrderDon.Type.EX_CODE != orderDon.getType()) {
-//			throw BusinessRuntimeException.getInstance("续费订单不支持退款");
-//		}
-//		if (goodsDon != null && OrderDon.Type.EX_CODE != orderDon.getType()) {
-//			//虚物订单 7天内可退款
-//			if (goodsDon.getType() == 1 && DateTime.now().isAfter(DateUtil.offsetDay(orderDon.getPayTime(), 7))) {
-//				throw BusinessRuntimeException.getInstance("该虚物订单已过退款时间");
-//			}
-//		}
-	}
-
-	/**
-	 * 记录退款信息
-	 */
-	private void refundRecord(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku, String outRefundNo) {
-		RefundOrderDon refundOrderDon = refundOrderDonMapper.selectOne(Wrappers.lambdaQuery(RefundOrderDon.class).eq(RefundOrderDon::getOrderId, orderDon.getId()).eq(RefundOrderDon::getOutRefundNo, outRefundNo).last("limit 1"));
-		if (refundOrderDon == null) {
-			RefundOrderDon refund = new RefundOrderDon();
-			refund.setOrderId(orderDon.getId());
-			refund.setOutRefundNo(outRefundNo);
-			refund.setTotalFee(orderDon.getMoney());
-			refund.setRefundDesc(orderDon.getRefundDesc());
-			//退款金额
-			refund.setRefundFee(orderDon.getRefundMoney());
-			refund.setUserId(orderDon.getUserId());
-			refund.setGoodsId(orderDon.getGoodsId());
-			refund.setGoodsTitle(goodsDon == null ? null : goodsDon.getTitle());
-			refund.setSkuId(orderDon.getSkuId());
-			refund.setSpecVal(sku == null ? null : String.format("%s%s", sku.getSpecVal(), sku.getPrice().divide(BigDecimal.valueOf(100))));
-			refund.setRefundMethod(orderDon.getType().name());
-			refund.setGoodsType(goodsDon == null ? 0 : goodsDon.getType());
-			refundOrderDonMapper.insert(refund);
-		}
-	}
-
-	@Override
-	public void wxRefundNotify(Map<String, String> map) throws Exception {
-		// 对退款数据 解密
-		String md5Key = Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5).setStringData(shopConfigMapper.getByAppId(map.get("mch_id")).getSecret()).toHexString();
-		String reqInfoXml = Codec.DoCipher.custom().setAlgorithm(Codec.DoCipher.Trans.AES_ECB_PKCS7Padding).setBase64Data(map.get("req_info")).setStringKey(md5Key).ofDecrypt().toText();
-		log.info("=== 退款数据解密: \n {}", reqInfoXml);
-		Map<String, String> reqInfoMap = Xmls.toMap(reqInfoXml);
-		// 退款失败
-		if (StrUtil.equals("CHANGE", reqInfoMap.get("refund_status"))) {
-			throw BusinessRuntimeException.getInstance("退款异常.");
-		} else if (StrUtil.equals("REFUNDCLOSE", reqInfoMap.get("refund_status"))) {
-			throw BusinessRuntimeException.getInstance("退款关闭.");
-		}
-		/* 此处处理业务逻辑 */
-		String outRefundNo = reqInfoMap.get("out_refund_no");
-		String transactionId = reqInfoMap.get("transaction_id");
-		String refundFee = reqInfoMap.get("refund_fee");
-		OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getTransactionId, transactionId).last("limit 1"));
-		if (orderDon == null) {
-			log.error("退款回调,transactionId:{}订单不存在", transactionId);
-			return;
-		}
-		BigDecimal refundMoney = new BigDecimal(refundFee);
-		if (orderDon.getStatus() == OrderDon.Status.refund) {
-			if (!orderDon.getRefundMoney().equals(refundMoney)) {
-				orderDon.setRefundMoney(refundMoney);
-				orderDonMapper.updateById(orderDon);
-			}
-		} else {
-			orderDon.setStatus(OrderDon.Status.refund);
-			orderDon.setRefundMoney(refundMoney);
-			orderDonMapper.updateById(orderDon);
-		}
-		if (orderDon.getCouponUserId() != 0) {
-			Object obj = redisService.get(String.format("%s-%s", orderDon.getId(), orderDon.getCouponUserId()));
-			if (obj == null) {
-				updateOrderDonCouponStatus(orderDon);
-			}
-		}
-		log.info("微信退款: 退款单号[{}] 退款成功.", outRefundNo);
-
-		TASK_POOL.execute(()->{
-			try {
-				marketFrontService.subSpecificChanceNum(orderDon.getUserId(), orderDon.getId(), orderDon.getGoodsId(), orderDon.getUpdateTime());
-				GoodsDon goodsDon = goodsDonMapper.selectById(orderDon.getGoodsId());
-				if (goodsDon.getType() == 2) {
-					equipmentDistributeService.subDistributeBenefitsChance(orderDon.getUserId(), orderDon.getId());
-					//扣除用户自身实物权益
-					userRealOrderBenefitsService.deductUserRealGoodsBenefits(orderDon.getId());
-				}
-			} catch (Exception e) {
-				log.error("wx减少抽奖机会错误,orderId:{},error:{}", orderDon.getId(), e);
-			}
-			if (orderDon.getIsDistribute()) {
-				DistributeWaitingSendPoints distributeWaitingSendPoints = distributeWaitingSendPointsMapper.selectOne(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class).eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId()).last("limit 1"));
-				if (distributeWaitingSendPoints != null) {
-					Integer success = distributeWaitingSendPointsMapper.updateOrderStatus(orderDon.getId(), DistributeWaitingSendPoints.Status.close);
-					//是否产生过积分
-					if (success != 1 && orderDon.getIsFixedDistribute()) {
-						//扣除固定推广者积分
-						BigDecimal money = orderDon.getMoney();
-						BigDecimal needSubPoints = distributeWaitingSendPoints.getPoints();
-						if (refundMoney.compareTo(money) == 0) {
-							userPointsService.deductDistributePoints(orderDon.getId(), distributeWaitingSendPoints.getSharedId(), needSubPoints);
-
-							distributeWaitingSendPointsMapper.update(null, Wrappers.lambdaUpdate(DistributeWaitingSendPoints.class)
-									.set(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.close)
-									.eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId())
-									.ne(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.close));
-						} else {
-							Integer thisGoodsRate = Optional.ofNullable(userDistributeMapper.getDistributeGoodsRateByUidAndGid(orderDon.getUserId(), orderDon.getGoodsId())).orElse(0);
-							if (thisGoodsRate > 0) {
-								needSubPoints = refundMoney.multiply(BigDecimal.valueOf(thisGoodsRate).divide(BigDecimal.valueOf(100))).multiply(BigDecimal.valueOf(10));
-								userPointsService.deductDistributePoints(orderDon.getId(), distributeWaitingSendPoints.getSharedId(), needSubPoints);
-
-								distributeWaitingSendPointsMapper.update(null, Wrappers.lambdaUpdate(DistributeWaitingSendPoints.class)
-										.set(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.close)
-										.eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId())
-										.ne(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.close));
-							}
-						}
-					}
-				}
-			}
-		});
-	}
-
-	public void setRelation(Long relationId, Long userId, Long goodsId, Boolean isLoginPopularize) {
+	public void setRelation(Long relationId, Long groupsId, Long userId, Long goodsId, Boolean isLoginPopularize) {
 		if (isLoginPopularize == null || !isLoginPopularize) {
 			int noPayCount = count(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getUserId, userId).eq(OrderDon::getGoodsId, goodsId).eq(OrderDon::getStatus, OrderDon.Status.noPayment.toString()));
 			if (noPayCount > 0) {
@@ -1307,10 +862,9 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			log.info("=====>用户:{}未抢到座位:{}", userId, relationId);
 			throw new BusinessRuntimeException("客官手慢啦,该座位已经有人啦!");
 		}
-		GroupsRelation relation = groupsRelationMapper.selectById(relationId);
-		int count = groupsMapper.decrAvailableNum(relation.getGroupsId());
+		int count = groupsMapper.decrAvailableNum(groupsId);
 		if (count == 0) {
-			log.error("车位异常 groupId:{}", relation.getGroupsId());
+			log.error("车位异常 groupId:{}", groupsId);
 			throw new BusinessRuntimeException("车位异常");
 		}
 	}
@@ -1449,6 +1003,24 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		return null;
 	}
 
+	@Override
+	public void zeroOrderConfirm(Long orderId) {
+		OrderDon orderDon = this.getById(orderId);
+		if (orderDon == null) throw BusinessRuntimeException.getInstance("订单不存在");
+		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) > 0) {
+			throw BusinessRuntimeException.getInstance("订单异常");
+		}
+		GoodsDon goodsDon = goodsDonMapper.selectById(orderDon.getGoodsId());
+		GoodsDonSku sku = goodsDonSkuMapper.selectById(orderDon.getSkuId());
+		orderDon.setStatus(goodsDon.getType() == 1 && goodsDon.getSecondType() == 2 ? OrderDon.Status.complete : OrderDon.Status.hasPayment);
+		updateGroupsTicket(orderDon, goodsDon, sku);
+		if (orderDon.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+			orderDon.setType(OrderDon.Type.BALANCE);
+			orderDon.setPayTime(DateTime.now());
+		}
+		this.updateById(orderDon);
+	}
+
 	public AliPayParams commonAliPay(Long orderId, String returnUrl, String productCode, String appId, String qrPayMode) throws Exception {
 		AlipayClient alipayClient = AliPayClientFactory.getAlipayClient(Optional.ofNullable(appId).orElse(ShopConfig.ZFB_DEFAULT_APP_ID));
 		// 创建API对应的request
@@ -1462,7 +1034,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		if (BaseZfbConfig.qrPayMode_redirect.equals(qrPayMode)) {
 			returnUrl = String.format("%s%s", envCommonService.getDomain(), BaseZfbConfig.loginReturnUrl);
 			alipayRequest.setReturnUrl(returnUrl);
-		}else if (StrUtil.isNotBlank(returnUrl)){
+		} else if (StrUtil.isNotBlank(returnUrl)) {
 			alipayRequest.setReturnUrl(returnUrl);
 		}
 		alipayRequest.setNotifyUrl(notifyUrl);
@@ -1569,30 +1141,30 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			}
 		}
 
-		//新订单产生 发送模板消息给用户、客服
-		User user = userMapper.selectById(order.getUserId());
-		if (user != null) {
-			THREAD_POOL_TASK_EXECUTOR.execute(() -> {
-				try {
-					//发送至用户消息
-					templateCommonService.newOderSendMsgByType(order, goodsDon, user, user.getOpenId(), false);
-				} catch (Exception e) {
-					log.error("向用户发送新订单{}通知消息失败:{}", order.getOrderNo(), e);
-				}
-			});
-		}
-		THREAD_POOL_TASK_EXECUTOR.execute(() -> {
-			try {
-				//发送至客服消息
-				List<CustomerService> customerServices = customerServiceMapper.selectList(null);
-				for (CustomerService customerService : customerServices) {
-					log.info("向客服{}发送新订单消息通知..", customerService.getNickName());
-					templateCommonService.newOderSendMsgByType(order, goodsDon, user, customerService.getOpenId(), true);
-				}
-			} catch (Exception e) {
-				log.error("向客服发送新订单{}通知消息失败:{}", order.getOrderNo(), e);
-			}
-		});
+//		//新订单产生 发送模板消息给用户、客服
+//		User user = userMapper.selectById(order.getUserId());
+//		if (user != null) {
+//			THREAD_POOL_TASK_EXECUTOR.execute(() -> {
+//				try {
+//					//发送至用户消息
+//					templateCommonService.newOderSendMsgByType(order, goodsDon, user, user.getOpenId(), false);
+//				} catch (Exception e) {
+//					log.error("向用户发送新订单{}通知消息失败:{}", order.getOrderNo(), e);
+//				}
+//			});
+//		}
+//		THREAD_POOL_TASK_EXECUTOR.execute(() -> {
+//			try {
+//				//发送至客服消息
+//				List<CustomerService> customerServices = customerServiceMapper.selectList(null);
+//				for (CustomerService customerService : customerServices) {
+//					log.info("向客服{}发送新订单消息通知..", customerService.getNickName());
+//					templateCommonService.newOderSendMsgByType(order, goodsDon, user, customerService.getOpenId(), true);
+//				}
+//			} catch (Exception e) {
+//				log.error("向客服发送新订单{}通知消息失败:{}", order.getOrderNo(), e);
+//			}
+//		});
 	}
 
 	/**
@@ -1642,7 +1214,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 						}
 						returnMoney = orderMoney.multiply(BigDecimal.valueOf(rate)).divide(BigDecimal.valueOf(100));
 					}
-				}else {
+				} else {
 					businessId = userDistribute.getBusinessId();
 					//固定推广分销
 					UserPlatDistributeRate userPlatDistributeRate = userPlatDistributeRateMapper.selectOne(Wrappers.lambdaQuery(UserPlatDistributeRate.class)
@@ -1789,28 +1361,6 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		return couponUser;
 	}
 
-	/**
-	 * 退款时,归还优惠券
-	 */
-	private void updateOrderDonCouponStatus(OrderDon orderDon) {
-		log.info("订单:{}退款,优惠券返还start...", orderDon.getId());
-		CouponUser couponUser = couponUserMapper.selectById(orderDon.getCouponUserId());
-		if (CouponUser.Channel.exCode == couponUser.getChannel()) {
-			//兑换码兑换的优惠券退款时,直接删除
-			couponUserMapper.deleteById(couponUser.getId());
-		}else {
-			CouponUserView couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
-					.field(CouponUserView::getCouponId, couponUser.getCouponId()).op(Operator.Equal)
-					.field(CouponUserView::getUserId, orderDon.getUserId()).op(Operator.Equal)
-					.build());
-			couponUserView.setCouponUserStatus(CouponUser.Status.unused);
-			couponFontService.setCouponUserStatus(couponUserView, DateTime.now());
-			couponUser.setStatus(couponUserView.getCouponUserStatus());
-			couponUserMapper.updateById(couponUser);
-		}
-		log.info("订单:{}退款,优惠券返还end...", orderDon.getId());
-	}
-
 	public void checkCouponGoods(GoodsDon goodsDon, Long skuId, Long couponId, Boolean isCouponCode) throws Exception {
 		if (goodsDon.getType() == 1) {
 			CouponSku couponSku = couponSkuMapper.selectOne(Wrappers.lambdaQuery(CouponSku.class)
@@ -1821,13 +1371,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				throw BusinessRuntimeException.getInstance(String.format("该%s不适用于该规格", isCouponCode ? "优惠码" : "优惠券"));
 			}
 		} else {
+			//实物、潮玩 优惠码优惠券 针对平台
 			Long gid = goodsDon.getId();
 			CouponSku couponSku = couponSkuMapper.selectOne(Wrappers.lambdaQuery(CouponSku.class)
 					.eq(CouponSku::getGoodsId, gid)
 					.eq(CouponSku::getCouponId, couponId)
 					.select(CouponSku::getId).last("limit 1"));
 			if (couponSku == null) {
-				throw BusinessRuntimeException.getInstance(String.format("该%s不可用于ga商品", isCouponCode ? "优惠码" : "优惠券"));
+				throw BusinessRuntimeException.getInstance(String.format("该%s不可用于商品", isCouponCode ? "优惠码" : "优惠券"));
 			}
 		}
 	}
@@ -1873,7 +1424,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				groups.setAccountId(accountView.getId());
 				groups.setStatus(GroupsTrips.Status.validity);
 				groups.setRemark("已设置唯一账号");
-			}else {
+			} else {
 				groups.setStatus(GroupsTrips.Status.waiting);
 				groups.setRemark("等待设置账号");
 			}
@@ -1926,7 +1477,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		if (userDistributeShared == null) {
 			CouponDistributePopularize couponDistributePopularize = couponDistributePopularizeMapper.selectOne(Wrappers.lambdaQuery(CouponDistributePopularize.class).eq(CouponDistributePopularize::getExCode, couponExCode).last("limit 1"));
 			Optional.ofNullable(couponDistributePopularize).ifPresent(popularize -> {
-				try{
+				try {
 					UserDistribute userDistribute = userDistributeMapper.selectById(popularize.getDistributeId());
 					if (userDistribute == null || !userDistribute.getDeleted()) {
 						return;
@@ -1975,11 +1526,10 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 	}
 
 	/**
-	 *
-	 * @param preSkuId 续费之前的规格id
+	 * @param preSkuId      续费之前的规格id
 	 * @param preRelationId 续费之前的座位id
-	 * @param orderId 订单id
-	 * @param afterSku 续费之后的规格
+	 * @param orderId       订单id
+	 * @param afterSku      续费之后的规格
 	 */
 	private void changeTicketAndRecord(Long preSkuId, Long preRelationId, Long orderId, Integer num, GoodsDonSku afterSku, Long userId) {
 		//清理之前的车票
@@ -2032,15 +1582,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 	}
 
 	/**
-	 *
-	 * @param preStartTime 续费之前的开始时间
-	 * @param preExpiryTime 续费之前的过期时间
-	 * @param preRelationId 之前的座位id
-	 * @param userId 用户id
-	 * @param preSkuId 之前座位的规格id
-	 * @param orderId 订单id
-	 * @param num 订单数量
-	 * @param afterSku 续费之后的规格id
+	 * @param preStartTime    续费之前的开始时间
+	 * @param preExpiryTime   续费之前的过期时间
+	 * @param preRelationId   之前的座位id
+	 * @param userId          用户id
+	 * @param preSkuId        之前座位的规格id
+	 * @param orderId         订单id
+	 * @param num             订单数量
+	 * @param afterSku        续费之后的规格id
 	 * @param afterRelationId 续费之后的座位id
 	 */
 	public void saveChangeTicketRecord(Date preStartTime, Date preExpiryTime, Long preRelationId, Long userId, Long preSkuId, Long orderId, Integer num, GoodsDonSku afterSku, Long afterRelationId) {
@@ -2168,7 +1717,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				try {
 					//Midjourney车票、ChatGPT独立规格车票
 					List<Long> skuIds = Jsons.parseList(sysValue, Long.class);
-					skuIds.forEach(skuId->{
+					skuIds.forEach(skuId -> {
 						GoodsDonSku sku = goodsDonSkuMapper.selectById(skuId);
 						if (sku != null && sku.getStatus()) {
 							GroupsRelation relation = exchangeCodeService.getRelationNoOrder(sku, userId);
@@ -2226,4 +1775,113 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			}
 		}
 	}
+
+	/**
+	 * 获取车位
+	 */
+	public Long getRelation(OrderPayRequest payRequest, GoodsDonSku sku) {
+		GroupsRelation relation;
+		Long payRelationId = payRequest.getRelationId();
+		Boolean isLoginPopularize = payRequest.getIsLoginPopularize();
+		Long userId = payRequest.getUserId();
+		//锁定座位
+		if (payRelationId != null && payRelationId > 0) {
+			relation = groupsRelationMapper.selectById(payRelationId);
+			if (relation == null) {
+				throw new BusinessRuntimeException(GROUP_INDEX_NOT_FIND.getCode(), GROUP_INDEX_NOT_FIND.getMsg());
+			}
+			if (relation.getUserId() != null && relation.getUserId() > 0L) {
+				throw new BusinessRuntimeException(GROUP_INDEX_ALREADY_BE_USER.getCode(), GROUP_INDEX_ALREADY_BE_USER.getMsg());
+			}
+
+		} else {
+			//寻找差不多过期时间规格的车位
+			relation = groupRelationFrontService.getSimilarExpiredGroupRelation(sku.getId(), sku.getMonths(), null);
+			if (relation == null) {
+				//TODO 如果没指定座位,等待逻辑确认
+				LambdaQueryWrapper<GroupsTrips> queryWrapper = Wrappers.lambdaQuery(GroupsTrips.class)
+						.eq(GroupsTrips::getSkuId, sku.getId())
+						.gt(GroupsTrips::getAvailableNum, 0).last("limit 1")
+						.ne(GroupsTrips::getStatus, GroupsTrips.Status.down)
+						.orderByAsc(GroupsTrips::getAvailableNum);
+				GroupsTrips groups = groupsMapper.selectOne(queryWrapper);
+				if (groups == null) {
+					relation = createNewGroupsTrips(sku);
+				} else {
+					//寻找快满编车队进行占座
+					relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+							.eq(GroupsRelation::getGroupsId, groups.getId())
+							.eq(GroupsRelation::getStatus, "none")
+							.eq(GroupsRelation::getUserId, 0)
+							.orderByAsc(GroupsRelation::getNum)
+							.last("limit 1")
+					);
+					if (relation == null) {
+						throw new BusinessRuntimeException(GROUP_INDEX_NOT_FIND.getCode(), GROUP_INDEX_NOT_FIND.getMsg());
+					}
+				}
+			}
+		}
+
+		//锁定座位
+		setRelation(relation.getId(), relation.getGroupsId(), userId, sku.getGoodsId(), isLoginPopularize);
+		relation.setUserId(userId);
+		relation.setStatus(GroupsRelation.Status.locking);
+		groupsRelationMapper.updateById(relation);
+		return relation.getId();
+	}
+
+	/**
+	 * 使用优惠营销 订单金额抵扣
+	 */
+	public void deductOrderMoney(OrderDon orderDon, CouponUser couponUser, BigDecimal balance, User user) {
+		//存在优惠券
+		if (couponUser != null) {
+			Long couponId = couponUser.getCouponId();
+			Coupon coupon = couponMapper.getCouponById(couponId);
+			BigDecimal couponMoney = null;
+			if (coupon.getType() == Coupon.Type.discount) {
+				BigDecimal discountMoney = orderDon.getMoney().multiply(coupon.getDiscount());
+				orderDon.setMoney(discountMoney);
+				couponMoney = orderDon.getRealMoney().subtract(discountMoney);
+			} else if (coupon.getType() == Coupon.Type.reduce) {
+				BigDecimal subtractMoney = orderDon.getMoney().subtract(coupon.getReduceMoney());
+				orderDon.setMoney(subtractMoney);
+				couponMoney = orderDon.getRealMoney().subtract(subtractMoney);
+			}
+			orderDon.setCouponMoney(couponMoney);
+			orderDon.setCouponUserId(couponUser.getId());
+		}
+		//存在余额
+		if (balance != null && balance.compareTo(BigDecimal.ZERO) > 0) {
+			BigDecimal money = orderDon.getMoney();
+			//去掉余额 多出的部分
+			BigDecimal subBalance = balance;
+			orderDon.setMoney(money.subtract(subBalance));
+			if (balance.compareTo(money) >= 0) {
+				orderDon.setMoney(BigDecimal.ZERO);
+				orderDon.setBalance(money);
+				subBalance = money;
+				orderDon.setType(OrderDon.Type.BALANCE);
+			}
+			BigDecimal userBalance = user.getBalance();
+			//扣除余额 是否成功
+			Integer success = userMapper.deductBalance(user.getId(), subBalance, userBalance);
+			if (success != 1) throw BusinessRuntimeException.getInstance("您的余额不足!");
+			orderDon.setBalance(balance);
+		}
+	}
+
+	/**
+	 * 更新车票状态
+	 */
+	public void updateGroupsTicket(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku) {
+		if (orderDon.getOrderType() == 2 && orderDon.getIsChangeTicket()) {
+			//将奈飞月付车票更换成其他规格的车票
+			changeTicketAndRecord(orderDon.getPreSkuId(), orderDon.getRelationId(), orderDon.getId(), orderDon.getNum(), sku, orderDon.getUserId());
+		} else {
+			//修改车位状态并发送模板消息
+			updateCarParkAndSendMsg(goodsDon, sku, orderDon);
+		}
+	}
 }

+ 2 - 2
netflix-service/src/main/java/com/cyksj/service/register/RegisterOrderDonService.java

@@ -6,7 +6,7 @@ import com.cyksj.model.dto.H5JsPayParams;
 import com.cyksj.model.entity.RegisterOrderDon;
 import com.cyksj.model.entity.SpotifyUnLimitRecord;
 import com.cyksj.model.manage.request.RegisterReq;
-import com.cyksj.model.request.WxOrderRefundReq;
+import com.cyksj.model.request.OrderRefundReq;
 import com.cyksj.model.views.CouponUserView;
 
 import java.util.Map;
@@ -25,7 +25,7 @@ public interface RegisterOrderDonService extends IService<RegisterOrderDon> {
 
     void prepayNotify(Map<String, String> map) throws Exception;
 
-    Result<String> wxRefund(WxOrderRefundReq wxOrderRefundReq) throws Exception;
+    Result<String> wxRefund(OrderRefundReq refundReq) throws Exception;
 
     void wxRefundNotify(Map<String, String> map) throws Exception;
 

+ 6 - 6
netflix-service/src/main/java/com/cyksj/service/register/impl/RegisterOrderDonServiceImpl.java

@@ -25,7 +25,7 @@ import com.cyksj.model.dto.H5JsPayParams;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.request.RegisterReq;
 import com.cyksj.model.request.OrderAttach;
-import com.cyksj.model.request.WxOrderRefundReq;
+import com.cyksj.model.request.OrderRefundReq;
 import com.cyksj.model.views.CouponUserView;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.mange.coupon.CouponCommonService;
@@ -332,8 +332,8 @@ public class RegisterOrderDonServiceImpl extends ServiceImpl<RegisterOrderDonMap
     }
 
     @Override
-    public Result<String> wxRefund(WxOrderRefundReq wxOrderRefundReq) throws Exception {
-        RegisterOrderDon registerOrderDon = this.getById(wxOrderRefundReq.getOrderId());
+    public Result<String> wxRefund(OrderRefundReq refundReq) throws Exception {
+        RegisterOrderDon registerOrderDon = this.getById(refundReq.getOrderId());
         if (registerOrderDon == null) {
             throw BusinessRuntimeException.getInstance("该订单不存在");
         }
@@ -346,7 +346,7 @@ public class RegisterOrderDonServiceImpl extends ServiceImpl<RegisterOrderDonMap
         if (registerOrderDon.getMoney().compareTo(BigDecimal.ZERO) <= 0) {
             throw BusinessRuntimeException.getInstance("该订单金额为0,无需退款");
         }
-        if (!redisService.setNx(String.format("%s", wxOrderRefundReq.getOrderId()), wxOrderRefundReq.getOrderId(), 10l)) {
+        if (!redisService.setNx(String.format("%s", refundReq.getOrderId()), refundReq.getOrderId(), 10l)) {
             throw BusinessRuntimeException.getInstance("请勿重复点击,正在退款中..");
         }
         long time = System.currentTimeMillis();
@@ -355,13 +355,13 @@ public class RegisterOrderDonServiceImpl extends ServiceImpl<RegisterOrderDonMap
                 .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
                 .setStringData(time + String.valueOf(registerOrderDon.getId()))
                 .toHexString();
-        wxOrderRefundReq.setRefundMoney(registerOrderDon.getMoney());
+        refundReq.setRefundMoney(registerOrderDon.getMoney());
         weChatService.refundWxOrder(weChatConfig.getAppid(),
                 Optional.ofNullable(registerOrderDon.getShopId()).orElse(ShopConfig.WX_DEFAULT_APP_ID),
                 registerOrderDon.getTransactionId(),
                 outRefundNo,
                 registerOrderDon.getMoney(),
-                wxOrderRefundReq.getRefundMoney(),
+                refundReq.getRefundMoney(),
                 weChatConfig.getDomain() + "manage/register/wx/refund/notify");
         //修改订单状态
         registerOrderDon.setStatus(RegisterOrderDon.Status.refund);

+ 31 - 30
netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationClearServiceImpl.java

@@ -57,12 +57,13 @@ public class GroupRelationClearServiceImpl implements GroupRelationClearService
 			Date expireTime = relation.getExpiryTime();
 			relation.setStartTime(null);
 			relation.setExpiryTime(null);
-			Boolean isFlag = false;
-			//非chatGpt独立会员规格车队
-			if (!groupsRelation.getSkuId().equals(177l)) {
-				relation.setUserId(0l);
-				isFlag = true;
-			}
+			relation.setUserId(0l);
+//			Boolean isFlag = false;
+//			//非chatGpt独立会员规格车队
+//			if (!groupsRelation.getSkuId().equals(177l)) {
+//				relation.setUserId(0l);
+//				isFlag = true;
+//			}
 			relation.setStatus(GroupsRelation.Status.none);
 			relation.setIsHandle(false);
 			relation.setRenewStatus(false);
@@ -86,30 +87,30 @@ public class GroupRelationClearServiceImpl implements GroupRelationClearService
 				userTicketClearedRecord.setStartTime(startTime);
 				userTicketClearedRecord.setExpiryTime(expireTime);
 				userTicketClearedRecordMapper.insert(userTicketClearedRecord);
-				if (!isFlag) {
-					//清除之前规格的车队
-					groupService.close(groupsRelation.getGroupsId());
-					if (groupsRelation.getAccountId() != null) {
-						//生成10人月付新规格车队
-						Account account = accountService.getById(groupsRelation.getAccountId());
-						account.setSkuId(161l);
-						accountService.updateById(account);
-						GroupsTrips gt = new GroupsTrips();
-						gt.setSkuId(account.getSkuId());
-						gt.setAccountId(account.getId());
-						groupService.issuance(gt, null);
-
-						GroupsTripsDeleteRecord record = new GroupsTripsDeleteRecord();
-						record.setAccountId(gt.getAccountId());
-						record.setAccount(gt.getAccount());
-						record.setBeforeGroupsId(groupsRelation.getId());
-						record.setBeforeSkuId(groupsRelation.getSkuId());
-						record.setAfterGroupsId(gt.getId());
-						record.setAfterSkuId(gt.getSkuId());
-						groupsTripsDeleteRecordMapper.insert(record);
-						log.info("ChatGPT 独立会员账号车票过期,删除原先车队id:{}并生成一个10人月付规格的车队id:{}", groupsRelation.getId(), gt.getId());
-					}
-				}
+//				if (!isFlag) {
+//					//清除之前规格的车队
+//					groupService.close(groupsRelation.getGroupsId());
+//					if (groupsRelation.getAccountId() != null) {
+//						//生成10人月付新规格车队
+//						Account account = accountService.getById(groupsRelation.getAccountId());
+//						account.setSkuId(161l);
+//						accountService.updateById(account);
+//						GroupsTrips gt = new GroupsTrips();
+//						gt.setSkuId(account.getSkuId());
+//						gt.setAccountId(account.getId());
+//						groupService.issuance(gt, null);
+//
+//						GroupsTripsDeleteRecord record = new GroupsTripsDeleteRecord();
+//						record.setAccountId(gt.getAccountId());
+//						record.setAccount(gt.getAccount());
+//						record.setBeforeGroupsId(groupsRelation.getId());
+//						record.setBeforeSkuId(groupsRelation.getSkuId());
+//						record.setAfterGroupsId(gt.getId());
+//						record.setAfterSkuId(gt.getSkuId());
+//						groupsTripsDeleteRecordMapper.insert(record);
+//						log.info("ChatGPT 独立会员账号车票过期,删除原先车队id:{}并生成一个10人月付规格的车队id:{}", groupsRelation.getId(), gt.getId());
+//					}
+//				}
 			}
 		}
 	}

+ 12 - 2
netflix-service/src/main/java/com/cyksj/service/user/UserPointsService.java → netflix-service/src/main/java/com/cyksj/service/user/UserBenefitsService.java

@@ -4,11 +4,11 @@ import java.math.BigDecimal;
 
 /*
  *项目名: netflix
- *文件名: UserPointsService
+ *文件名: UserBenefitsService
  *创建者: JavaZou
  *创建时间:2023/5/18 16:06
  */
-public interface UserPointsService {
+public interface UserBenefitsService {
 	/**
 	 * 扣除用户分销积分
 	 * @param userId 用户ID
@@ -20,4 +20,14 @@ public interface UserPointsService {
 	 * 增加积分
 	 */
 	void addDistributePoints(Long userId, BigDecimal addPoints);
+
+	/**
+	 * 增加用户余额
+	 */
+	void addUserBalance(Long userId,BigDecimal addBalance);
+
+	/**
+	 * 扣除用户余额
+	 */
+	void deductUserBalance(Long userId, BigDecimal subBalance);
 }

+ 2 - 0
netflix-service/src/main/java/com/cyksj/service/user/UserService.java

@@ -29,4 +29,6 @@ public interface UserService extends IService<User> {
      * 获取关联用户id集合
      */
     List<Long> getRelationUserIdList(Long userId, User selectUser);
+
+	List<Long> getUserIdsByNickname(String nickname);
 }

+ 27 - 5
netflix-service/src/main/java/com/cyksj/service/user/impl/UserPointsServiceImpl.java → netflix-service/src/main/java/com/cyksj/service/user/impl/UserBenefitsServiceImpl.java

@@ -4,7 +4,7 @@ import com.cyksj.mapper.DistributeOrdersDeductPointsRecordMapper;
 import com.cyksj.mapper.UserMapper;
 import com.cyksj.model.entity.DistributeOrdersDeductPointsRecord;
 import com.cyksj.model.entity.User;
-import com.cyksj.service.user.UserPointsService;
+import com.cyksj.service.user.UserBenefitsService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.dao.DuplicateKeyException;
 import org.springframework.stereotype.Service;
@@ -13,13 +13,13 @@ import java.math.BigDecimal;
 
 /*
  *项目名: netflix
- *文件名: UserPointsServiceImpl
+ *文件名: UserBenefitsServiceImpl
  *创建者: JavaZou
  *创建时间:2023/5/18 16:06
  */
 @Service
 @RequiredArgsConstructor
-public class UserPointsServiceImpl implements UserPointsService {
+public class UserBenefitsServiceImpl implements UserBenefitsService {
 	private final UserMapper userMapper;
 
 	private final DistributeOrdersDeductPointsRecordMapper distributeOrdersDeductPointsRecordMapper;
@@ -33,7 +33,7 @@ public class UserPointsServiceImpl implements UserPointsService {
 			Integer success = userMapper.subPointsNegative(userId, points, deductPoints);
 			if (success == 1){
 				//记录积分已发送后,退款后扣除积分记录
-				deductRecord(orderId, userId, deductPoints);
+				deductPointsRecord(orderId, userId, deductPoints);
 				break;
 			}
 		}
@@ -50,7 +50,29 @@ public class UserPointsServiceImpl implements UserPointsService {
 		}
 	}
 
-	public void deductRecord(Long orderId, Long userId, BigDecimal deductPoints) {
+	@Override
+	public void addUserBalance(Long userId, BigDecimal addBalance) {
+		while (true) {
+			User user = userMapper.selectById(userId);
+			if (user == null) break;
+			BigDecimal balance = user.getBalance();
+			Integer success = userMapper.addUserBalance(user.getId(), balance, addBalance);
+			if (success == 1) break;
+		}
+	}
+
+	@Override
+	public void deductUserBalance(Long userId, BigDecimal subBalance) {
+		while (true) {
+			User user = userMapper.selectById(userId);
+			if (user == null) break;
+			BigDecimal balance = user.getBalance();
+			Integer success = userMapper.deductUserBalance(user.getId(), balance, subBalance);
+			if (success == 1) break;
+		}
+	}
+
+	public void deductPointsRecord(Long orderId, Long userId, BigDecimal deductPoints) {
 		try {
 			DistributeOrdersDeductPointsRecord record = new DistributeOrdersDeductPointsRecord();
 			record.setOrderId(orderId);

+ 7 - 0
netflix-service/src/main/java/com/cyksj/service/user/impl/UserServiceImpl.java

@@ -55,6 +55,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 
 	private final UserDistributePopularizeMapper distributePopularizeMapper;
 
+	private final UserMapper userMapper;
+
 	private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
 
 	@Override
@@ -239,4 +241,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 		}
 		return userIds;
 	}
+
+	@Override
+	public List<Long> getUserIdsByNickname(String nickname) {
+		return userMapper.selectListByNickname(nickname);
+	}
 }

+ 6 - 4
netflix-service/src/main/java/com/cyksj/task/Scheduler.java

@@ -29,7 +29,7 @@ import com.cyksj.service.register.RegisterOrderDonService;
 import com.cyksj.service.register.SpotifyService;
 import com.cyksj.service.relation.GroupRelationClearService;
 import com.cyksj.service.sms.SmsService;
-import com.cyksj.service.user.UserPointsService;
+import com.cyksj.service.user.UserBenefitsService;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.param.Operator;
 import com.ejlchina.searcher.util.MapUtils;
@@ -93,7 +93,8 @@ public class Scheduler {
         private final CmsUserMapper cmsUserMapper;
 
 	    private final AccountDoubleVerifyRecordMapper doubleVerifyRecordMapper;
-	    private final UserPointsService userPointsService;
+
+	    private final UserBenefitsService userBenefitsService;
 
 
     /**
@@ -114,7 +115,7 @@ public class Scheduler {
 		if (envCommonService.isPrdEnv()) {
 			DateTime now = new DateTime();
 			DateTime zero = DateUtil.offset(now, DateField.HOUR, -9);
-			DateTime time = DateUtil.offset(zero, DateField.DAY_OF_MONTH, 3);
+			DateTime time = DateUtil.offset(zero, DateField.DAY_OF_MONTH, 1);
 			List<GroupsRelationView> groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getExpiryTime, time).op(Operator.LessEqual).build());
 			//String url = String.format("%sauth/weChat?url=%syinhe/web/ticket", envCommonService.getHost(), envCommonService.getDomain());
 			for (GroupsRelationView relation : groupsRelations) {
@@ -172,6 +173,7 @@ public class Scheduler {
 				//短信通知过期账号 用户
 				try {
 					String day = DateUtil.formatBetween(zero, relation.getExpiryTime(), BetweenFormater.Level.DAY);
+					if ("0".equals(day)) day = "今";
 					smsService.sendSmsToRelationUser(relation.getUserId(), String.format(Constant.ACCOUNT_EXPIRY_MSG_TEMPLATE, relation.getTitle(), day));
 				} catch (Exception e) {
 					log.error("过期账号用户:{}发送短信错误:{}", relation.getUserId(), StringUtil.getErrorText(e));
@@ -247,7 +249,7 @@ public class Scheduler {
             } else {
                 wait.setSendStatus(DistributeWaitingSendPoints.Status.success);
                 log.info("更新用户积分开始=====>");
-	            userPointsService.addDistributePoints(sharedId, wait.getPoints());
+	            userBenefitsService.addDistributePoints(sharedId, wait.getPoints());
 	            distributeWaitingSendPointsMapper.updateById(wait);
 	            log.info("分销积分表订单id:{}发放积分success", wait.getOrderId());
             }

+ 2 - 1
netflix-web/src/main/java/com/cyksj/config/SaTokenConfigure.java

@@ -40,7 +40,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
 						"/manage/account/get/expired/accounts",
 						"/manage/account/put/account",
 						"/manage/coupon/get/classificationList",
-						"/manage/coupon/get/couponList"));
+						"/manage/coupon/get/couponList",
+						"manage/order/wx/refund/notify"));
 		//注解权限校验
 		registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
 	}

+ 0 - 1
netflix-web/src/main/java/com/cyksj/web/controller/cps/CpsPopularizeController.java

@@ -46,7 +46,6 @@ public class CpsPopularizeController {
 		MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
 		builder.put("userId", String.format("and user_id = %s", cpsUserId));
 		SearchResult<UserDistributePopularizeView> search = beanSearcher.search(UserDistributePopularizeView.class, builder.build());
-		String domain = envCommonService.getDomain();
 		search.getDataList().forEach(data -> {
 			data.setPopularizeLink(String.format(envCommonService.DISTRIBUTE_DESIGN_URL_PREFIX, data.getCode()));
 		});

+ 108 - 30
netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java

@@ -10,11 +10,16 @@ import com.cyksj.common.annotation.Log;
 import com.cyksj.common.annotation.NoSubmit;
 import com.cyksj.common.constant.Constant;
 import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.IoKit;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.common.util.Xmls;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.BusinessType;
 import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.GroupsRelationMapper;
 import com.cyksj.mapper.OrderCommentMapper;
 import com.cyksj.mapper.RealGoodsInterestUserMapper;
+import com.cyksj.mapper.channel.ChannelPopularizeMapper;
 import com.cyksj.mapper.manage.RealGoodsOrderHandleMapper;
 import com.cyksj.mapper.manage.coupon.CouponMapper;
 import com.cyksj.model.entity.*;
@@ -24,10 +29,8 @@ import com.cyksj.model.excel.ExcelRealGoodsUserInterestData;
 import com.cyksj.model.excel.ExcelRealGoodsUserInterestHasPaymentData;
 import com.cyksj.model.manage.request.ReqSetLogisticsPost;
 import com.cyksj.model.manage.views.AccountView;
-import com.cyksj.model.manage.views.OrderDonView;
-import com.cyksj.model.request.ClickOutReq;
-import com.cyksj.model.request.RealGoodsServiceReq;
-import com.cyksj.model.request.UserRealGoodsInterestReq;
+import com.cyksj.model.manage.views.OrderDonBackView;
+import com.cyksj.model.request.*;
 import com.cyksj.model.views.*;
 import com.cyksj.service.mange.CmsOrderDonService;
 import com.cyksj.service.user.UserService;
@@ -38,28 +41,29 @@ import com.ejlchina.searcher.param.Operator;
 import com.ejlchina.searcher.util.MapBuilder;
 import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.OutputStream;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 
 /**
  * @author chan
  * @date 2021/10/12 下午11:05
  */
+@Slf4j
 @RequestMapping("/manage/order")
 @RestController
 @RequiredArgsConstructor
 public class CmsOrderController {
-
-    private final HttpServletResponse response;
-
     private final HttpServletRequest request;
 
     private final CmsOrderDonService cmsOrderDonService;
@@ -76,30 +80,42 @@ public class CmsOrderController {
 
 	private final OrderCommentMapper orderCommentMapper;
 
+	private final GroupsRelationMapper relationMapper;
 
-    @GetMapping("/get")
-    @SaCheckPermission("order:view")
-    public Result<SearchResult<OrderDonView>> get() {
-        SearchResult<OrderDonView> search = beanSearcher.search(OrderDonView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
-        search.getDataList().forEach(data -> {
-	        if (data.getGoodsDonType() != null && Constant.realGoodsType.contains(data.getGoodsDonType())) {
-                RealGoodsOrderHandle realGoodsOrderHandle = realGoodsOrderHandleMapper.selectOne(Wrappers.lambdaQuery(RealGoodsOrderHandle.class).eq(RealGoodsOrderHandle::getOrderId, data.getId()).last("limit 1"));
-                if (realGoodsOrderHandle != null) {
-                    data.setBeforeServiceName(realGoodsOrderHandle.getBeforeServiceName());
-                    data.setAfterServiceName(realGoodsOrderHandle.getAfterServiceName());
-                }
-            }
-            if (data.getCouponUserId() != 0) {
-                data.setCouponName(couponMapper.getCouponNameByCouponUserId(data.getCouponUserId()));
-                data.setCouponMoney(data.getCouponMoney() == null ? BigDecimal.ZERO : data.getCouponMoney());
-            }
-            Optional.ofNullable(data.getGroupsId()).ifPresent(groupsId->{
-	            AccountView accountView = beanSearcher.searchFirst(AccountView.class, MapUtils.builder().field(AccountView::getGroupsId, groupsId).onlySelect(AccountView::getAccount).build());
-	            Optional.ofNullable(accountView).ifPresent(account -> data.setGtAccount(account.getAccount()));
-            });
-        });
-        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
-    }
+	private final ChannelPopularizeMapper channelPopularizeMapper;
+
+	@GetMapping("/get")
+	@SaCheckPermission("order:view")
+	public Result<SearchResult<OrderDonBackView>> get(Long channelId) {
+		MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
+		if (channelId != null) {
+			List<Long> popularizeIds = channelPopularizeMapper.getPopularizeIdByChannelId(channelId);
+			builder.field(OrderDonBackView::getPopularizeId, popularizeIds).op(Operator.InList);
+		}
+		SearchResult<OrderDonBackView> search = beanSearcher.search(OrderDonBackView.class, builder.build());
+		search.getDataList().forEach(data -> {
+			if (data.getPopularizeId() != null) {
+				Optional.ofNullable(channelPopularizeMapper.selectChannelByPid(data.getPopularizeId()))
+						.ifPresent(channelName -> data.setChannel(channelName));
+			}
+			if (data.getGoodsDonType() != null && Constant.realGoodsType.contains(data.getGoodsDonType())) {
+				RealGoodsOrderHandle realGoodsOrderHandle = realGoodsOrderHandleMapper.selectOne(Wrappers.lambdaQuery(RealGoodsOrderHandle.class).eq(RealGoodsOrderHandle::getOrderId, data.getId()).last("limit 1"));
+				if (realGoodsOrderHandle != null) {
+					data.setBeforeServiceName(realGoodsOrderHandle.getBeforeServiceName());
+					data.setAfterServiceName(realGoodsOrderHandle.getAfterServiceName());
+				}
+			}
+			if (data.getCouponUserId() != 0) {
+				data.setCouponName(couponMapper.getCouponNameByCouponUserId(data.getCouponUserId()));
+				data.setCouponMoney(data.getCouponMoney() == null ? BigDecimal.ZERO : data.getCouponMoney());
+			}
+			Optional.ofNullable(data.getGroupsId()).ifPresent(groupsId -> {
+				AccountView accountView = beanSearcher.searchFirst(AccountView.class, MapUtils.builder().field(AccountView::getGroupsId, groupsId).onlySelect(AccountView::getAccount).build());
+				Optional.ofNullable(accountView).ifPresent(account -> data.setGtAccount(account.getAccount()));
+			});
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
 
 	/**
 	 * 用户退款订单列表
@@ -432,4 +448,66 @@ public class CmsOrderController {
 				.eq(RealGoodsOrderHandle::getOrderId, id));
 		return GatewayResponse.SUCCESS.newBuilder().toResult();
 	}
+
+	/**
+	 * 订单统一退款
+	 */
+	@PostMapping("/unified/refund")
+	@Log(module = "订单管理/统一退款", businessType = BusinessType.PUT, isSaveRequestData = true)
+	public Result<String> unifiedRefund(@RequestBody @Validated OrderRefundReq refundReq) throws Exception {
+		cmsOrderDonService.unifiedRefund(refundReq);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("退款成功");
+	}
+
+	/**
+	 * 微信退款回调
+	 */
+	@PostMapping("/wx/refund/notify")
+	public void wxRefundNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
+		String xml = IoKit.toString(request.getInputStream());
+		log.info("=== 退款成功的回调: \n {}", xml);
+
+		// 解析xml
+		Map<String, String> map = Xmls.toMap(xml);
+
+		// 退款失败
+		if (!StrUtil.equals("SUCCESS", map.get("return_code"))) {
+			throw BusinessRuntimeException.getInstance("退款失败.");
+		}
+
+		cmsOrderDonService.wxRefundNotify(map);
+
+		String toWechatXmlResponse = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
+
+		OutputStream out = null;
+		try {
+			out = response.getOutputStream();
+			IoKit.write(toWechatXmlResponse, out);
+		} catch (Throwable e) {
+			throw BusinessRuntimeException.getInstance(e == null ? null : e.getMessage());
+		} finally {
+			IoKit.close(out);
+		}
+	}
+
+	/**
+	 * 更换账号
+	 */
+	@PutMapping("/get/sameSpec/empty/relation")
+	public Result<String> changeRelation(@RequestBody ChangeRelationReq req) {
+		cmsOrderDonService.changeRelation(req);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+
+	/**
+	 * 多余车位的空账号
+	 */
+	@GetMapping("/get/sameSpec/empty/relation")
+	public Result<SearchResult<EmptyGroupsRelationView>> getSameSpecEmptyRelation(Long skuId) {
+		SearchResult<EmptyGroupsRelationView> search = beanSearcher.search(EmptyGroupsRelationView.class, MapUtils.builder()
+				.field(EmptyGroupsRelationView::getSkuId, skuId).orderBy(EmptyGroupsRelationView::getAvailableNum).desc()
+				.build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
 }

+ 3 - 8
netflix-web/src/main/java/com/cyksj/web/controller/manage/distribute/DistributeController.java

@@ -16,7 +16,6 @@ import com.cyksj.enums.BusinessType;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.GoodsDonMapper;
 import com.cyksj.mapper.GoodsDonSkuMapper;
-import com.cyksj.mapper.OrderDonMapper;
 import com.cyksj.mapper.UserMapper;
 import com.cyksj.mapper.manage.cms.CmsUserMapper;
 import com.cyksj.mapper.manage.distribute.*;
@@ -27,7 +26,7 @@ import com.cyksj.model.views.*;
 import com.cyksj.service.distribute.DistributeService;
 import com.cyksj.service.mange.business.UserBusinessRateService;
 import com.cyksj.service.mange.statistics.StatisticsDataService;
-import com.cyksj.service.user.UserPointsService;
+import com.cyksj.service.user.UserBenefitsService;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
 import com.ejlchina.searcher.param.Operator;
@@ -89,11 +88,7 @@ public class DistributeController {
 
 	private final BusinessDistributeDefaultRateConfigMapper businessDefaultRateConfigMapper;
 
-	private final UserDistributeSharedMapper userDistributeSharedMapper;
-
-	private final OrderDonMapper orderDonMapper;
-
-	private final UserPointsService userPointsService;
+	private final UserBenefitsService userBenefitsService;
 
 	/**
 	 * 添加固定推广者
@@ -325,7 +320,7 @@ public class DistributeController {
 		if (DistributeMoneyApply.Status.fail.name().equals(distributeApplyMoneyReq.getVerifyStatus())) {
 			distributeMoneyApply.setReason(distributeApplyMoneyReq.getReason());
 			Long userId = distributeMoneyApply.getUserId();
-			userPointsService.addDistributePoints(userId, distributeMoneyApply.getPoints());
+			userBenefitsService.addDistributePoints(userId, distributeMoneyApply.getPoints());
 			log.info("归还积分:{}至用户{}结束", distributeMoneyApply.getPoints(), userId);
 		}
 		if (DistributeMoneyApply.Status.success.name().equals(distributeApplyMoneyReq.getVerifyStatus())) {

+ 3 - 3
netflix-web/src/main/java/com/cyksj/web/controller/manage/register/CmsRegisterController.java

@@ -18,7 +18,7 @@ import com.cyksj.model.entity.RegisterOrderDon;
 import com.cyksj.model.entity.SpotifyUnLimitRecord;
 import com.cyksj.model.excel.ExcelAppleIdTemplateData;
 import com.cyksj.model.excel.listener.ExcelAppleDataListener;
-import com.cyksj.model.request.WxOrderRefundReq;
+import com.cyksj.model.request.OrderRefundReq;
 import com.cyksj.model.views.AppleIdCountryOrAreaDataView;
 import com.cyksj.model.views.AppleIdHeadDataView;
 import com.cyksj.model.views.AppleIdRegisterView;
@@ -138,8 +138,8 @@ public class CmsRegisterController {
      */
     @PostMapping("/wx/refund")
     @SaCheckPermission("sign:refund")
-    public Result<String> wxRefund(@RequestBody @Validated WxOrderRefundReq wxOrderRefundReq) throws Exception {
-        return registerOrderDonService.wxRefund(wxOrderRefundReq);
+    public Result<String> wxRefund(@RequestBody @Validated OrderRefundReq refundReq) throws Exception {
+        return registerOrderDonService.wxRefund(refundReq);
     }
 
     /**

+ 9 - 52
netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java

@@ -5,7 +5,6 @@ import cn.hutool.extra.qrcode.QrCodeUtil;
 import cn.hutool.extra.qrcode.QrConfig;
 import com.alipay.api.response.AlipayFundTransCommonQueryResponse;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.cyksj.common.annotation.Log;
 import com.cyksj.common.constant.Constant;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.util.IoKit;
@@ -14,7 +13,6 @@ import com.cyksj.common.util.Xmls;
 import com.cyksj.config.zfb.BaseZfbConfig;
 import com.cyksj.config.zfb.ZfbProductCode;
 import com.cyksj.dto.Result;
-import com.cyksj.enums.BusinessType;
 import com.cyksj.enums.GatewayApiCode;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.OrderDonMapper;
@@ -28,8 +26,6 @@ import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.OrderDonView;
 import com.cyksj.model.request.AlipayOrderDonReq;
 import com.cyksj.model.request.OrderPayRequest;
-import com.cyksj.model.request.WxOrderRefundReq;
-import com.cyksj.model.request.ZfbOrderRefundReq;
 import com.cyksj.model.response.AliPayTradeStatus;
 import com.cyksj.model.views.GoodsDonSkuView;
 import com.cyksj.model.views.OrderCommentView;
@@ -110,6 +106,15 @@ public class OrderController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
     }
 
+    /**
+     * 0元订单确认
+     */
+    @PostMapping("/post/order/status/{orderId}")
+    public Result<String> zeroOrderConfirm(@PathVariable Long orderId) {
+        orderDonService.zeroOrderConfirm(orderId);
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
+
 
     /**
      * 支付
@@ -140,45 +145,6 @@ public class OrderController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(QrCodeUtil.generateAsBase64(codeUrl, qrConfig, "png"));
     }
 
-    /**
-     * 微信订单退款
-     */
-    @PostMapping("/wx/refund")
-    @Log(module = "订单管理/微信退款", businessType = BusinessType.PUT, isSaveRequestData = true)
-    public Result<String> wxRefund(@RequestBody @Validated WxOrderRefundReq wxOrderRefundReq) throws Exception {
-        return orderDonService.wxRefund(wxOrderRefundReq);
-    }
-
-    /**
-     * 微信退款回调
-     */
-    @PostMapping("/wx/refund/notify")
-    public void wxRefundNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
-        String xml = IoKit.toString(request.getInputStream());
-        log.info("=== 退款成功的回调: \n {}", xml);
-
-        // 解析xml
-        Map<String, String> map = Xmls.toMap(xml);
-
-        // 退款失败
-        if (!StrUtil.equals("SUCCESS", map.get("return_code"))) {
-            throw BusinessRuntimeException.getInstance("退款失败.");
-        }
-
-        orderDonService.wxRefundNotify(map);
-
-        String toWechatXmlResponse = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
-
-        OutputStream out = null;
-        try {
-            out = response.getOutputStream();
-            IoKit.write(toWechatXmlResponse, out);
-        } catch (Throwable e) {
-            throw BusinessRuntimeException.getInstance(e == null ? null : e.getMessage());
-        } finally {
-            IoKit.close(out);
-        }
-    }
     /**
      * 微信支付回调
      * @author chan
@@ -293,15 +259,6 @@ public class OrderController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(aliPayParams.getBody());
     }
 
-    /**
-     * 支付宝退款
-     */
-    @PostMapping("/zfb/refund")
-    @Log(module = "订单管理/支付宝退款", businessType = BusinessType.PUT, isSaveRequestData = true)
-    public Result<String> zfbOrderRefund(@RequestBody @Validated ZfbOrderRefundReq zfbOrderRefundReq) throws Exception {
-        return orderDonService.zfbRefund(zfbOrderRefundReq);
-    }
-
     /**
 	 * 支付宝回调
 	 */

+ 11 - 9
netflix-web/src/main/java/com/cyksj/web/controller/user/DistributePopularizeController.java

@@ -10,7 +10,9 @@ import com.alipay.api.AlipayResponse;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.common.EnvCommonService;
 import com.cyksj.common.annotation.NoSubmit;
+import com.cyksj.common.constant.Constant;
 import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.SmsUtil;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.GoodsDonMapper;
@@ -24,13 +26,9 @@ import com.cyksj.mapper.manage.distribute.UserDistributeMapper;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.request.ApplyMoneyReq;
 import com.cyksj.model.request.TransferAccountsReq;
-import com.cyksj.model.views.DistributeMoneyApplyView;
-import com.cyksj.model.views.DistributePopularizeOrdersView;
-import com.cyksj.model.views.DistributePopularizeView;
-import com.cyksj.model.views.UserDistributeView;
-import com.cyksj.service.order.OrderDonService;
 import com.cyksj.model.views.*;
-import com.cyksj.service.user.UserPointsService;
+import com.cyksj.service.order.OrderDonService;
+import com.cyksj.service.user.UserBenefitsService;
 import com.cyksj.web.util.StpUserUtil;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
@@ -81,7 +79,7 @@ public class DistributePopularizeController {
 
 	private final OrderDonService orderService;
 
-	private final UserPointsService userPointsService;
+	private final UserBenefitsService userBenefitsService;
 
 	/**
 	 * 推广订单明细
@@ -270,7 +268,7 @@ public class DistributePopularizeController {
 				} else {
 					distributeMoneyApply.setVerifyStatus(DistributeMoneyApply.Status.fail);
 					distributeMoneyApply.setReason(alipayResponse.getSubMsg());
-					userPointsService.addDistributePoints(userId, distributeMoneyApply.getPoints());
+					userBenefitsService.addDistributePoints(userId, distributeMoneyApply.getPoints());
 					if (StrUtil.equals(alipayResponse.getSubCode(), "SECURITY_CHECK_FAILED")) {
 						return GatewayResponse.FAIL.newBuilder().setMsg("当前支付宝提现人数较多,请稍后再试").toResult();
 					}
@@ -279,8 +277,12 @@ public class DistributePopularizeController {
 			}
 			try {
 				distributeMoneyApplyMapper.insert(distributeMoneyApply);
+				//大额提现 超过400
+				if (distributeMoneyApply.getVerifyStatus() == DistributeMoneyApply.Status.verifying && distributeMoneyApply.getMoney().compareTo(fourHundred) > 0) {
+					SmsUtil.sendLuoKey2Msg(Constant.ZK_MOBILE, "推广有大额提现申请【银河录像局】");
+				}
 			} catch (Exception e) {
-				userPointsService.addDistributePoints(userId, distributeMoneyApply.getPoints());
+				userBenefitsService.addDistributePoints(userId, distributeMoneyApply.getPoints());
 				return GatewayResponse.FAIL.newBuilder().setMsg("网络错误,请重试").toResult();
 			}
 			return GatewayResponse.SUCCESS.newBuilder().toResult(distributeMoneyApply);

+ 14 - 0
netflix-web/src/main/java/com/cyksj/web/controller/user/UserController.java

@@ -109,6 +109,7 @@ public class UserController {
         personalView.setActivePoints(user.getActivePoints());
         //推广积分
         personalView.setPopularizePoints(user.getPoints());
+        personalView.setBalance(user.getBalance());
         OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
                 .eq(OrderDon::getUserId, userId)
                 .eq(OrderDon::getStatus, OrderDon.Status.noPayment).last("limit 1"));
@@ -250,4 +251,17 @@ public class UserController {
         userService.updateById(user);
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
+
+    /**
+     * 查看用户所有余额
+     */
+    @GetMapping("/get/balance")
+    public Result<User> getUserBalance() {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        User user = userService.getOne(Wrappers.lambdaQuery(User.class)
+                .eq(User::getId, userId)
+                .select(User::getNickname, User::getBalance)
+                .last("limit 1"));
+        return GatewayResponse.SUCCESS.newBuilder().toResult(user);
+    }
 }