瀏覽代碼

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/main/java/com/gree/mall/contest/commonmapper/CustomGoodsMapper.java
#	src/main/java/com/gree/mall/contest/logic/goods/GoodsLogic.java
‘linchangsheng’ 1 周之前
父節點
當前提交
1d889f2bcc
共有 81 個文件被更改,包括 7837 次插入235 次删除
  1. 13 0
      pom.xml
  2. 14 0
      src/main/java/com/gree/mall/contest/bean/coupon/CouponObtainBean.java
  3. 17 0
      src/main/java/com/gree/mall/contest/bean/coupon/CouponQrcodeBean.java
  4. 15 0
      src/main/java/com/gree/mall/contest/bean/coupon/CustomCoupouBean.java
  5. 33 0
      src/main/java/com/gree/mall/contest/bean/goods/AckGoodsBean.java
  6. 30 0
      src/main/java/com/gree/mall/contest/bean/goods/CategoryBean.java
  7. 16 0
      src/main/java/com/gree/mall/contest/bean/goods/CommentTagCount.java
  8. 20 0
      src/main/java/com/gree/mall/contest/bean/goods/CutGoodsBean.java
  9. 25 1
      src/main/java/com/gree/mall/contest/bean/goods/GoodsBean.java
  10. 21 0
      src/main/java/com/gree/mall/contest/bean/goods/GoodsComment.java
  11. 15 0
      src/main/java/com/gree/mall/contest/bean/goods/GoodsCommentCount.java
  12. 16 0
      src/main/java/com/gree/mall/contest/bean/goods/GoodsFavoriteBean.java
  13. 4 3
      src/main/java/com/gree/mall/contest/bean/goods/GoodsPackageBean.java
  14. 40 0
      src/main/java/com/gree/mall/contest/bean/goods/GoodsSpecSecBean.java
  15. 28 0
      src/main/java/com/gree/mall/contest/bean/goods/PromotionGoodsBean.java
  16. 16 0
      src/main/java/com/gree/mall/contest/bean/goods/PromotionShareQrCode.java
  17. 20 0
      src/main/java/com/gree/mall/contest/bean/merchant/WebsitApplyAdd.java
  18. 22 0
      src/main/java/com/gree/mall/contest/bean/merchant/WebsitApplyDetail.java
  19. 47 0
      src/main/java/com/gree/mall/contest/bean/merchant/WebsitApplyVO.java
  20. 35 0
      src/main/java/com/gree/mall/contest/bean/order/OrderAckBean.java
  21. 48 0
      src/main/java/com/gree/mall/contest/bean/order/OrderBuyBean.java
  22. 19 0
      src/main/java/com/gree/mall/contest/bean/order/OrderCommentBean.java
  23. 4 1
      src/main/java/com/gree/mall/contest/bean/order/OrderDetailBean.java
  24. 15 0
      src/main/java/com/gree/mall/contest/bean/order/OrderDetailGoodsBean.java
  25. 17 0
      src/main/java/com/gree/mall/contest/bean/order/PromotionDiscountBean.java
  26. 14 0
      src/main/java/com/gree/mall/contest/bean/order/RefundGoods.java
  27. 27 0
      src/main/java/com/gree/mall/contest/bean/order/RefundGoodsBean.java
  28. 23 0
      src/main/java/com/gree/mall/contest/bean/pay/PayDetail.java
  29. 42 0
      src/main/java/com/gree/mall/contest/bean/promotion/PromotionLuckDrawExchangeDetail.java
  30. 16 0
      src/main/java/com/gree/mall/contest/bean/user/UserCouponCountBean.java
  31. 30 0
      src/main/java/com/gree/mall/contest/commonmapper/CategoryMapper.java
  32. 10 0
      src/main/java/com/gree/mall/contest/commonmapper/CommonMapper.java
  33. 24 0
      src/main/java/com/gree/mall/contest/commonmapper/CustomCoupouMapper.java
  34. 9 2
      src/main/java/com/gree/mall/contest/commonmapper/CustomGoodsMapper.java
  35. 65 0
      src/main/java/com/gree/mall/contest/commonmapper/GoodsSpecDetailMapper.java
  36. 14 0
      src/main/java/com/gree/mall/contest/commonmapper/SoldNumMapper.java
  37. 5 0
      src/main/java/com/gree/mall/contest/constant/Constant.java
  38. 181 0
      src/main/java/com/gree/mall/contest/controller/mini/common/MiniCommonController.java
  39. 83 0
      src/main/java/com/gree/mall/contest/controller/mini/common/PayController.java
  40. 155 0
      src/main/java/com/gree/mall/contest/controller/mini/coupon/CouponController.java
  41. 170 0
      src/main/java/com/gree/mall/contest/controller/mini/goods/GoodsController.java
  42. 73 0
      src/main/java/com/gree/mall/contest/controller/mini/goods/GoodsFavoriteController.java
  43. 74 0
      src/main/java/com/gree/mall/contest/controller/mini/order/OrderCommentController.java
  44. 268 0
      src/main/java/com/gree/mall/contest/controller/mini/order/OrderController.java
  45. 137 0
      src/main/java/com/gree/mall/contest/controller/mini/order/OrderTaxController.java
  46. 78 0
      src/main/java/com/gree/mall/contest/controller/mini/order/ShoppingCartController.java
  47. 1 1
      src/main/java/com/gree/mall/contest/controller/mini/user/AddressController.java
  48. 1 1
      src/main/java/com/gree/mall/contest/controller/mini/user/UserController.java
  49. 1 1
      src/main/java/com/gree/mall/contest/controller/mini/user/WorkerController.java
  50. 113 0
      src/main/java/com/gree/mall/contest/controller/pc/apply/ApplyController.java
  51. 153 0
      src/main/java/com/gree/mall/contest/controller/pc/goods/CategoryLogic.java
  52. 16 0
      src/main/java/com/gree/mall/contest/enums/ExchangeCodeTypeEnum.java
  53. 22 0
      src/main/java/com/gree/mall/contest/enums/merchant/WebsitApplyStatusEnum.java
  54. 68 6
      src/main/java/com/gree/mall/contest/logic/FreightLogic.java
  55. 23 23
      src/main/java/com/gree/mall/contest/logic/SMSLogic.java
  56. 168 0
      src/main/java/com/gree/mall/contest/logic/activity/PromotionLuckDrawLogic.java
  57. 91 30
      src/main/java/com/gree/mall/contest/logic/common/CommonLogic.java
  58. 230 43
      src/main/java/com/gree/mall/contest/logic/common/WechatLogic.java
  59. 452 80
      src/main/java/com/gree/mall/contest/logic/coupon/CouponLogic.java
  60. 70 0
      src/main/java/com/gree/mall/contest/logic/goods/GoodsFavoriteLogic.java
  61. 434 24
      src/main/java/com/gree/mall/contest/logic/goods/GoodsLogic.java
  62. 124 0
      src/main/java/com/gree/mall/contest/logic/merchant/MerchantLogic.java
  63. 138 0
      src/main/java/com/gree/mall/contest/logic/order/OrderCommentLogic.java
  64. 1503 19
      src/main/java/com/gree/mall/contest/logic/order/OrderLogic.java
  65. 310 0
      src/main/java/com/gree/mall/contest/logic/order/OrderTaxLogic.java
  66. 288 0
      src/main/java/com/gree/mall/contest/logic/pay/PayLogic.java
  67. 60 0
      src/main/java/com/gree/mall/contest/utils/AESUtil.java
  68. 46 0
      src/main/java/com/gree/mall/contest/utils/Base64Util.java
  69. 92 0
      src/main/java/com/gree/mall/contest/utils/MD5Utils.java
  70. 33 0
      src/main/java/com/gree/mall/contest/utils/ShaUtils.java
  71. 213 0
      src/main/java/com/gree/mall/contest/utils/Utils.java
  72. 201 0
      src/main/java/com/gree/mall/contest/utils/WeChatUtils.java
  73. 148 0
      src/main/java/com/gree/mall/contest/utils/email/EmailUtilsNew.java
  74. 319 0
      src/main/java/com/gree/mall/contest/utils/http/HttpUtils.java
  75. 110 0
      src/main/java/com/gree/mall/contest/utils/http/OkHttpUtil.java
  76. 93 0
      src/main/resources/mapper/CategoryMapper.xml
  77. 10 0
      src/main/resources/mapper/CommonMapper.xml
  78. 85 0
      src/main/resources/mapper/CustomCoupouMapper.xml
  79. 16 0
      src/main/resources/mapper/CustomGoodsMapper.xml
  80. 137 0
      src/main/resources/mapper/GoodsSpecDetailMapper.xml
  81. 20 0
      src/main/resources/mapper/SoldNumMapper.xml

+ 13 - 0
pom.xml

@@ -37,6 +37,8 @@
         <ali.easyexcel.version>3.3.2</ali.easyexcel.version>
         <poi.version>3.17</poi.version>
         <bitwalker.version>1.21</bitwalker.version>
+        <javax-mail.version>1.5.5</javax-mail.version>
+        <ok-http.version>4.0.1</ok-http.version>
     </properties>
 
     <dependencies>
@@ -223,6 +225,17 @@
             <artifactId>UserAgentUtils</artifactId>
             <version>${bitwalker.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.sun.mail</groupId>
+            <artifactId>javax.mail</artifactId>
+            <version>${javax-mail.version}</version>
+        </dependency>
+        <!--okhttp3-->
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>${ok-http.version}</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 14 - 0
src/main/java/com/gree/mall/contest/bean/coupon/CouponObtainBean.java

@@ -0,0 +1,14 @@
+package com.gree.mall.contest.bean.coupon;
+
+import com.gree.mall.contest.plus.entity.Coupon;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class CouponObtainBean extends Coupon {
+    @Schema(description = "优惠券类型,0/1/2,不可领/可领/已领")
+    private Integer obtainType;
+}

+ 17 - 0
src/main/java/com/gree/mall/contest/bean/coupon/CouponQrcodeBean.java

@@ -0,0 +1,17 @@
+package com.gree.mall.contest.bean.coupon;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class CouponQrcodeBean {
+    @Schema(description = "优惠券二维码")
+    private String qrcode;
+
+    @Schema(description = "总分享次数")
+    private Integer shareTimes;
+
+    @Schema(description = "剩余分享次数")
+    private Integer leftShareTimes;
+
+}

+ 15 - 0
src/main/java/com/gree/mall/contest/bean/coupon/CustomCoupouBean.java

@@ -0,0 +1,15 @@
+package com.gree.mall.contest.bean.coupon;
+
+import com.gree.mall.contest.plus.entity.UserCoupon;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class CustomCoupouBean extends UserCoupon {
+
+    @Schema(description = "优惠券是否可用标记true:可用,false:不可用")
+    private Boolean useableFlag;
+
+}

+ 33 - 0
src/main/java/com/gree/mall/contest/bean/goods/AckGoodsBean.java

@@ -0,0 +1,33 @@
+package com.gree.mall.contest.bean.goods;
+
+import com.gree.mall.contest.plus.entity.Goods;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AckGoodsBean extends Goods {
+
+    @Schema(description = "规则值")
+    private String specValue;
+    @Schema(description = "规格图片")
+    private String specImgUrl;
+    @Schema(description = "价格")
+    private BigDecimal price;
+    @Schema(description = "划线价")
+    private BigDecimal orgPrice;
+    @Schema(description = "商品数量")
+    private Integer num;
+    @Schema(description = "是否为礼品卡活动商品")
+    private Boolean isGift;
+    @Schema(description = "秒杀活动id")
+    private String secKillId;
+    @Schema(description = "团购活动id")
+    private String promotionGroupId;
+
+
+}

+ 30 - 0
src/main/java/com/gree/mall/contest/bean/goods/CategoryBean.java

@@ -0,0 +1,30 @@
+package com.gree.mall.contest.bean.goods;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class CategoryBean {
+
+    @Schema(description = "分类id")
+    private String categoryId;
+
+    @Schema(description = "分类名称")
+    private String name;
+    @Schema(description = "图片url")
+    private String imgUrl;
+    @Schema(description = "商品数量")
+    private String goodsNum;
+    @Schema(description = "层级")
+    private String level;
+    @Schema(description = "上层id")
+    private String parentId;
+    @Schema(description = "排序值")
+    private String sortNum;
+
+    @Schema(description = "子分类")
+    private List<CategoryBean> sub;
+
+}

+ 16 - 0
src/main/java/com/gree/mall/contest/bean/goods/CommentTagCount.java

@@ -0,0 +1,16 @@
+package com.gree.mall.contest.bean.goods;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class CommentTagCount {
+
+    @Schema(description = "评价标签id")
+    private String orderCommentTagId;
+    @Schema(description = "标签名称")
+    private String tag;
+    @Schema(description = "标签数量")
+    private Integer total;
+
+}

+ 20 - 0
src/main/java/com/gree/mall/contest/bean/goods/CutGoodsBean.java

@@ -0,0 +1,20 @@
+package com.gree.mall.contest.bean.goods;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class CutGoodsBean {
+    @Schema(description = "秒杀活动id")
+    private String secKillActivityId;
+    @Schema(description = "商品id")
+    private String goodsId;
+    @Schema(description = "商品规格id")
+    private String GoodsSpecId;
+    @Schema(description = "降价金额")
+    private BigDecimal cutAmount;
+    @Schema(description =  "true=生成二维码并返回二维码url  false=分享链接,并返回参数id")
+    private Boolean qrcode;
+}

+ 25 - 1
src/main/java/com/gree/mall/contest/bean/goods/GoodsBean.java

@@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 @EqualsAndHashCode(callSuper = true)
@@ -12,7 +13,7 @@ import java.util.List;
 public class GoodsBean extends Goods {
 
     @Schema(description = "商品规格")
-    private List<GoodsSpec> goodsSpecs;
+    private List<GoodsSpecSecBean> goodsSpecs;
 
     @Schema(description = "轮播图")
     private List<CommonFile> images;
@@ -41,4 +42,27 @@ public class GoodsBean extends Goods {
     @Schema(description = "套购商品配置的用户列表-详情用")
     private List<User> users;
 
+    @Schema(description = "佣金")
+    private BigDecimal shareAmount;
+
+    @Schema(description = "团长id")
+    private String groupUserId;
+
+    @Schema(description = "团长昵称")
+    private String groupUserName;
+
+    @Schema(description = "团长头像")
+    private String groupPic;
+
+    @Schema(description = "团购活动id")
+    private String promotionGroupId;
+
+    @Schema(description = "是否收藏标记")
+    private Boolean favorite;
+
+    @Schema(description = "商品模板")
+    private CommonTemplate commonTemplate;
+
+    @Schema(description = "公共商品模板")
+    private PubTemplate pubCommonTemplate;
 }

+ 21 - 0
src/main/java/com/gree/mall/contest/bean/goods/GoodsComment.java

@@ -0,0 +1,21 @@
+package com.gree.mall.contest.bean.goods;
+
+import com.gree.mall.contest.plus.entity.OrderComment;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class GoodsComment extends OrderComment {
+
+    @Schema(description = "用户名称")
+    private String userName;
+    @Schema(description = "用户头像")
+    private String avatar;
+    @Schema(description = "标签")
+    private List<String> tags;
+    @Schema(description = "图片")
+    private List<String> imgs;
+
+}

+ 15 - 0
src/main/java/com/gree/mall/contest/bean/goods/GoodsCommentCount.java

@@ -0,0 +1,15 @@
+package com.gree.mall.contest.bean.goods;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class GoodsCommentCount {
+    @Schema(description = "综合好评")
+    private Integer goodRate;
+    @Schema(description = "标签统计")
+    private List<CommentTagCount> tagCountList;
+
+}

+ 16 - 0
src/main/java/com/gree/mall/contest/bean/goods/GoodsFavoriteBean.java

@@ -0,0 +1,16 @@
+package com.gree.mall.contest.bean.goods;
+
+import com.gree.mall.contest.plus.entity.Goods;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class GoodsFavoriteBean extends Goods {
+
+    @Schema(description = "收藏id")
+    private String goodsFavoriteId;
+
+
+}

+ 4 - 3
src/main/java/com/gree/mall/contest/bean/goods/GoodsPackageBean.java

@@ -1,12 +1,15 @@
 package com.gree.mall.contest.bean.goods;
 
+import com.gree.mall.contest.plus.entity.GoodsPackagePop;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
 import java.util.List;
 
+@EqualsAndHashCode(callSuper = true)
 @Data
-public class GoodsPackageBean {
+public class GoodsPackageBean extends GoodsPackagePop {
 
     @Schema(description = "商品名称")
     private String goodsName;
@@ -14,7 +17,5 @@ public class GoodsPackageBean {
     private String imgUrl;
     @Schema(description = "规格")
     private List<GoodsPackagePopBean> goodsPackagePopList;
-    @Schema(description = "排序")
-    private Integer sortNum;
 
 }

+ 40 - 0
src/main/java/com/gree/mall/contest/bean/goods/GoodsSpecSecBean.java

@@ -0,0 +1,40 @@
+package com.gree.mall.contest.bean.goods;
+
+import com.gree.mall.contest.plus.entity.GoodsSpec;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class GoodsSpecSecBean extends GoodsSpec {
+    @Schema(description = "秒杀id")
+    private String secKillId;
+
+    @Schema(description = "秒杀商品规格id")
+    private String secKillSpecId;
+
+    @Schema(description = "秒杀商品库存")
+    private Integer secStockNum;
+
+    @Schema(description = "秒杀商品限制购买数量")
+    private Integer limitBuy;
+
+    @Schema(description = "场次结束时间")
+    private Integer endHour;
+
+    @Schema(description = "秒杀分销金额")
+    private BigDecimal secShareAmount;
+
+    @Schema(description = "秒杀价格")
+    private BigDecimal secPrice;
+
+    @Schema(description = "是否正在秒杀商品,true:秒杀中,false:非秒杀")
+    private Boolean secType;
+
+    @Schema(description = "商品划线价")
+    private BigDecimal orgGoodsPrice;
+
+}

+ 28 - 0
src/main/java/com/gree/mall/contest/bean/goods/PromotionGoodsBean.java

@@ -0,0 +1,28 @@
+package com.gree.mall.contest.bean.goods;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class PromotionGoodsBean {
+
+    @Schema(description = "团购活动id")
+    private String promotionGroupId;
+    @Schema(description = "商品id")
+    private String goodsId;
+    @Schema(description = "商品名称")
+    private String goodsName;
+    @Schema(description = "商品图")
+    private String goodsImgSrc;
+    @Schema(description = "拼团价")
+    private BigDecimal groupPrice;
+    @Schema(description = "销售数量")
+    private Integer saleNum;
+    @Schema(description = "库存")
+    private Integer stock;
+    @Schema(description = "划线价")
+    private BigDecimal orgGoodsPrice;
+
+}

+ 16 - 0
src/main/java/com/gree/mall/contest/bean/goods/PromotionShareQrCode.java

@@ -0,0 +1,16 @@
+package com.gree.mall.contest.bean.goods;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class PromotionShareQrCode {
+
+    @Schema(description = "活动背景图")
+    private String promotionImgUrl;
+    @Schema(description = "海报图片")
+    private String posterImgUrl;
+    @Schema(description = "小程序二维码")
+    private String qrcode;
+
+}

+ 20 - 0
src/main/java/com/gree/mall/contest/bean/merchant/WebsitApplyAdd.java

@@ -0,0 +1,20 @@
+package com.gree.mall.contest.bean.merchant;
+
+import com.gree.mall.contest.plus.entity.WebsitApply;
+import com.gree.mall.contest.plus.entity.WebsitApplyArea;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class WebsitApplyAdd extends WebsitApply {
+
+    @Schema(description = "大类id")
+    private List<String> categoryIds;
+
+    @Schema(description = "区域list")
+    private List<WebsitApplyArea> websitApplyAreaList;
+}

+ 22 - 0
src/main/java/com/gree/mall/contest/bean/merchant/WebsitApplyDetail.java

@@ -0,0 +1,22 @@
+package com.gree.mall.contest.bean.merchant;
+
+
+import com.gree.mall.contest.plus.entity.WebsitApply;
+import com.gree.mall.contest.plus.entity.WebsitApplyArea;
+import com.gree.mall.contest.plus.entity.WebsitApplyLog;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class WebsitApplyDetail extends WebsitApply {
+
+    @Schema(description = "区域")
+    private List<WebsitApplyArea> websitApplyAreaList;
+
+    @Schema(description = "跟进日志")
+    private List<WebsitApplyLog> websitApplyLogList;
+}

+ 47 - 0
src/main/java/com/gree/mall/contest/bean/merchant/WebsitApplyVO.java

@@ -0,0 +1,47 @@
+package com.gree.mall.contest.bean.merchant;
+
+import com.gree.mall.contest.annotation.ZfireField;
+import com.gree.mall.contest.enums.merchant.WebsitApplyStatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class WebsitApplyVO {
+
+    @ZfireField(hide = true)
+    @Schema(description = "服务商申请id")
+    private String websitApplyId;
+
+    @Schema(description = "意向服务类目")
+    private String categoryName;
+    @ZfireField(hide = true)
+    @Schema(description = "服务类目id")
+    private String categoryId;
+
+    @Schema(description = "公司名称")
+    private String companyName;
+
+    @Schema(description = "联系人")
+    private String linkName;
+
+    @Schema(description = "状态")
+    private WebsitApplyStatusEnum status;
+
+    @Schema(description = "联系电话")
+    private String mobile;
+
+    @Schema(description = "公司地址")
+    private String address;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "更新人")
+    private String updateBy;
+
+    @Schema(description = "更新时间")
+    private Date updateTime;
+
+}

+ 35 - 0
src/main/java/com/gree/mall/contest/bean/order/OrderAckBean.java

@@ -0,0 +1,35 @@
+package com.gree.mall.contest.bean.order;
+
+import com.gree.mall.contest.bean.goods.AckGoodsBean;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class OrderAckBean {
+
+    @Schema(description = "商品总金额")
+    private BigDecimal totalAmount;
+    @Schema(description = "商品详情")
+    private List<AckGoodsBean> goods;
+    @Schema(description = "商品总数量")
+    private Integer totalNum;
+    @Schema(description = "openid")
+    private String openId;
+    @Schema(description = "是否为秒杀活动")
+    private Boolean isSecKill = false;
+    @Schema(description = "运费")
+    private BigDecimal freight;
+    @Schema(description = "优惠券或者优惠码抵消金额")
+    private BigDecimal exchangeAmount;
+    @Schema(description = "折扣抵消金额")
+    private BigDecimal discountAmount;
+    @Schema(description = "订单实际支付金额 总金额 - 优惠券/优惠码金额 - 折扣扣减金额")
+    private BigDecimal payAmount;
+    @Schema(description = "折扣活动的折扣")
+    private BigDecimal promotionDiscountRate;
+    @Schema(description = "云闪付是否记录下单人信息")
+    private Boolean isRecordBuyer;
+}

+ 48 - 0
src/main/java/com/gree/mall/contest/bean/order/OrderBuyBean.java

@@ -0,0 +1,48 @@
+package com.gree.mall.contest.bean.order;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class OrderBuyBean {
+    @Schema(description = "用户id")
+    private String userId;
+    @Schema(description = "我的优惠券id")
+    private String userCouponId;
+    @Schema(description = "收货地址id")
+    private String userAddressId;
+    @Schema(description = "是否代客下单")
+    private Boolean proxyUser = false;
+    @Schema(description = "买家留言")
+    private String buyerMsg;
+    @Schema(description = "兑换码")
+    private String exchangeCode;
+
+    @Schema(description = "购物车")
+    public List<BuyGood> buyGoods;
+
+    @Schema(description = "申请优惠的订单号")
+    private String orderId;
+    @Schema(description = "申请优惠价格")
+    private BigDecimal applyAmount;
+    @Schema(description = "申请优惠备注")
+    private String applyRemark;
+
+    @Schema(description = "套购商品id(如果是套购商品必传这个)")
+    private String goodsPackageId;
+
+    @Schema(description = "工单派单方式 自卖自装/当地安装")
+    private String dispatchType;
+
+    @Schema(description = "wechat pay")
+    private Boolean wechatPay = true;
+
+    @Schema(description = "购买人姓名")
+    private String buyerName;
+
+    @Schema(description = "购买人身份证")
+    private String buyerIdCard;
+}

+ 19 - 0
src/main/java/com/gree/mall/contest/bean/order/OrderCommentBean.java

@@ -0,0 +1,19 @@
+package com.gree.mall.contest.bean.order;
+
+import com.gree.mall.contest.plus.entity.OrderComment;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class OrderCommentBean extends OrderComment {
+
+    @Schema(description = "标签集合")
+    private List<String> tags;
+    @Schema(description = "附件集合")
+    private List<String> fileIds;
+
+}

+ 4 - 1
src/main/java/com/gree/mall/contest/bean/order/OrderDetailBean.java

@@ -37,5 +37,8 @@ public class OrderDetailBean extends OrderInfo {
     @Schema(description = "团长手机号")
     private String promotionGroupMobile;
 
-
+    @Schema(description = "小程序订单详情")
+    private List<OrderDetailGoodsBean> orderDetailList;
+    @Schema(description = "结算状态")
+    private String orderShareStatus;
 }

+ 15 - 0
src/main/java/com/gree/mall/contest/bean/order/OrderDetailGoodsBean.java

@@ -0,0 +1,15 @@
+package com.gree.mall.contest.bean.order;
+
+import com.gree.mall.contest.plus.entity.Goods;
+import com.gree.mall.contest.plus.entity.OrderDetail;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class OrderDetailGoodsBean extends OrderDetail {
+
+    @Schema(description = "商品")
+    private Goods goods;
+}

+ 17 - 0
src/main/java/com/gree/mall/contest/bean/order/PromotionDiscountBean.java

@@ -0,0 +1,17 @@
+package com.gree.mall.contest.bean.order;
+
+import com.gree.mall.contest.plus.entity.PromotionDiscount;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class PromotionDiscountBean extends PromotionDiscount {
+
+    @Schema(description = "折扣优惠掉的金额")
+    private BigDecimal subDiscountAmount;
+
+}

+ 14 - 0
src/main/java/com/gree/mall/contest/bean/order/RefundGoods.java

@@ -0,0 +1,14 @@
+package com.gree.mall.contest.bean.order;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class RefundGoods {
+
+    @Schema(description = "明细id")
+    private String orderDetailId;
+    @Schema(description = "数量")
+    private Integer num;
+
+}

+ 27 - 0
src/main/java/com/gree/mall/contest/bean/order/RefundGoodsBean.java

@@ -0,0 +1,27 @@
+package com.gree.mall.contest.bean.order;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class RefundGoodsBean {
+
+    @Schema(description = "订单id")
+    private String orderId;
+    @Schema(description = "退款订单商品明细id")
+    private String orderDetailId;
+    @Schema(description = "退款方式:REFUND_AMOUNT=仅退款  REFUND_GOODS=退货退款")
+    private String refundType;
+    @Schema(description = "退换货原因")
+    private String refundReason;
+    @Schema(description = "退换货说明")
+    private String refundExplain;
+    @Schema(description = "凭证ids")
+    private List<String> imgIds;
+    @Schema(description = "退款商品")
+    private List<RefundGoods> refundGoods;
+
+
+}

+ 23 - 0
src/main/java/com/gree/mall/contest/bean/pay/PayDetail.java

@@ -0,0 +1,23 @@
+package com.gree.mall.contest.bean.pay;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class PayDetail {
+    @Schema(description = "paySign")
+    private String paySign;
+    @Schema(description = "微信返回的预支付id")
+    private String payPackage;
+    @Schema(description = "paySign")
+    private String nonceStr;
+    @Schema(description = "当前的时间")
+    private String timeStamp;
+    @Schema(description = "id")
+    private String id;
+    @Schema(description = "是否需要微信支付 true/false")
+    private Boolean isPay = true;
+    @Schema(description = "扫码支付的url")
+    private String codeUrl;
+
+}

+ 42 - 0
src/main/java/com/gree/mall/contest/bean/promotion/PromotionLuckDrawExchangeDetail.java

@@ -0,0 +1,42 @@
+package com.gree.mall.contest.bean.promotion;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class PromotionLuckDrawExchangeDetail {
+
+    @Schema(description = "活动名称")
+    private String name;
+    @Schema(description = "奖品")
+    private String couponName;
+    @Schema(description = "活动开始时间")
+    private Date startTime;
+    @Schema(description = "活动结束时间")
+    private Date endTime;
+    @Schema(description = "使用开始时间")
+    private Date useStartTime;
+    @Schema(description = "使用结束时间")
+    private Date useEndTime;
+    @Schema(description = "使用条件")
+    private String useRemark;
+    @Schema(description = "活动说明")
+    private String remark;
+
+    @Schema(description = "是否兑换true/false")
+    private Boolean status;
+    @Schema(description = "是否使用true/false")
+    private Boolean use;
+    @Schema(description = "使用时间")
+    private Date useTime;
+    @Schema(description = "兑换时间")
+    private Date userExchangeTime;
+    @Schema(description = "满减券需满足金额")
+    private BigDecimal orderLimitAmount;
+    @Schema(description = "券类型 SATISFY=满减券 GOODS=商品券 DISCOUNT=折扣券")
+    private String couponType;
+
+}

+ 16 - 0
src/main/java/com/gree/mall/contest/bean/user/UserCouponCountBean.java

@@ -0,0 +1,16 @@
+package com.gree.mall.contest.bean.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class UserCouponCountBean {
+
+    @Schema(description = "未使用数量")
+    private Integer wsy = 0;
+    @Schema(description = "已使用数量")
+    private Integer ysy = 0;
+    @Schema(description = "已过期数量")
+    private Integer ygq = 0;
+
+}

+ 30 - 0
src/main/java/com/gree/mall/contest/commonmapper/CategoryMapper.java

@@ -0,0 +1,30 @@
+package com.gree.mall.contest.commonmapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.bean.goods.CategoryBean;
+import com.gree.mall.contest.bean.goods.GoodsNewBean;
+import com.gree.mall.contest.plus.entity.GoodsCategory;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface CategoryMapper {
+
+    @InterceptorIgnore(tenantLine = "true")
+    List<CategoryBean> queryCategory(@Param("companyWechatId") String companyWechatId);
+
+    @InterceptorIgnore(tenantLine = "true")
+    IPage<GoodsNewBean> listGoods(IPage page,
+                                  @Param("currentCompanyWechatId") String currentCompanyWechatId,
+                                  @Param("categoryId")String categoryId,
+                                  @Param("sort")Integer sort);
+
+    /**
+     * 查询商品分类
+     */
+    @InterceptorIgnore(tenantLine = "true")
+    GoodsCategory queryGoodsCategoryByGoodsId(@Param("goodsId") String goodsId);
+}

+ 10 - 0
src/main/java/com/gree/mall/contest/commonmapper/CommonMapper.java

@@ -8,6 +8,7 @@ import com.gree.mall.contest.bean.common.CarouselMapVO;
 import com.gree.mall.contest.bean.manage.NotifyRecordVO;
 import com.gree.mall.contest.bean.manage.NotifyVO;
 import com.gree.mall.contest.bean.manage.UserTopPopVO;
+import com.gree.mall.contest.bean.merchant.WebsitApplyVO;
 import com.gree.mall.contest.bean.zfire.ZfireParamBean;
 import com.gree.mall.contest.plus.entity.UserTopPop;
 import org.apache.ibatis.annotations.Mapper;
@@ -59,4 +60,13 @@ public interface CommonMapper {
     IPage<UserTopPopVO> topPopList(Page page, @Param("ex") ZfireParamBean zfireParamBean);
 
     List<UserTopPop> queryTopPopVaildRecord(@Param("curDate") DateTime curDate, @Param("popIds") List<String> popIds);
+
+    /**
+     * 申请列表
+     *
+     * @param page
+     * @param zfireParamBean
+     * @return
+     */
+    IPage<WebsitApplyVO> pageApply(Page page, @Param("ex") ZfireParamBean zfireParamBean);
 }

+ 24 - 0
src/main/java/com/gree/mall/contest/commonmapper/CustomCoupouMapper.java

@@ -0,0 +1,24 @@
+package com.gree.mall.contest.commonmapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.gree.mall.contest.bean.coupon.CustomCoupouBean;
+import com.gree.mall.contest.bean.coupon.CouponObtainBean;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Mapper
+public interface CustomCoupouMapper {
+
+    @InterceptorIgnore(tenantLine = "true")
+    List<CustomCoupouBean> listCoupou(@Param("userId") String userId,
+                                      @Param("orderAmount") BigDecimal orderAmount,
+                                      @Param("goodsIds")List<String> goodsIds);
+
+    @InterceptorIgnore(tenantLine = "true")
+    List<CouponObtainBean> listObtainCoupou(@Param("userId") String userId,
+                                            @Param("companyWechatId") String companyWechatId);
+
+}

+ 9 - 2
src/main/java/com/gree/mall/contest/commonmapper/CustomGoodsMapper.java

@@ -8,6 +8,7 @@ import com.gree.mall.contest.bean.goods.GoodsSpecBean;
 import com.gree.mall.contest.bean.goods.GoodsTypeCount;
 import com.gree.mall.contest.bean.goods.GoodsVO;
 import com.gree.mall.contest.bean.zfire.ZfireParamBean;
+import com.gree.mall.contest.bean.goods.GoodsFavoriteBean;
 import com.gree.mall.contest.plus.entity.Goods;
 import com.gree.mall.contest.plus.entity.GoodsCategory;
 import org.apache.ibatis.annotations.Param;
@@ -45,7 +46,7 @@ public interface CustomGoodsMapper {
                                   @Param("categoryId") List<String> categoryId,
                                   @Param("status") Boolean status,
                                   @Param("goodsTypes") List<String> goodsTypes,
-                                  @Param("companyWechatIds")List<String> companyWechatIds);
+                                  @Param("companyWechatIds") List<String> companyWechatIds);
 
     @InterceptorIgnore(tenantLine = "true")
     List<GoodsSpecBean> list(@Param("commonTemplateId") String commonTemplateId);
@@ -102,7 +103,13 @@ public interface CustomGoodsMapper {
                                                   @Param("sortStr") String sortStr,
                                                   @Param("categoryId") List<String> categoryId,
                                                   @Param("status") Boolean status,
-                                                  @Param("companyWechatIds")List<String> companyWechatIds);
+                                                  @Param("companyWechatIds") List<String> companyWechatIds);
+
+    @InterceptorIgnore(tenantLine = "true")
+    IPage<GoodsFavoriteBean> queryGoodsFavorite(Page page, @Param("userId") String userId);
+
+    @InterceptorIgnore(tenantLine = "true")
+    Integer queryGoodsFavoriteCount(@Param("userId") String userId);
 
     IPage<GoodsVO> goodsList(Page page, @Param("ex") ZfireParamBean zfireParam);
 }

+ 65 - 0
src/main/java/com/gree/mall/contest/commonmapper/GoodsSpecDetailMapper.java

@@ -0,0 +1,65 @@
+package com.gree.mall.contest.commonmapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.bean.goods.*;
+import com.gree.mall.contest.plus.entity.CommonTemplate;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface GoodsSpecDetailMapper {
+
+    @InterceptorIgnore(tenantLine = "true")
+    List<GoodsSpecSecBean> querySpecSec(@Param("goodsId") String goodsId);
+    @InterceptorIgnore(tenantLine = "true")
+    CommonTemplate queryCommonTemplate(@Param("goodsId") String goodsId);
+    /**
+     * 团购商品列表
+     */
+    @InterceptorIgnore(tenantLine = "true")
+    IPage<PromotionGoodsBean> queryPromotionGoods(IPage page,
+                                                         @Param("userId") String userId,
+                                                         @Param("goodsCategoryId") String goodsCategoryId,
+                                                         @Param("keyword") String keyword,
+                                                         @Param("companyWechatId") String companyWechatId);
+
+    /**
+     * 商品评价列表
+     */
+    @InterceptorIgnore(tenantLine = "true")
+    IPage<GoodsComment> goodsCommentList(IPage page, @Param("companyWechatId")String companyWechatId,
+                                                @Param("goodsId") String goodsId, @Param("tag") String tag);
+
+    /**
+     * 商品评价汇总
+     */
+    @InterceptorIgnore(tenantLine = "true")
+    List<CommentTagCount> goodsCommentCount(@Param("companyWechatId")String companyWechatId,@Param("goodsId") String goodsId);
+
+
+    /**
+     * 套购商品列表
+     */
+    @InterceptorIgnore(tenantLine = "true")
+    List<GoodsPackageBean> queryGoodsPackage(@Param("userId") String userId,
+                                                    @Param("userType") String userType,
+                                                    @Param("goodsId") String goodsId,
+                                                    @Param("companyWechatId") String companyWechatId);
+
+
+    /**
+     * 商品列表
+     */
+    @InterceptorIgnore(tenantLine = "true")
+    IPage<GoodsNewBean> queryGoodsList(IPage page, @Param("userId") String userId,
+                                              @Param("userType") String userType,
+                                              @Param("keyword") String keyword,
+                                              @Param("categoryId") String categoryId,
+                                              @Param("sort") Integer sort,
+                                              @Param("companyWechatId") String companyWechatId);
+
+
+}

+ 14 - 0
src/main/java/com/gree/mall/contest/commonmapper/SoldNumMapper.java

@@ -0,0 +1,14 @@
+package com.gree.mall.contest.commonmapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.gree.mall.contest.plus.entity.OrderDetail;
+import org.apache.ibatis.annotations.Param;
+
+public interface SoldNumMapper {
+    @InterceptorIgnore(tenantLine = "true")
+    void updateGoods(@Param("orderDetail") OrderDetail orderDetail);
+
+    @InterceptorIgnore(tenantLine = "true")
+    void updateGoodsSpec(@Param("orderDetail") OrderDetail orderDetail);
+
+}

+ 5 - 0
src/main/java/com/gree/mall/contest/constant/Constant.java

@@ -57,6 +57,11 @@ public class Constant {
         public final static String LOCK_ORDER = "mall:contest:lock:order:";
         public final static String LOCK_AUTH = "mall:contest:lock:auth:";
         public final static String LOCK_ORDER_STOCK = "mall:contest:lock:stock";
+        public final static String LOCK_COUPON = "mall:contest:lock:coupon";
+        public final static String LOCK_COUPON_TRANSFER = "mall:contest:lock:coupon:transfer";
+        public final static String TOKEN_TAX = "mall:contest:token:tax";
+        public final static String LOCK_GIFT = "mall:contest:lock:gift";
+        public final static String LOCK_COUPON_CHECK = "mall:contest:lock:coupon:check";
 
         public final static String CLIENT = "mall:contest:client:";
     }

+ 181 - 0
src/main/java/com/gree/mall/contest/controller/mini/common/MiniCommonController.java

@@ -0,0 +1,181 @@
+package com.gree.mall.contest.controller.mini.common;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.annotation.ApiNotAuth;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.CarouselMapLogic;
+import com.gree.mall.contest.logic.ExpressLogic;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.logic.common.WechatLogic;
+import com.gree.mall.contest.plus.entity.*;
+import com.gree.mall.contest.plus.service.AdminCompanyWechatOtherService;
+import com.gree.mall.contest.plus.service.OrderDetailService;
+import com.gree.mall.contest.plus.service.OrderInfoService;
+import com.gree.mall.contest.plus.service.OrderTaxService;
+import com.gree.mall.contest.utils.RedisUtil;
+import com.gree.mall.contest.utils.VerifiUtils;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+
+@Slf4j
+@RestController
+@Tag(name = "公共API", description = "公共API")
+@RequestMapping(value = "/mini/common", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class MiniCommonController {
+
+    private final CommonLogic commonLogic;
+    private final ExpressLogic expressLogic;
+    private final OrderTaxService orderTaxService;
+    private final RedisUtil redisUtil;
+    private final CarouselMapLogic carouselMapLogic;
+    private final WechatLogic wechatLogic;
+    private final OrderInfoService orderInfoService;
+    private final OrderDetailService orderDetailService;
+    private final AdminCompanyWechatOtherService adminCompanyWechatOtherService;
+
+    @PostMapping("/upload")
+    @Operation(summary = "文件上传")
+    public ResponseHelper<CommonFile> upload(
+            @Parameter(description = "附件", required = true) @RequestParam(required = true) MultipartFile file
+    ) throws Exception {
+        CommonFile commonFile = commonLogic.uploadFile(file);
+        return ResponseHelper.success(commonFile);
+    }
+
+    @GetMapping("/express/company")
+    @Operation(summary = "所有物流公司")
+    public ResponseHelper<List<ExpressCompany>> companyList(
+    ) throws Exception {
+        List<ExpressCompany> expressCompanies = expressLogic.expressCompanyList();
+        return ResponseHelper.success(expressCompanies);
+    }
+
+    @GetMapping("/express")
+    @Operation(summary = "查看物流")
+    public ResponseHelper<List<ExpressInfo>> express(
+            @Parameter(description = "附件", required = true) @RequestParam(required = true) String logisticsNo,
+            @Parameter(description = "物流公司code", required = true) @RequestParam(required = true) String companyCode
+    ) throws Exception {
+        List<ExpressInfo> expressInfos = expressLogic.queryExpress(logisticsNo, companyCode);
+        return ResponseHelper.success(expressInfos);
+    }
+
+
+    @GetMapping("/street")
+    @Operation(summary = "街道")
+    public ResponseHelper<List<Map>> street(
+            @Parameter(description = "省", required = true) @RequestParam(required = true) String province,
+            @Parameter(description = "市", required = true) @RequestParam(required = true) String city,
+            @Parameter(description = "区", required = true) @RequestParam(required = true) String area
+    ) throws RemoteServiceException {
+        List<Map> street = commonLogic.street(province, city, area);
+        return ResponseHelper.success(street);
+    }
+
+    @GetMapping("/list/page")
+    @Operation(summary = "轮播图列表")
+    public ResponseHelper<Page<CarouselMap>> page(
+            HttpServletRequest request,
+            @Parameter(description = "状态(true:启用 false:禁用", required = false) @RequestParam(required = false) Boolean state,
+            @Parameter(description = "页号", required = true) @RequestParam(required = true) Integer pageNum,
+            @Parameter(description = "页大小", required = true) @RequestParam(required = true) Integer pageSize
+    ) throws RemoteServiceException {
+        IPage<CarouselMap> page = carouselMapLogic.page(request, state, pageNum, pageSize);
+        return ResponseHelper.success(page);
+    }
+
+    @GetMapping("/wechat/detail")
+    @Operation(summary = "微信配置")
+    public ResponseHelper<CurrentCompanyWechat> detail(
+            HttpServletRequest request
+    ) throws RemoteServiceException {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        //设置其他配置
+        AdminCompanyWechatOther one = adminCompanyWechatOtherService.lambdaQuery()
+                .eq(AdminCompanyWechatOther::getCompanyWechatId, currentCompanyWechat.getCompanyWechatId())
+                .one();
+        currentCompanyWechat.setAdminCompanyWechatOther(one);
+        return ResponseHelper.success(currentCompanyWechat);
+    }
+
+
+    @GetMapping("/scene")
+    @Operation(summary = "获取微信scene参数")
+    public ResponseHelper<String> page(
+            @Parameter(description = "微信scene", required = true) @RequestParam(required = true) String scene
+    ) throws RemoteServiceException {
+        String sceneValue = commonLogic.getSceneValue(scene);
+        return ResponseHelper.success(sceneValue);
+    }
+
+    @GetMapping("/getVerifi")
+    @Operation(summary = "获取拖拽式验证码")
+    public ResponseHelper<Map<String, Object>> getVerifi() {
+        Map<String, Object> map = VerifiUtils.getVerifi();
+        Integer xWidth = (Integer) map.get("xWidth");
+        String key = UUID.randomUUID().toString();
+        redisUtil.set(Constant.RedisPrefix.VERIFICATION + ":" + key, xWidth, 2 * 60);
+        map.remove("xWidth");
+        map.put("key", key);
+        return ResponseHelper.success(map);
+    }
+
+    @GetMapping("/config/get")
+    @Operation(summary = "获取小程序配置")
+    public ResponseHelper<AdminCompanyWechat> getConfig(HttpServletRequest request) {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        //剔除不返回的内容
+        currentCompanyWechat.setPubAppId(null);
+        currentCompanyWechat.setSubAppId(null);
+        currentCompanyWechat.setMchKey(null);
+        currentCompanyWechat.setMchId(null);
+        return ResponseHelper.success(currentCompanyWechat);
+    }
+
+
+    @ApiNotAuth
+    @GetMapping("/getfile")
+    public ResponseHelper<String> getfile(@RequestParam(required = true) String id) {
+        OrderTax orderTax = orderTaxService.getById(id);
+        String taxLink = orderTax.getTaxLink();
+        return ResponseHelper.success(taxLink);
+    }
+
+    @ApiNotAuth
+    @GetMapping("/oss/config")
+    public ResponseHelper getOSSConfig() throws UnsupportedEncodingException {
+        Map<String, String> ossConfig = commonLogic.getOSSConfig();
+        return ResponseHelper.success(ossConfig);
+    }
+
+    @ApiNotAuth
+    @GetMapping("/img/get")
+    @Operation(summary = "获取图片")
+    public void getImg(
+            @Parameter(description = "附件key", required = true) @RequestParam String key,
+            HttpServletResponse response
+    ) throws IOException, RemoteServiceException {
+        String file = commonLogic.getFile(key);
+        response.sendRedirect(file);
+    }
+
+}

+ 83 - 0
src/main/java/com/gree/mall/contest/controller/mini/common/PayController.java

@@ -0,0 +1,83 @@
+package com.gree.mall.contest.controller.mini.common;
+
+import com.gree.mall.contest.annotation.ApiNotAuth;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.logic.common.WechatLogic;
+import com.gree.mall.contest.logic.pay.PayLogic;
+import com.gree.mall.contest.utils.WeChatUtils;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.integration.redis.util.RedisLockRegistry;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
+@Slf4j
+@RestController
+@RequestMapping(value = "/pay", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class PayController {
+
+    private final PayLogic payLogic;
+    private final WechatLogic wechatLogic;
+    private final RedisLockRegistry redisLockRegistry;
+    static ExecutorService threadPool = Executors.newFixedThreadPool(4);
+
+
+    @ApiNotAuth
+    @PostMapping("/payCall")
+    @Operation(summary = "支付回调")
+    public String payCall(@RequestBody String body) throws Exception {
+        Map<String, String> params = wechatLogic.doXMLParse(body);
+        String id = params.get("out_trade_no");//订单号
+        String transactionId = params.get("transaction_id");//微信支付单号
+        BigDecimal totalFee = new BigDecimal(params.get("total_fee")).divide(BigDecimal.valueOf(100));//
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_ORDER + id);
+
+        threadPool.submit(() -> {
+            try {
+                log.info("==========支付回调开始:{}", body);
+                if (!obtain.tryLock(10, TimeUnit.SECONDS)) {
+                    log.error("【支付回调】,请勿重复请求,orderId:{}", id);
+                } else {
+                    payLogic.payCall(id, transactionId, totalFee);
+                }
+            } catch (Exception e) {
+                log.error("处理支付回调失败", e);
+            } finally {
+                if (obtain != null) {
+                    obtain.unlock();
+                }
+            }
+        });
+
+        SortedMap<Object, Object> returnData = new TreeMap<>();
+        returnData.put("return_code", "SUCCESS");
+        returnData.put("return_msg", "OK");
+        return WeChatUtils.getRequestXml(returnData);
+    }
+
+
+    @ApiNotAuth
+    @PostMapping("/refundCall")
+    @Operation(summary = "退款回调")
+    public String refundCall(@RequestBody String body) throws Exception {
+        SortedMap<Object, Object> returnData = new TreeMap<>();
+        returnData.put("return_code", "SUCCESS");
+        returnData.put("return_msg", "OK");
+        return WeChatUtils.getRequestXml(returnData);
+    }
+
+
+}

+ 155 - 0
src/main/java/com/gree/mall/contest/controller/mini/coupon/CouponController.java

@@ -0,0 +1,155 @@
+package com.gree.mall.contest.controller.mini.coupon;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.coupon.CouponBean;
+import com.gree.mall.contest.bean.coupon.CouponObtainBean;
+import com.gree.mall.contest.bean.coupon.CouponQrcodeBean;
+import com.gree.mall.contest.bean.coupon.CustomCoupouBean;
+import com.gree.mall.contest.bean.user.UserCouponCountBean;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.coupon.CouponLogic;
+import com.gree.mall.contest.plus.entity.UserCoupon;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.integration.redis.util.RedisLockRegistry;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
+@Slf4j
+@RestController
+@Tag(name = "优惠券管理", description = "优惠券管理")
+@RequestMapping(value = "/mini/coupon", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class CouponController {
+
+    private final CouponLogic couponLogic;
+    private final RedisLockRegistry redisLockRegistry;
+
+
+    @GetMapping("/timeout")
+    @Operation(summary = "优惠券过期提醒")
+    public ResponseHelper<Boolean> timeout(
+            @Parameter(description = "用户id") @RequestParam(required = false) String userId
+    ) {
+        Boolean timeout = couponLogic.timeout(userId);
+        return ResponseHelper.success(timeout);
+    }
+
+
+    @GetMapping("/count")
+    @Operation(summary = "优惠券统计")
+    public ResponseHelper<UserCouponCountBean> count(
+            @Parameter(description = "用户id") @RequestParam(required = false) String userId
+    ) {
+        UserCouponCountBean count = couponLogic.count(userId);
+        return ResponseHelper.success(count);
+    }
+
+    @GetMapping("/list/page")
+    @Operation(summary = "优惠券列表")
+    public ResponseHelper<Page<UserCoupon>> page(
+            @Parameter(description = "用户id") @RequestParam(required = false) String userId,
+            @Parameter(description = "0:未使用  1:已使用 2:已过期") @RequestParam(required = false) Integer type,
+            @Parameter(description = "页号", required = true) @RequestParam Integer pageNum,
+            @Parameter(description = "页大小", required = true) @RequestParam Integer pageSize
+
+    ) throws RemoteServiceException {
+        IPage<UserCoupon> page = couponLogic.miniPage(userId, type, pageNum, pageSize);
+        return ResponseHelper.success(page);
+    }
+
+    @GetMapping("/list/expiring")
+    @Operation(summary = "快要到期优惠券列表")
+    public ResponseHelper<List<UserCoupon>> expiring(
+            @Parameter(description = "用户id") @RequestParam(required = false) String userId
+    ) throws RemoteServiceException {
+        List<UserCoupon> coupons = couponLogic.expiring(userId);
+        return ResponseHelper.success(coupons);
+    }
+
+    @GetMapping("/list/all")
+    @Operation(summary = "可领/已领/待领优惠券列表")
+    public ResponseHelper<List<CouponObtainBean>> listAll(
+            @Parameter(description = "用户id") @RequestParam(required = false) String userId,
+            HttpServletRequest request
+    ) throws RemoteServiceException {
+        List<CouponObtainBean> coupons = couponLogic.allObtainCoupou(userId,request);
+        return ResponseHelper.success(coupons);
+    }
+
+
+    @GetMapping("/obtain")
+    @Operation(summary = "领券")
+    public ResponseHelper obtain(HttpServletRequest request,
+            @Parameter(description = "用户id") @RequestParam(required = false) String userId,
+            @Parameter(description = "优惠券id") @RequestParam(required = false) List<String> couponIds
+
+    ) throws RemoteServiceException, InterruptedException {
+        for(String couponId : couponIds) {
+            Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_COUPON + couponId);
+            if(!obtain.tryLock(10,TimeUnit.SECONDS)){
+                log.error("系统繁忙,领券失败,userId:{},couponId:{}",userId,couponId);
+                continue;
+            }
+            try {
+                couponLogic.obtainCoupou(request, userId, couponId);
+            }finally {
+                obtain.unlock();
+            }
+        }
+        return ResponseHelper.success();
+    }
+
+
+    @GetMapping("/list/useable")
+    @Operation(summary = "可用优惠券列表")
+    public ResponseHelper<List<CustomCoupouBean>> useableCoupou(
+            @Parameter(description = "用户id", required = true) @RequestParam(required = true) String userId,
+            @Parameter(description = "订单金额", required = true) @RequestParam(required = true) BigDecimal orderAmount,
+            @Parameter(description = "商品ids", required = true) @RequestParam(required = true) List<String> goodsSpecIds
+    ) throws RemoteServiceException {
+        return ResponseHelper.success(couponLogic.useableCoupou(userId, orderAmount, goodsSpecIds));
+    }
+
+
+    @GetMapping("/detail")
+    @Operation(summary = "券详情")
+    public ResponseHelper<CouponBean> detail(
+            @Parameter(description = "id", required = true) @RequestParam String couponId
+    ) throws RemoteServiceException {
+        UserCoupon detail = couponLogic.miniDetail(couponId);
+        return ResponseHelper.success(detail);
+    }
+
+    @GetMapping("/transfer/qrcode")
+    @Operation(summary = "获取转赠二维码")
+    public ResponseHelper<CouponQrcodeBean> qrcode(@Parameter(description = "id", required = true) @RequestParam String userCouponId)
+            throws RemoteServiceException {
+        return ResponseHelper.success(couponLogic.qrcode(userCouponId));
+    }
+
+
+    @GetMapping("/transfer/coupon")
+    @Operation(summary = "转赠优惠券")
+    public ResponseHelper<String> transferCoupon(HttpServletRequest request,@Parameter(description = "id", required = true) @RequestParam String userCouponId)
+            throws RemoteServiceException {
+
+        return ResponseHelper.success(couponLogic.transferCoupon(request,userCouponId));
+    }
+
+
+}

+ 170 - 0
src/main/java/com/gree/mall/contest/controller/mini/goods/GoodsController.java

@@ -0,0 +1,170 @@
+package com.gree.mall.contest.controller.mini.goods;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.bean.goods.*;
+import com.gree.mall.contest.bean.user.UserWxBean;
+import com.gree.mall.contest.controller.pc.goods.CategoryLogic;
+import com.gree.mall.contest.enums.QrCodeEnum;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.common.WechatLogic;
+import com.gree.mall.contest.logic.goods.GoodsLogic;
+import com.gree.mall.contest.logic.user.UserLogic;
+import com.gree.mall.contest.plus.entity.Goods;
+import com.gree.mall.contest.plus.entity.GoodsCategory;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.List;
+
+@Slf4j
+@RestController
+@Tag(name = "商品列表", description = "商品列表")
+@RequestMapping(value = "/mini/goods", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class GoodsController {
+    private final GoodsLogic goodsLogic;
+    private final UserLogic userLogic;
+    private final CategoryLogic categoryLogic;
+    private final WechatLogic wechatLogic;
+
+
+    @GetMapping("/category/list/all")
+    @Operation(summary = "分类列表(树形)")
+    public ResponseHelper<List<CategoryBean>> category(HttpServletRequest request) throws Exception {
+        List<CategoryBean> categoryBeans = categoryLogic.queryAll(request);
+        return ResponseHelper.success(categoryBeans);
+    }
+
+    @GetMapping("/category/list/first")
+    @Operation(summary = "获取一级分类列表")
+    public ResponseHelper<List<GoodsCategory>> categoryFirst(HttpServletRequest request) throws Exception {
+        List<GoodsCategory> categoryBeans = categoryLogic.queryFirst(request);
+        return ResponseHelper.success(categoryBeans);
+    }
+
+    @GetMapping("/category/list/second")
+    @Operation(summary = "获取二级分类列表")
+    public ResponseHelper<List<GoodsCategory>> categorySecond(
+            HttpServletRequest request,
+            @Parameter(description = "一级分类id", required = true) @RequestParam(required = true) String categoryId
+    ) throws Exception {
+        List<GoodsCategory> categoryBeans = categoryLogic.querySecond(request, categoryId);
+        return ResponseHelper.success(categoryBeans);
+    }
+
+
+    @GetMapping("/category/list")
+    @Operation(summary = "分类列表")
+    public ResponseHelper<List<GoodsCategory>> category(
+            HttpServletRequest request,
+            @Parameter(description = "分类id", required = false) @RequestParam(required = false) String parentId,
+            @Parameter(description = "分类名称", required = false) @RequestParam(required = false) String name
+    ) throws Exception {
+        List<GoodsCategory> list = categoryLogic.list(request, parentId, name);
+        return ResponseHelper.success(list);
+    }
+
+    @GetMapping("/list/level1")
+    @Operation(summary = "根据一级分类查询商品列表")
+    public ResponseHelper<IPage<Goods>> listByLevel1(
+            HttpServletRequest request,
+            @Parameter(description = "一级分类id", required = true) @RequestParam(required = true) String categoryId,
+            @Parameter(description = "页号", required = true) @RequestParam Integer pageNum,
+            @Parameter(description = "页大小", required = true) @RequestParam Integer pageSize
+    ) throws Exception {
+        IPage<Goods> categoryGoodBeans = categoryLogic.categoryGoodsList(request, categoryId, pageNum, pageSize);
+        return ResponseHelper.success(categoryGoodBeans);
+    }
+
+
+    @GetMapping("/list/sort/page")
+    @Operation(summary = "商品列表")
+    public ResponseHelper<IPage<GoodsNewBean>> page(
+            HttpServletRequest request,
+            @Parameter(description = "商品关键字") @RequestParam(required = false) String keyword,
+            @Parameter(description = "排序 0=综合 1=销量 2=价格升 3=价格降 4=上新") @RequestParam(required = false, defaultValue = "0") Integer sort,
+            @Parameter(description = "分类id") @RequestParam(required = false) String categoryId,
+            @Parameter(description = "页号", required = true) @RequestParam Integer pageNum,
+            @Parameter(description = "页大小", required = true) @RequestParam Integer pageSize
+    ) throws Exception {
+        IPage<GoodsNewBean> page = goodsLogic.miniPage(request, keyword, categoryId, sort, pageNum, pageSize);
+        return ResponseHelper.success(page);
+    }
+
+    @GetMapping("/detail")
+    @Operation(summary = "商品详情")
+    public ResponseHelper<GoodsBean> detail(
+            HttpServletRequest request,
+            @Parameter(description = "id", required = true) @RequestParam String goodsId,
+            @Parameter(description = "userId", required = true) @RequestParam String userId,
+            @Parameter(description = "wxSceneId", required = false) @RequestParam(required = false) String wxSceneId
+    ) throws RemoteServiceException {
+        GoodsBean detail = goodsLogic.miniDetail(request, goodsId, userId, wxSceneId);
+        return ResponseHelper.success(detail);
+    }
+
+    @GetMapping("/qrcode")
+    @Operation(summary = "获取商品二维码")
+    public ResponseHelper<String> qrcode(
+            @Parameter(description = "用户id", required = true) @RequestParam String userId,
+            @Parameter(description = "商品id", required = true) @RequestParam String goodsId,
+            HttpServletRequest request
+    ) throws IOException, RemoteServiceException {
+        UserWxBean userWxBean = userLogic.userDetail(userId, request);
+        String qrcode = wechatLogic.getQrcode(QrCodeEnum.GOODS.toString().toLowerCase(), goodsId, userId, userWxBean.getCompanyWechatId());
+        return ResponseHelper.success(qrcode);
+    }
+
+    @GetMapping("/promotion/group/list")
+    @Operation(summary = "团购商品列表")
+    public ResponseHelper<IPage<PromotionGoodsBean>> grouplist(
+            @Parameter(description = "用户id", required = true) @RequestParam String userId,
+            @Parameter(description = "商品类型id", required = false) @RequestParam(required = false) String goodsCategoryId,
+            @Parameter(description = "搜索内容", required = false) @RequestParam(required = false) String keyword,
+            @Parameter(description = "页号", required = true) @RequestParam(required = true) Integer pageNo,
+            @Parameter(description = "页大小", required = true) @RequestParam(required = true) Integer pageSize,
+            HttpServletRequest request
+    ) throws IOException, RemoteServiceException {
+        IPage<PromotionGoodsBean> promotionGoodsBeanIPage = goodsLogic.myGroupGoods(userId, goodsCategoryId, keyword, pageNo, pageSize, request);
+        return ResponseHelper.success(promotionGoodsBeanIPage);
+    }
+
+    @GetMapping("/promotion/share/qrcode")
+    @Operation(summary = "团购活动分享二维码")
+    public ResponseHelper<PromotionShareQrCode> shareQrcode(
+            @Parameter(description = "用户id", required = true) @RequestParam String userId,
+            HttpServletRequest request
+    ) throws IOException {
+        PromotionShareQrCode promotionShareQrCode = goodsLogic.queryShareQrcode(userId, request);
+        return ResponseHelper.success(promotionShareQrCode);
+    }
+
+
+    @PostMapping("/cut")
+    @Operation(summary = "让利二维码或链接")
+    public ResponseHelper<String> cut(
+            @RequestBody List<CutGoodsBean> cutGoodsBeans,
+            HttpServletRequest request
+    ) throws IOException {
+        String cutQrcode = goodsLogic.getCutQrcode(cutGoodsBeans, request);
+        return ResponseHelper.success(cutQrcode);
+    }
+
+    @GetMapping("/package/choice")
+    @Operation(summary = "团购商品-选购列表")
+    public ResponseHelper<List<List<GoodsPackageBean>>> choicePackage(
+            @Parameter(description = "套购商品id", required = true) @RequestParam String goodsId,
+            HttpServletRequest request
+    ) throws IOException {
+        List<List<GoodsPackageBean>> lists = goodsLogic.choicePackage(goodsId, request);
+        return ResponseHelper.success(lists);
+    }
+
+}

+ 73 - 0
src/main/java/com/gree/mall/contest/controller/mini/goods/GoodsFavoriteController.java

@@ -0,0 +1,73 @@
+package com.gree.mall.contest.controller.mini.goods;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.goods.GoodsFavoriteBean;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.goods.GoodsFavoriteLogic;
+import com.gree.mall.contest.plus.entity.GoodsFavorite;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@RestController
+@Tag(name = "商品收藏", description = "商品收藏")
+@RequestMapping(value = "/mini/goods/favorite", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class GoodsFavoriteController {
+
+    private final GoodsFavoriteLogic goodsFavoriteLogic;
+
+    @PostMapping("/add")
+    @Operation(summary = "添加收藏")
+    public ResponseHelper<GoodsFavorite> add(
+            HttpServletRequest request,
+            @Parameter(description = "用户id", required = true) @RequestParam(required = true) String userId,
+            @Parameter(description = "商品id", required = true) @RequestParam(required = true) String goodsId,
+            @Parameter(description = "商品规格id", required = false) @RequestParam(required = false) String goodsSpecId
+    ) throws Exception {
+        GoodsFavorite goodsFavorite = goodsFavoriteLogic.saveGoodsFavorite(request, userId, goodsId, goodsSpecId);
+        return ResponseHelper.success(goodsFavorite);
+    }
+
+    @PostMapping("/del")
+    @Operation(summary = "删除收藏")
+    public ResponseHelper<GoodsFavorite> del(
+            @Parameter(description = "商品收藏id", required = true) @RequestParam(required = true) String goodFavoriteId
+
+    ) throws Exception {
+        goodsFavoriteLogic.delGoodsFavorite(goodFavoriteId);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/detail/del")
+    @Operation(summary = "从商品详情页面删除收藏")
+    public ResponseHelper<GoodsFavorite> detailDel(
+            @Parameter(description = "商品id", required = true) @RequestParam(required = true) String goodsId,
+            @Parameter(description = "用户id", required = true) @RequestParam(required = true) String userId,
+            @Parameter(description = "商品规格id", required = false) @RequestParam(required = false) String goodSpecId
+
+    ) throws Exception {
+        goodsFavoriteLogic.detailDelGoodsFavorite(goodsId, userId, goodSpecId);
+        return ResponseHelper.success();
+    }
+
+    @GetMapping("/query")
+    @Operation(summary = "获取商品收藏列表")
+    public ResponseHelper<Page<GoodsFavoriteBean>> queryList(
+            @Parameter(description = "用户id", required = true) @RequestParam(required = true) String userId,
+            @Parameter(description = "页号", required = true) @RequestParam Integer pageNum,
+            @Parameter(description = "页大小", required = true) @RequestParam Integer pageSize
+    ) throws Exception {
+        IPage<GoodsFavoriteBean> page = goodsFavoriteLogic.pageGoodsFavorite(userId, pageNum, pageSize);
+        return ResponseHelper.success(page);
+    }
+
+
+}

+ 74 - 0
src/main/java/com/gree/mall/contest/controller/mini/order/OrderCommentController.java

@@ -0,0 +1,74 @@
+package com.gree.mall.contest.controller.mini.order;
+
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.bean.goods.GoodsComment;
+import com.gree.mall.contest.bean.goods.GoodsCommentCount;
+import com.gree.mall.contest.bean.order.OrderCommentBean;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.order.OrderCommentLogic;
+import com.gree.mall.contest.plus.entity.CommentTag;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+
+@Slf4j
+@RestController
+@Tag(name = "订单评价", description = "订单评价")
+@RequestMapping(value = "/mini/order/comment", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class OrderCommentController {
+
+    private final OrderCommentLogic orderCommentLogic;
+
+    @PostMapping("/add")
+    @Operation(summary = "评价")
+    public ResponseHelper add(
+            HttpServletRequest request,
+            @RequestBody OrderCommentBean orderCommentBean
+    ) throws RemoteServiceException {
+        orderCommentLogic.add(request , orderCommentBean);
+        return ResponseHelper.success();
+    }
+
+    @GetMapping("/tag/list")
+    @Operation(summary = "评价标签列表")
+    public ResponseHelper<List<CommentTag>> tagList(HttpServletRequest request) throws RemoteServiceException {
+        List<CommentTag> list = orderCommentLogic.list(request);
+        return ResponseHelper.success(list);
+    }
+
+
+    @GetMapping("/goods")
+    @Operation(summary = "商品的评价列表")
+    public ResponseHelper<IPage<GoodsComment>> goods(
+            HttpServletRequest request,
+            @Parameter(description = "商品id",required = true) @RequestParam(required = true) String goodsId,
+            @Parameter(description = "标签名",required = false) @RequestParam(required = false) String tag,
+            @Parameter(description = "页号",required = true) @RequestParam(required = true) Integer pageNo,
+            @Parameter(description = "页大小",required = true) @RequestParam(required = true) Integer pageSize
+    ) throws RemoteServiceException {
+        IPage<GoodsComment> goodsCommentIPage = orderCommentLogic.goodsCommentList(request , goodsId, tag, pageNo, pageSize);
+        return ResponseHelper.success(goodsCommentIPage);
+    }
+
+    @GetMapping("/goods/count")
+    @Operation(summary = "商品的评价汇总")
+    public ResponseHelper<GoodsCommentCount> goodsCount(
+            HttpServletRequest request,
+            @Parameter(description = "商品id",required = true) @RequestParam(required = true) String goodsId
+    ) throws RemoteServiceException {
+        GoodsCommentCount goodsCommentCount = orderCommentLogic.goodsCommentCount(request, goodsId);
+        return ResponseHelper.success(goodsCommentCount);
+    }
+
+
+}

+ 268 - 0
src/main/java/com/gree/mall/contest/controller/mini/order/OrderController.java

@@ -0,0 +1,268 @@
+package com.gree.mall.contest.controller.mini.order;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.bean.order.*;
+import com.gree.mall.contest.bean.pay.PayDetail;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.enums.QrCodeEnum;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.FreightLogic;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.logic.common.WechatLogic;
+import com.gree.mall.contest.logic.order.OrderLogic;
+import com.gree.mall.contest.logic.pay.PayLogic;
+import com.gree.mall.contest.utils.IpUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.integration.redis.util.RedisLockRegistry;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
+@Slf4j
+@RestController
+@Tag(name = "订单", description = "订单")
+@RequestMapping(value = "/mini/order", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class OrderController {
+
+    private final OrderLogic orderLogic;
+    private final FreightLogic freightLogic;
+    private final WechatLogic wechatLogic;
+    private final PayLogic payLogic;
+    private final CommonLogic commonLogic;
+    private final RedisLockRegistry redisLockRegistry;
+
+
+    @PostMapping("/ackdata")
+    @Operation(summary = "确认订单内容")
+    public ResponseHelper<OrderAckBean> ackData(
+            @RequestBody OrderBuyBean orderBuyBean,
+            HttpServletRequest request
+    ) throws Exception {
+        OrderAckBean orderAckBean = orderLogic.ackOrder(orderBuyBean,request);
+        try {
+            //运费
+            if(orderAckBean.getFreight().doubleValue() < 0){
+                return ResponseHelper.success(1100,orderAckBean,"暂不支持配送该地区");
+            }
+        }catch(Exception e){
+            return ResponseHelper.success(1100,orderAckBean,e.getMessage());
+        }
+        return ResponseHelper.success(orderAckBean);
+    }
+
+
+    @PostMapping("/freight/v2")
+    @Operation(summary = "获取运费")
+    public ResponseHelper<BigDecimal> getFregiht(
+            @RequestBody OrderBuyBean orderBuyBean,
+            HttpServletRequest request
+    ) throws RemoteServiceException {
+        BigDecimal freightAmount = freightLogic.getFreightAmountV2(orderBuyBean,request);
+        return ResponseHelper.success(freightAmount);
+    }
+
+    @PostMapping("/buy")
+    @Operation(summary = "立即购买")
+    public ResponseHelper<PayDetail> buy(
+            @RequestBody OrderBuyBean orderBuyBean,
+            HttpServletRequest request
+    ) throws Exception {
+
+        PayDetail buy = orderLogic.buy(orderBuyBean,request);
+        return ResponseHelper.success(buy);
+    }
+
+    @PostMapping("/wait/pay")
+    @Operation(summary = "支付待支付订单")
+    public ResponseHelper<PayDetail> waitPayOrder(
+            @Parameter(description = "支付人用户id") @RequestParam(required = false) String userId,
+            @Parameter(description = "订单id", required = true) @RequestParam String orderId,
+            @Parameter(description = "支付方式 微信支付 云闪付", required = true) @RequestParam(defaultValue = "微信支付") String payType,
+            @Parameter(description = "购买人姓名") @RequestParam(required = false) String buyerName,
+            @Parameter(description = "购买人身份证") @RequestParam(required = false) String buyerIdCard,
+            HttpServletRequest request
+    ) throws Exception {
+        String ip = IpUtil.getIpAddr(request);
+        PayDetail payDetail = orderLogic.waitPayOrder(userId, orderId, ip, payType, buyerName, buyerIdCard, request);
+        return ResponseHelper.success(payDetail);
+    }
+
+    @GetMapping("/my/order")
+    @Operation(summary = "我的订单")
+    public ResponseHelper<IPage<OrderDetailBean>> myOrder(
+            @Parameter(description = "用户id",required = true) @RequestParam String userId,
+            @Parameter(description = "订单状态(NOPAY:待付款 DFH:待发货 YFH:已发货 OVER:已完成 CLOSE:已关闭)",required = false) @RequestParam(required = false) String orderStatus,
+            @Parameter(description = "申请优惠状态 (100=全部  1=待审核 2=已通过 3=已驳回)",required = false) @RequestParam(required = false) Integer promotionApplyStatus,
+            @Parameter(description = "pageNo",required = true) @RequestParam Integer pageNo,
+            @Parameter(description = "pageSize",required = true) @RequestParam Integer pageSize
+    ){
+        IPage<OrderDetailBean> list = orderLogic.list(userId, orderStatus,promotionApplyStatus, pageNo, pageSize);
+        return ResponseHelper.success(list);
+    }
+
+    @GetMapping("/detail")
+    @Operation(summary = "订单详情")
+    public ResponseHelper<OrderDetailBean> detail(
+            @Parameter(description = "订单id",required = true) @RequestParam String orderId
+    ){
+        OrderDetailBean detail = orderLogic.detail(orderId);
+        return ResponseHelper.success(detail);
+    }
+
+    @GetMapping("/cancel")
+    @Operation(summary = "取消订单")
+    public ResponseHelper cancel(
+            @Parameter(description = "订单id",required = true) @RequestParam String orderId
+    ) throws RemoteServiceException, InterruptedException {
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_ORDER + orderId);
+        if(!obtain.tryLock(10,TimeUnit.SECONDS)){
+            throw new RemoteServiceException("请勿频繁操作,请稍后再试");
+        }
+        try {
+            orderLogic.cancel(Collections.singletonList(orderId));
+            return ResponseHelper.success();
+        }finally {
+            obtain.unlock();
+        }
+    }
+
+    @PostMapping("/ack")
+    @Operation(summary = "确认收货")
+    public ResponseHelper ack(
+            @Parameter(description = "订单id",required = true) @RequestParam String orderId
+    ) throws Exception {
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_ORDER + orderId);
+        if(!obtain.tryLock(10,TimeUnit.SECONDS)){
+            throw new RemoteServiceException("请勿频繁操作,请稍后再试");
+        }
+        try {
+            orderLogic.ack(orderId);
+            return ResponseHelper.success();
+        }finally {
+            obtain.unlock();
+        }
+    }
+
+
+    @PostMapping("/refund/cancel")
+    @Operation(summary = "售后订单-撤销申请")
+    public ResponseHelper<String> applyRefund(
+            @Parameter(description = "订单id",required = true) @RequestParam String refundOrderId
+    ) throws RemoteServiceException, InterruptedException {
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_ORDER + refundOrderId);
+        if(!obtain.tryLock(10,TimeUnit.SECONDS)){
+            throw new RemoteServiceException("请勿频繁操作,请稍后再试");
+        }
+        try {
+            String s = orderLogic.cancelRefund(refundOrderId);
+            return ResponseHelper.success(s);
+        }finally {
+            obtain.unlock();
+        }
+    }
+
+
+    @PostMapping("/refund/apply")
+    @Operation(summary = "售后订单-申请退款")
+    public ResponseHelper<String> applyRefund(
+            @Parameter(description = "退款",required = true) @RequestBody RefundGoodsBean refundGoodsBean
+    ) throws Exception {
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_ORDER + refundGoodsBean.getOrderId());
+        if(!obtain.tryLock(10,TimeUnit.SECONDS)){
+            throw new RemoteServiceException("请勿频繁操作,请稍后再试");
+        }
+        try {
+            String s = orderLogic.applyAfterSale(refundGoodsBean);
+            return ResponseHelper.success(s);
+        }finally {
+            obtain.unlock();
+        }
+    }
+
+    @GetMapping("/refund/list")
+    @Operation(summary = "售后订单-列表")
+    public ResponseHelper<IPage<OrderRefundDetailBean>> refundDetail(
+            @Parameter(description = "用户id",required = true) @RequestParam String userId,
+            @Parameter(description = "订单状态(DSJCL=待商家处理  DSJSH=待商家收货 DMJCL=待买家处理 OVER=已完成 CANCEL=已取消)",required = false) @RequestParam(required = false) String orderStatus,
+            @Parameter(description = "pageNo",required = true) @RequestParam Integer pageNo,
+            @Parameter(description = "pageSize",required = true) @RequestParam Integer pageSize
+    ){
+        IPage<OrderRefundDetailBean> orderRefundDetailBeanIPage = orderLogic.refundList(userId, orderStatus, pageNo, pageSize);
+        return ResponseHelper.success(orderRefundDetailBeanIPage);
+    }
+
+
+    @GetMapping("/refund/detail")
+    @Operation(summary = "售后订单-订单退款详情")
+    public ResponseHelper<OrderRefundDetailBean> refundDetail(
+            @Parameter(description = "售后订单id",required = true) @RequestParam String orderRefundId
+    ){
+        OrderRefundDetailBean orderRefundDetailBean = orderLogic.orderRefundDetail(orderRefundId);
+        return ResponseHelper.success(orderRefundDetailBean);
+    }
+
+    @PostMapping("/refund/supply")
+    @Operation(summary = "售后订单-商家已处理填写资料")
+    public ResponseHelper applyRefund(
+            @Parameter(description = "订单id",required = true) @RequestParam String orderRefundId,
+            @Parameter(description = "物流单号",required = true) @RequestParam String logisticsNo,
+            @Parameter(description = "快递公司code",required = true) @RequestParam String expressCompany,
+            @Parameter(description = "凭证ids",required = true) @RequestParam List<String> imgIds
+    ) throws RemoteServiceException, InterruptedException {
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_ORDER + orderRefundId);
+        if(!obtain.tryLock(10,TimeUnit.SECONDS)){
+            throw new RemoteServiceException("请勿频繁操作,请稍后再试");
+        }
+        try {
+            orderLogic.supplyData(orderRefundId, logisticsNo, expressCompany, imgIds);
+            return ResponseHelper.success();
+        }finally {
+            obtain.unlock();
+        }
+    }
+
+    @GetMapping("/count")
+    @Operation(summary = "各订单状态的数量")
+    public ResponseHelper<OrderStatusBean> count(
+            @Parameter(description = "订单id",required = true) @RequestParam String userId
+    ){
+        OrderStatusBean count = orderLogic.count(userId);
+        return ResponseHelper.success(count);
+    }
+
+    @GetMapping("/qrcode")
+    @Operation(summary = "获取付费订单码")
+    public ResponseHelper<String> qrcode(
+            @Parameter(description = "订单id",required = true) @RequestParam String orderId,
+            @Parameter(description = "用户id",required = true) @RequestParam String userId,
+            HttpServletRequest request
+    ) throws RemoteServiceException {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        String qrcode = wechatLogic.getQrcode(QrCodeEnum.ORDER.toString().toLowerCase(Locale.ROOT),
+                orderId,userId,currentCompanyWechat.getCompanyWechatId());
+        return ResponseHelper.success(qrcode);
+    }
+
+    @PostMapping("/notice")
+    @Operation(summary = "提醒发货")
+    public ResponseHelper notice(
+            @Parameter(description = "订单id",required = true) @RequestParam String orderId
+    ){
+        orderLogic.notice(orderId);
+        return ResponseHelper.success();
+    }
+
+}

+ 137 - 0
src/main/java/com/gree/mall/contest/controller/mini/order/OrderTaxController.java

@@ -0,0 +1,137 @@
+package com.gree.mall.contest.controller.mini.order;
+
+import com.aliyuncs.utils.StringUtils;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.annotation.ApiNotAuth;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.order.OrderTaxLogic;
+import com.gree.mall.contest.plus.entity.OrderTax;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@RestController
+@Tag(name = "小程序用户发票", description = "小程序用户发票")
+@RequestMapping(value = "/mini/user/order/tax", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class OrderTaxController {
+
+    private final OrderTaxLogic orderTaxLogic;
+
+    @PostMapping("/save")
+    @Operation(summary = "添加发票")
+    public ResponseHelper<OrderTax> saveTax(
+            @Parameter(description = "object", required = true) @RequestBody OrderTax orderTaxBean,
+            HttpServletRequest request
+    ) throws Exception {
+        if (orderTaxBean.getTaxType()) {
+            if (StringUtils.isEmpty(orderTaxBean.getReceiverPhone())) {
+                throw new RemoteServiceException("请添加收件人电话");
+            }
+            if (StringUtils.isEmpty(orderTaxBean.getReceiverAddress())) {
+                throw new RemoteServiceException("请添加收件人地址");
+            }
+            if (StringUtils.isEmpty(orderTaxBean.getReceiverName())) {
+                throw new RemoteServiceException("请添加收件人姓名");
+            }
+            if(StringUtils.isEmpty(orderTaxBean.getRegisterAddress())) {
+                throw new RemoteServiceException("请添加注册地址");
+            }
+            if(StringUtils.isEmpty(orderTaxBean.getRegisterPhone())){
+                throw new RemoteServiceException("请添加注册电话");
+            }
+            if(StringUtils.isEmpty(orderTaxBean.getBank())){
+                throw new RemoteServiceException("请添加开户银行");
+            }
+            if(StringUtils.isEmpty(orderTaxBean.getAccount())){
+                throw new RemoteServiceException("请添加开户帐号");
+            }
+        }
+        //判断纳税识别号长度
+        if(orderTaxBean.getTaxNo() != null && (orderTaxBean.getTaxNo().length() < 15 || orderTaxBean.getTaxNo().length() > 20)){
+            throw new RemoteServiceException("请填写15至20位长度的纳税识别号");
+        }
+        OrderTax orderTax = orderTaxLogic.saveTax(orderTaxBean);
+        return ResponseHelper.success(orderTax);
+    }
+
+    @PostMapping("/update")
+    @Operation(summary = "修改发票")
+    public ResponseHelper<OrderTax> updateTax(
+            @RequestBody OrderTax orderTax
+    ) throws Exception {
+        orderTaxLogic.updateTax(orderTax);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/del")
+    @Operation(summary = "删除发票")
+    public ResponseHelper<String> delTax(
+            @Parameter(description = "发票id", required = true) @RequestParam(required = true) String orderTaxId
+    ) throws Exception {
+        return ResponseHelper.success(orderTaxLogic.delTax(orderTaxId));
+    }
+
+    @PostMapping("/send")
+    @Operation(summary = "发送发票到email")
+    public ResponseHelper<String> sendTax(
+            @Parameter(description = "发票id", required = true) @RequestParam(required = true) String orderTaxId
+    ) throws Exception {
+        String addressId = orderTaxLogic.sendTax(orderTaxId);
+        return ResponseHelper.success(addressId);
+    }
+
+
+    @ApiNotAuth
+    @PostMapping("/yonge")
+    @Operation(summary = "拆分邮件发送")
+    public ResponseHelper yonge(
+            @Parameter(description = "excel文件", required = true) @RequestParam(required = true) MultipartFile file
+    ) throws Exception {
+        orderTaxLogic.yonge(file);
+        return ResponseHelper.success();
+    }
+
+
+    @GetMapping("/list")
+    @Operation(summary = "发票列表")
+    public ResponseHelper<Page<OrderTax>> pageTax(
+            @Parameter(description = "用户id", required = true) @RequestParam(required = true) String userId,
+            @Parameter(description = "页号", required = true) @RequestParam Integer pageNum,
+            @Parameter(description = "页大小", required = true) @RequestParam Integer pageSize
+    ) throws Exception {
+
+        IPage<OrderTax> page = orderTaxLogic.page(userId, pageNum, pageSize);
+        return ResponseHelper.success(page);
+    }
+
+    @GetMapping("/detail")
+    @Operation(summary = "发票详情")
+    public ResponseHelper<OrderTax> detail(
+            @Parameter(description = "发票id", required = true) @RequestParam(required = true) String orderTaxId
+    ) throws Exception {
+        OrderTax orderTax = orderTaxLogic.detail(orderTaxId);
+        return ResponseHelper.success(orderTax);
+    }
+
+
+    @GetMapping("/title/list")
+    @Operation(summary = "发票抬头列表")
+    public ResponseHelper<String> title(
+            @Parameter(description = "公司名称", required = true) @RequestParam(required = true) String companyName
+    ) throws Exception {
+        Object object = orderTaxLogic.title(companyName);
+        return ResponseHelper.success(object);
+    }
+
+
+}

+ 78 - 0
src/main/java/com/gree/mall/contest/controller/mini/order/ShoppingCartController.java

@@ -0,0 +1,78 @@
+package com.gree.mall.contest.controller.mini.order;
+
+import com.gree.mall.contest.bean.order.ShoppingCartBean;
+import com.gree.mall.contest.bean.order.ShoppingCartDetail;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.order.ShoppingCartLogic;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+
+@Slf4j
+@RestController
+@Tag(name = "APP师傅端-购物车", description = "APP师傅端-购物车")
+@RequestMapping(value = "/mini/shpping/cart", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class ShoppingCartController {
+
+    private final ShoppingCartLogic shoppingCartLogic;
+
+    @GetMapping("/notice")
+    @Operation(summary = "消息通知")
+    public ResponseHelper<String> notice(HttpServletRequest request) {
+        return ResponseHelper.success(shoppingCartLogic.miniNotice(request));
+    }
+
+
+    @GetMapping("/list")
+    @Operation(summary = "购物车列表")
+    public ResponseHelper<ShoppingCartDetail> list(
+            @Parameter(description = "用户id", required = true) @RequestParam String userId
+    ) throws InterruptedException {
+        ShoppingCartDetail shoppingCartLists = shoppingCartLogic.queryShoppingCart(userId);
+        return ResponseHelper.success(shoppingCartLists);
+    }
+
+    @PostMapping("/add/one")
+    @Operation(summary = "商品详情添加商品购物车")
+    public ResponseHelper<ShoppingCartDetail> addOne(HttpServletRequest request, @RequestBody ShoppingCartBean shoppingCartBean) throws RemoteServiceException, InterruptedException {
+        ShoppingCartDetail shoppingCartDetail = shoppingCartLogic.addOne(request, shoppingCartBean);
+        return ResponseHelper.success(shoppingCartDetail);
+    }
+
+
+    @PostMapping("/add")
+    @Operation(summary = "添加到购物车")
+    public ResponseHelper<ShoppingCartDetail> addShoppingCart(HttpServletRequest request, @RequestBody ShoppingCartBean shoppingCartBean) throws RemoteServiceException, InterruptedException {
+        ShoppingCartDetail shoppingCartDetail = shoppingCartLogic.addShoppingCart(request, shoppingCartBean);
+        return ResponseHelper.success(shoppingCartDetail);
+    }
+
+    @PostMapping("/clear")
+    @Operation(summary = "清空购物车")
+    public ResponseHelper clearShoppingCart(
+            @Parameter(description = "用户id", required = true) @RequestParam String userId
+    ) {
+        shoppingCartLogic.clearShoppingCart(userId);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/remove")
+    @Operation(summary = "删除购物车商品")
+    public ResponseHelper<ShoppingCartDetail> clearShoppingCart(
+            @Parameter(description = "用户id", required = true) @RequestParam String userId,
+            @Parameter(description = "购物车ids", required = true) @RequestParam List<String> shoppingCartIds
+    ) throws InterruptedException {
+        ShoppingCartDetail remove = shoppingCartLogic.remove(userId, shoppingCartIds);
+        return ResponseHelper.success(remove);
+    }
+
+}

+ 1 - 1
src/main/java/com/gree/mall/contest/controller/mini/user/AddressController.java

@@ -18,7 +18,7 @@ import javax.servlet.http.HttpServletRequest;
 @Slf4j
 @RestController
 @Tag(name = "小程序用户地址", description = "小程序用户地址")
-@RequestMapping(value = "/miniapp/user/address", produces = "application/json; charset=utf-8")
+@RequestMapping(value = "/mini/user/address", produces = "application/json; charset=utf-8")
 @RequiredArgsConstructor
 public class AddressController {
 

+ 1 - 1
src/main/java/com/gree/mall/contest/controller/mini/user/UserController.java

@@ -27,7 +27,7 @@ import java.math.BigDecimal;
 @Slf4j
 @RestController
 @Tag(name = "小程序用户", description = "小程序用户")
-@RequestMapping(value = "/miniapp/user", produces = "application/json; charset=utf-8")
+@RequestMapping(value = "/mini/user", produces = "application/json; charset=utf-8")
 @RequiredArgsConstructor
 public class UserController {
 

+ 1 - 1
src/main/java/com/gree/mall/contest/controller/mini/user/WorkerController.java

@@ -18,7 +18,7 @@ import javax.servlet.http.HttpServletRequest;
 @Slf4j
 @RestController
 @Tag(name = "申请成为业务员", description = "申请成为业务员")
-@RequestMapping(value = "/miniapp/worker", produces = "application/json; charset=utf-8")
+@RequestMapping(value = "/mini/worker", produces = "application/json; charset=utf-8")
 @RequiredArgsConstructor
 public class WorkerController {
     private final WorkerLogic workerLogic;

+ 113 - 0
src/main/java/com/gree/mall/contest/controller/pc/apply/ApplyController.java

@@ -0,0 +1,113 @@
+package com.gree.mall.contest.controller.pc.apply;
+
+
+import cn.hutool.core.lang.TypeReference;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.annotation.ApiNotAuth;
+import com.gree.mall.contest.annotation.ZfireList;
+import com.gree.mall.contest.bean.merchant.WebsitApplyAdd;
+import com.gree.mall.contest.bean.merchant.WebsitApplyDetail;
+import com.gree.mall.contest.bean.merchant.WebsitApplyVO;
+import com.gree.mall.contest.bean.zfire.ZfireParamBean;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.merchant.MerchantLogic;
+import com.gree.mall.contest.plus.entity.WebsitApply;
+import com.gree.mall.contest.utils.zfire.FieldUtils;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+@Validated
+@RequiredArgsConstructor
+@RestController
+@Tag(name = "商家入驻申请管理,申请入驻服API", description = "商家入驻申请管理,申请入驻API")
+@RequestMapping("/pc/websitApply")
+public class ApplyController {
+
+    private final MerchantLogic merchantLogic;
+
+    @ApiNotAuth
+    @PostMapping("/register")
+    @Operation(summary = "注册账号-验证验证码 true下一步 false展示申请审核详情")
+    public ResponseHelper<Boolean> register(
+            @Parameter(description = "手机号", required = true) @RequestParam String mobile,
+            @Parameter(description = "验证码") @RequestParam(required = false) String code
+    ) {
+        Boolean b = merchantLogic.register(mobile, code);
+        return ResponseHelper.success(b);
+    }
+
+    @ApiNotAuth
+    @PostMapping("/applyMobile")
+    @Operation(summary = "展示申请审核详情")
+    public ResponseHelper<WebsitApplyDetail> applyMobile(
+            @Parameter(description = "手机号", required = true) @RequestParam String mobile
+    ) {
+        WebsitApplyDetail websitApply = merchantLogic.applyMobile(mobile);
+        return ResponseHelper.success(websitApply);
+    }
+
+    @ApiNotAuth
+    @PostMapping("/applyList")
+    @Operation(summary = "申请入驻")
+    public ResponseHelper<WebsitApply> applyMobile(
+            @RequestBody WebsitApplyAdd websitApplyAdd
+    ) {
+        WebsitApply websitApply = merchantLogic.applyList(websitApplyAdd);
+        return ResponseHelper.success(websitApply);
+    }
+
+    @ZfireList
+    @PostMapping("/list")
+    @Operation(summary = "服务商入驻申请管理-列表")
+    public ResponseHelper<IPage<WebsitApplyVO>> pageApply(
+            @RequestBody ZfireParamBean zfireParamBean
+    ) {
+        IPage<WebsitApplyVO> page = merchantLogic.pageApply(zfireParamBean);
+        return ResponseHelper.success(page, new TypeReference<WebsitApplyVO>() {
+        });
+    }
+
+    @PostMapping("/list/export")
+    @Operation(summary = "服务商入驻申请管理-导出")
+    public void listExport(
+            @RequestBody ZfireParamBean zfireParamBean,
+            HttpServletRequest request,
+            HttpServletResponse response
+    ) throws Exception {
+        //2.查询要导出的内容
+        IPage<WebsitApplyVO> baseVOIPage = merchantLogic.pageApply(zfireParamBean);
+        //3.导出
+        FieldUtils.exportData(baseVOIPage.getRecords(), zfireParamBean.getExportFields(), request, response);
+    }
+
+
+    @PostMapping("/detail")
+    @Operation(summary = "展示申请审核详情")
+    public ResponseHelper<WebsitApplyDetail> detail(
+            @Parameter(description = "id", required = true) @RequestParam String id
+    ) {
+        WebsitApplyDetail websitApply = merchantLogic.detail(id);
+        return ResponseHelper.success(websitApply);
+    }
+
+    @PostMapping("/updateIn")
+    @Operation(summary = "跟进")
+    public ResponseHelper update(
+            @Parameter(description = "id", required = true) @RequestParam String id,
+            @Parameter(description = "WAIT 跟进 FAIL 无需跟进", required = true) @RequestParam String status,
+            @Parameter(description = "备注", required = true) @RequestParam String remark
+    ) {
+        merchantLogic.updateIn(id, status, remark);
+        return ResponseHelper.success();
+    }
+
+
+}

+ 153 - 0
src/main/java/com/gree/mall/contest/controller/pc/goods/CategoryLogic.java

@@ -0,0 +1,153 @@
+package com.gree.mall.contest.controller.pc.goods;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.goods.CategoryBean;
+import com.gree.mall.contest.bean.goods.GoodsNewBean;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
+import com.gree.mall.contest.commonmapper.CategoryMapper;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.Goods;
+import com.gree.mall.contest.plus.entity.GoodsCategory;
+import com.gree.mall.contest.plus.entity.GoodsTagRela;
+import com.gree.mall.contest.plus.service.GoodsCategoryService;
+import com.gree.mall.contest.plus.service.GoodsService;
+import com.gree.mall.contest.plus.service.GoodsTagRelaService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class CategoryLogic {
+
+    private final GoodsService goodsService;
+    private final CategoryMapper categoryMapper;
+    private final GoodsCategoryService goodsCategoryService;
+    private final CommonLogic commonLogic;
+    private final GoodsTagRelaService goodsTagRelaService;
+
+    /**
+     * 分类列表
+     * @return
+     */
+    public List<GoodsCategory> list(HttpServletRequest request, String parentId, String name){
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+
+        List<GoodsCategory> list = goodsCategoryService.lambdaQuery()
+                .eq(GoodsCategory::getCompanyWechatId , currentCompanyWechat.getCurrentCompanyWechatId())
+                .eq(StringUtils.isNotEmpty(parentId), GoodsCategory::getParentId, parentId)
+                .eq(StringUtils.isEmpty(parentId), GoodsCategory::getLevel, 1)
+                .like(StringUtils.isNotEmpty(name),GoodsCategory::getName,name)
+                .eq(GoodsCategory::getStatus, true)
+                .eq(GoodsCategory::getDel,false)
+                .orderByDesc(GoodsCategory::getSortNum)
+                .orderByDesc(GoodsCategory::getCreateTime)
+                .list();
+        return list;
+    }
+
+
+    public List<CategoryBean> queryAll(HttpServletRequest request){
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        return  categoryMapper.queryCategory(currentCompanyWechat.getCurrentCompanyWechatId());
+    }
+    public List<GoodsCategory> queryFirst(HttpServletRequest request){
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        return goodsCategoryService.lambdaQuery()
+                .isNull(GoodsCategory::getParentId)
+                .eq(GoodsCategory::getStatus,true)
+                .eq(GoodsCategory::getDel,false)
+                .eq(GoodsCategory::getCompanyWechatId,currentCompanyWechat.getCurrentCompanyWechatId())
+                .orderByAsc(GoodsCategory::getSortNum)
+                .orderByDesc(GoodsCategory::getCreateTime)
+                .list();
+    }
+    public List<GoodsCategory> querySecond(HttpServletRequest request , String categoryId){
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        return goodsCategoryService.lambdaQuery()
+                .eq(GoodsCategory::getCompanyWechatId , currentCompanyWechat.getCurrentCompanyWechatId())
+                .eq(GoodsCategory::getParentId,categoryId)
+                .eq(GoodsCategory::getStatus,true)
+                .eq(GoodsCategory::getDel,false)
+                .orderByAsc(GoodsCategory::getSortNum)
+                .orderByDesc(GoodsCategory::getCreateTime)
+                .list();
+    }
+
+
+    public IPage<GoodsNewBean> categoryGoodsList(HttpServletRequest request, String categoryId, Integer sort, Integer pageNum, Integer pageSize) {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        if (StringUtils.isEmpty(categoryId))
+            categoryId=null;
+
+        IPage<GoodsNewBean> page = categoryMapper.listGoods(new Page(pageNum, pageSize),currentCompanyWechat.getCurrentCompanyWechatId(),categoryId,sort);
+
+        List<GoodsNewBean> records = new ArrayList<>();
+
+        //补充标签
+        for (Goods bean : page.getRecords()) {
+            GoodsNewBean goodsNewBean = BeanUtil.toBean(bean, GoodsNewBean.class);
+            //查询上标签
+            List<GoodsTagRela> list1 = goodsTagRelaService.lambdaQuery().eq(GoodsTagRela::getGoodsId, bean.getGoodsId())
+                    .eq(GoodsTagRela::getType,1).list();
+            if (list1.size() > 0) {
+                List<String> collect1 = list1.stream().map(GoodsTagRela::getGoodsTagName).collect(Collectors.toList());
+                goodsNewBean.setTags1(collect1);
+            }
+
+            //查询活动专区标签 //查询下标签
+            //活动商品
+            List<GoodsTagRela> list = goodsTagRelaService.lambdaQuery().eq(GoodsTagRela::getGoodsId, bean.getGoodsId())
+                    .eq(GoodsTagRela::getType,2).list();
+            if (list.size() > 0) {
+                List<String> collect1 = list.stream().map(GoodsTagRela::getGoodsTagName).collect(Collectors.toList());
+                goodsNewBean.setTags2(collect1);
+            }
+            records.add(goodsNewBean);
+        }
+        page.setRecords(records);
+
+        return page;
+    }
+
+
+    public IPage<Goods> categoryGoodsList(HttpServletRequest request,String categoryId, Integer pageNum, Integer pageSize) {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        List<GoodsCategory> goodsCategories = goodsCategoryService.lambdaQuery()
+                .eq(GoodsCategory::getCompanyWechatId , currentCompanyWechat.getCurrentCompanyWechatId())
+                .eq(GoodsCategory::getParentId, categoryId)
+                .list();
+        List<String> categoryIds = goodsCategories.stream().map(GoodsCategory::getCategoryId).collect(Collectors.toList());
+        if(categoryIds.size() == 0){
+            return new Page<>(pageNum,pageSize);
+        }
+
+        return goodsService.lambdaQuery()
+                .eq(Goods::getCompanyWechatId , currentCompanyWechat.getCurrentCompanyWechatId())
+                .in(Goods::getCategoryId,categoryIds)
+                .eq(Goods::getStatus,true)
+                .eq(Goods::getDel,false)
+                .eq(Goods::getPromotionGroup,false)
+                .orderByDesc(Goods::getSortNum)
+                .orderByDesc(Goods::getSoldNum)
+                .orderByDesc(Goods::getCreateTime)
+                .page(new Page(pageNum, pageSize));
+    }
+
+
+}

+ 16 - 0
src/main/java/com/gree/mall/contest/enums/ExchangeCodeTypeEnum.java

@@ -0,0 +1,16 @@
+package com.gree.mall.contest.enums;
+
+public enum ExchangeCodeTypeEnum {
+
+    GIFT("礼品兑换码"),SUB("抵扣码");
+
+    private String name;
+
+    ExchangeCodeTypeEnum(String name) {
+        this.name = name;
+    }
+
+    public String getName(){
+        return name;
+    }
+}

+ 22 - 0
src/main/java/com/gree/mall/contest/enums/merchant/WebsitApplyStatusEnum.java

@@ -0,0 +1,22 @@
+package com.gree.mall.contest.enums.merchant;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum WebsitApplyStatusEnum implements BaseEnum {
+    WAIT("WAIT","继续跟进"),
+    FAIL("FAIL","无需跟进"),
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+
+}

+ 68 - 6
src/main/java/com/gree/mall/contest/logic/FreightLogic.java

@@ -1,21 +1,22 @@
 package com.gree.mall.contest.logic;
 
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.json.JSONUtil;
 import com.aliyuncs.utils.StringUtils;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.gree.mall.contest.bean.Freight.FreightTemplateBean;
 import com.gree.mall.contest.bean.Freight.RegionTreeBean;
 import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.bean.order.BuyGood;
+import com.gree.mall.contest.bean.order.OrderBuyBean;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
+import com.gree.mall.contest.enums.FreightTypeEnum;
 import com.gree.mall.contest.enums.RegionLevelEnum;
 import com.gree.mall.contest.exception.RemoteServiceException;
 import com.gree.mall.contest.logic.common.CommonLogic;
-import com.gree.mall.contest.plus.entity.FreightTemplate;
-import com.gree.mall.contest.plus.entity.FreightTemplateDetail;
-import com.gree.mall.contest.plus.entity.Region;
-import com.gree.mall.contest.plus.service.FreightTemplateDetailService;
-import com.gree.mall.contest.plus.service.FreightTemplateService;
-import com.gree.mall.contest.plus.service.RegionService;
+import com.gree.mall.contest.plus.entity.*;
+import com.gree.mall.contest.plus.service.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
@@ -38,6 +39,8 @@ public class FreightLogic {
     private final FreightTemplateDetailService freightTemplateDetailService;
     private final RegionService regionService;
     private final CommonLogic commonLogic;
+    private final UserAddressService userAddressService;
+    private final GoodsService goodsService;
 
     /**
      * 运费模板列表
@@ -201,5 +204,64 @@ public class FreightLogic {
         return childs;
     }
 
+    public BigDecimal getFreightAmountV2(OrderBuyBean freightBean, HttpServletRequest request) throws RemoteServiceException {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        if (StringUtils.isNotEmpty(freightBean.getUserAddressId())) {
+            UserAddress userAddress = userAddressService.getById(freightBean.getUserAddressId());
+            BigDecimal freightAmountV2 = this.getFreightAmountV2(userAddress.getProvince(), userAddress.getCity(), userAddress.getArea(), freightBean.getBuyGoods(), currentCompanyWechat);
+            return freightAmountV2;
+        }
+        return BigDecimal.valueOf(0);
+    }
+
+    public BigDecimal getFreightAmountV2(String province, String city, String area, List<BuyGood> buyGoods, AdminCompanyWechat adminCompanyWechat) throws RemoteServiceException {
+        BigDecimal totalAmount = new BigDecimal(0.00);
+        log.info("运费商品列表{}", JSONUtil.toJsonStr(buyGoods));
+        if (buyGoods != null && buyGoods.size() > 0) {
+
+            for (BuyGood buyGood : buyGoods) {
+                BigDecimal freightItem = computeAmountItem(province, city, area, buyGood, adminCompanyWechat);
+                totalAmount = totalAmount.add(freightItem);
+            }
+        }
+        return totalAmount;
+    }
 
+    public BigDecimal computeAmountItem(String province, String city, String area, BuyGood buyGood, AdminCompanyWechat adminCompanyWechat) throws RemoteServiceException {
+        Goods goods = goodsService.getById(buyGood.getGoodsId());
+        if (goods != null && FreightTypeEnum.UNIFIED.toString().equals(goods.getFreightType())) {
+            return goods.getFreight().multiply(new BigDecimal(buyGood.getNum()));
+        }
+        FreightTemplate freightTemplate = freightTemplateService.lambdaQuery()
+                .eq(FreightTemplate::getFreightTemplateId, goods.getFreightTemplateId())
+                .one();
+
+        if (freightTemplate == null) {
+            log.info("异常商品id{}", buyGood.getGoodsId());
+            throw new RemoteServiceException("暂不支持该地区的配送服务...");
+        }
+        if (StringUtils.isNotEmpty(province) && StringUtils.isNotEmpty(city)) {
+            FreightTemplateDetail one = freightTemplateDetailService.lambdaQuery()
+                    .eq(FreightTemplateDetail::getFreightTemplateId, freightTemplate.getFreightTemplateId())
+                    .eq(FreightTemplateDetail::getProvince, province)
+                    .eq(FreightTemplateDetail::getCity, city)
+//                .eq(FreightTemplateDetail::getArea, area)
+                    .eq(FreightTemplateDetail::getCompanyWechatId, adminCompanyWechat.getCompanyWechatId())
+                    .eq(FreightTemplateDetail::getStatus, true)
+                    .one();
+            if (one == null) {
+                throw new RemoteServiceException("暂不支持该地区的配送服务");
+            }
+            Integer firstLimit = one.getFirstLimit();
+            Integer continuousLimit = one.getContinuousLimit();
+            int num = buyGood.getNum();
+            BigDecimal conAmount = new BigDecimal(0.00);
+            if (num > firstLimit) {
+                int xj = (num - firstLimit) / continuousLimit + ((num - firstLimit) % continuousLimit == 0 ? 0 : 1);
+                conAmount = one.getContinuousAmount().multiply(new BigDecimal(xj));
+            }
+            return one.getFirstAmount().add(conAmount);
+        }
+        return BigDecimal.valueOf(0);
+    }
 }

+ 23 - 23
src/main/java/com/gree/mall/contest/logic/SMSLogic.java

@@ -58,14 +58,15 @@ public class SMSLogic {
 
     /**
      * 发送优惠申请通知
-     * @param mobile 手机号
-     * @param orderNo  订单号
+     *
+     * @param mobile      手机号
+     * @param orderNo     订单号
      * @param totalAmount 当前金额
-     * @param discunt 优惠金额
-     * @param name 申请人
-     * @param user 收货人
+     * @param discunt     优惠金额
+     * @param name        申请人
+     * @param user        收货人
      */
-    public void sendDiscountNotice(String mobile, String orderNo, BigDecimal totalAmount,BigDecimal discunt,String name,String user){
+    public void sendDiscountNotice(String mobile, String orderNo, BigDecimal totalAmount, BigDecimal discunt, String name, String user) {
         try {
             String orderNo2 = orderNo.substring(orderNo.length() - 6, orderNo.length());
             Map<String, Object> map = new HashMap<>();
@@ -75,18 +76,19 @@ public class SMSLogic {
             map.put("name", name);
             map.put("user", user);
             this.send(mobile, this.TEMPLATE_SMS_DISCOUNT, map);
-        }catch(Exception e){
-            log.error("发送优惠申请通知失败",e);
+        } catch (Exception e) {
+            log.error("发送优惠申请通知失败", e);
         }
     }
 
 
     /**
      * 发送短信验证码
-     * @param mobile 手机号
-     * @param key 拖拽式验证码的key
+     *
+     * @param mobile    手机号
+     * @param key       拖拽式验证码的key
      * @param vrifyCode 拖拽式验证码的x轴值
-     * @param sendType 发送短信类型
+     * @param sendType  发送短信类型
      * @throws Exception
      */
     public void sendSms(String mobile, String key, String vrifyCode, String sendType) throws Exception {
@@ -119,7 +121,7 @@ public class SMSLogic {
         smsRecordService.save(smsRecord);
 
         //记录发送的code
-        redisUtil.set(Constant.RedisPrefix.SMS + ":" + sendType + ":" + mobile,code,5*60);
+        redisUtil.set(Constant.RedisPrefix.SMS + ":" + sendType + ":" + mobile, code, 5 * 60);
     }
 
 
@@ -128,8 +130,8 @@ public class SMSLogic {
      */
     public void checkSmsCode(String mobile, String code, String sendType) throws RemoteServiceException {
         String redisKey = Constant.RedisPrefix.SMS + ":" + sendType + ":" + mobile;
-        String value = (String)redisUtil.get(redisKey);
-        if(!StringUtils.equals(value,code)){
+        String value = (String) redisUtil.get(redisKey);
+        if (!StringUtils.equals(value, code)) {
             throw new RemoteServiceException("短信验证失败");
         }
         redisUtil.del(redisKey);
@@ -144,7 +146,7 @@ public class SMSLogic {
      */
     private void checkSendTime(String mobile) throws RemoteServiceException {
         String key = Constant.RedisPrefix.SMS + ":" + mobile;
-        if(redisUtil.hasKey(key)){
+        if (redisUtil.hasKey(key)) {
             throw new RemoteServiceException("发送短信间隔时间太短,请耐心等候");
         }
     }
@@ -158,7 +160,7 @@ public class SMSLogic {
      * @return
      */
     private boolean checkVrifyCode(String key, String vrifyCode) throws RemoteServiceException {
-        Object serverCode = redisUtil.get(Constant.RedisPrefix.VERIFICATION +":"+key);
+        Object serverCode = redisUtil.get(Constant.RedisPrefix.VERIFICATION + ":" + key);
         if (serverCode == null) {
             throw new RemoteServiceException("验证码超时");
         }
@@ -166,7 +168,7 @@ public class SMSLogic {
         int code = (int) serverCode;
         int code2 = Integer.parseInt(vrifyCode);
         if (code >= (code2 - 15) && code <= (code2 + 15)) {
-            redisUtil.del(Constant.RedisPrefix.VERIFICATION +":"+key);
+            redisUtil.del(Constant.RedisPrefix.VERIFICATION + ":" + key);
             return true;
         } else {
             throw new RemoteServiceException("验证码校验不通过");
@@ -174,9 +176,7 @@ public class SMSLogic {
     }
 
 
-
-
-    private String send(String mobile,String templateCode,Map<String,Object> param) throws ClientException, com.aliyuncs.exceptions.ClientException {
+    private String send(String mobile, String templateCode, Map<String, Object> param) throws ClientException, com.aliyuncs.exceptions.ClientException {
 
         DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
         IAcsClient client = new DefaultAcsClient(profile);
@@ -199,11 +199,11 @@ public class SMSLogic {
         log.info("短信验证返回数据 :" + response.getData());
 
         String dateRes = response.getData();
-        Map<String,Object> smsRsp = JSONUtil.parseObj(dateRes);
+        Map<String, Object> smsRsp = JSONUtil.parseObj(dateRes);
         //获取数据
         String codeRes = smsRsp.get("Code").toString();
-        if(!codeRes.equals("OK")) {
-            log.error("短信发送失败,mobile:{}",mobile);
+        if (!codeRes.equals("OK")) {
+            log.error("短信发送失败,mobile:{}", mobile);
         }
         return content;
     }

+ 168 - 0
src/main/java/com/gree/mall/contest/logic/activity/PromotionLuckDrawLogic.java

@@ -0,0 +1,168 @@
+package com.gree.mall.contest.logic.activity;
+
+import com.aliyuncs.utils.StringUtils;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.promotion.PromotionLuckDrawExchangeDetail;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.*;
+import com.gree.mall.contest.plus.service.PromotionLuckDrawCouponCodeService;
+import com.gree.mall.contest.plus.service.PromotionLuckDrawCouponService;
+import com.gree.mall.contest.plus.service.PromotionLuckDrawService;
+import com.gree.mall.contest.plus.service.UserCouponService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PromotionLuckDrawLogic {
+
+    private final PromotionLuckDrawService promotionLuckDrawService;
+    private final PromotionLuckDrawCouponService promotionLuckDrawCouponService;
+    private final PromotionLuckDrawCouponCodeService promotionLuckDrawCouponCodeService;
+    private final UserCouponService userCouponService;
+    private final CommonLogic commonLogic;
+
+    /**
+     * 兑换优惠券
+     *
+     * @param code
+     * @param request
+     */
+    @Transactional
+    public PromotionLuckDrawExchangeDetail exchange(String code, HttpServletRequest request) {
+
+        String userCouponId = "draw" + IdWorker.getIdStr();
+
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        User user = currentCompanyWechat.getUser();
+        if (user.getExchangeNum() >= 10) {
+            throw new RemoteServiceException("您错误兑换次数已超上限,已被禁止兑换");
+        }
+        PromotionLuckDrawCouponCode promotionLuckDrawCouponCode = promotionLuckDrawCouponCodeService.lambdaQuery().eq(PromotionLuckDrawCouponCode::getCode, code).one();
+        if (promotionLuckDrawCouponCode == null) {
+            user.setExchangeNum(user.getExchangeNum() + 1);
+            user.updateById();
+            throw new RemoteServiceException("兑换码错误,请重新兑换");
+        }
+        long curTime = new Date().getTime();
+        if (promotionLuckDrawCouponCode.getEndTime().getTime() < curTime || promotionLuckDrawCouponCode.getStartTime().getTime() > curTime
+                || promotionLuckDrawCouponCode.getStatus()) {
+            throw new RemoteServiceException("兑换码无效");
+        }
+        //本次活动
+        PromotionLuckDraw promotionLuckDraw = promotionLuckDrawService.getById(promotionLuckDrawCouponCode.getPromotionLuckDrawId());
+        if (promotionLuckDraw == null || !promotionLuckDraw.getStatus() || promotionLuckDraw.getStartTime().getTime() > curTime
+                || promotionLuckDraw.getEndTime().getTime() < curTime) {
+            throw new RemoteServiceException("本次活动已取消");
+        }
+        //兑换成功
+        promotionLuckDrawCouponCode.setUserNickName(user.getNickName());
+        promotionLuckDrawCouponCode.setUserId(user.getUserId());
+        promotionLuckDrawCouponCode.setUserMobile(user.getMobile());
+        promotionLuckDrawCouponCode.setUserExchangeTime(new Date());
+        promotionLuckDrawCouponCode.setStatus(true);
+        promotionLuckDrawCouponCode.setUserCouponId(userCouponId);
+        promotionLuckDrawCouponCode.updateById();
+
+        //发放优惠券
+        PromotionLuckDrawCoupon promotionLuckDrawCoupon = promotionLuckDrawCouponService.getById(promotionLuckDrawCouponCode.getPromotionLuckDrawCouponId());
+        if (promotionLuckDrawCoupon == null) {
+            throw new RemoteServiceException("兑换失败");
+        }
+        UserCoupon userCoupon = new UserCoupon();
+        userCoupon.setId(userCouponId);
+        userCoupon.setUserId(currentCompanyWechat.getUserId());
+        userCoupon.setCouponId(promotionLuckDrawCoupon.getId());
+        userCoupon.setCouponName(promotionLuckDrawCoupon.getCouponName());
+        userCoupon.setCouponValue(promotionLuckDrawCoupon.getDiscountAmount());
+        userCoupon.setCouponType(promotionLuckDrawCoupon.getCouponType());
+        userCoupon.setReceiveTime(new Date());
+        userCoupon.setActiveStartTime(promotionLuckDraw.getCouponStartTime());
+        userCoupon.setActiveEndTime(promotionLuckDraw.getCouponEndTime());
+        userCoupon.setOrderAmount(promotionLuckDrawCoupon.getOrderLimitAmount());
+        userCoupon.setReceivePlatform("抽奖活动兑换");
+        userCoupon.setTransferType(false);
+        userCoupon.setLeftShareTimes(0);
+        userCoupon.setCompanyWechatId(currentCompanyWechat.getCompanyWechatId());
+        userCoupon.setCompanyName(currentCompanyWechat.getCompanyName());
+        userCoupon.insert();
+
+        return this.detail(code);
+    }
+
+
+    /**
+     * 兑换记录
+     */
+    public IPage<PromotionLuckDrawCouponCode> list(Integer pageNum, Integer pageSize, HttpServletRequest request) {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        IPage<PromotionLuckDrawCouponCode> list = promotionLuckDrawCouponCodeService.lambdaQuery()
+                .eq(PromotionLuckDrawCouponCode::getUserId, currentCompanyWechat.getUserId())
+                .orderByDesc(PromotionLuckDrawCouponCode::getUserExchangeTime).page(new Page<>(pageNum, pageSize));
+        return list;
+    }
+
+    /**
+     * 兑换详情
+     *
+     * @param code
+     * @return
+     */
+    public PromotionLuckDrawExchangeDetail detail(String code) {
+        PromotionLuckDrawCouponCode promotionLuckDrawCouponCode = promotionLuckDrawCouponCodeService.lambdaQuery().eq(PromotionLuckDrawCouponCode::getCode, code).one();
+        PromotionLuckDraw promotionLuckDraw = promotionLuckDrawService.getById(promotionLuckDrawCouponCode.getPromotionLuckDrawId());
+        PromotionLuckDrawCoupon promotionLuckDrawCoupon = promotionLuckDrawCouponService.getById(promotionLuckDrawCouponCode.getPromotionLuckDrawCouponId());
+        //返回详情
+        PromotionLuckDrawExchangeDetail detail = new PromotionLuckDrawExchangeDetail();
+        detail.setName(promotionLuckDraw.getName());
+        detail.setCouponName(promotionLuckDrawCoupon.getCouponName());
+        detail.setStartTime(promotionLuckDraw.getStartTime());
+        detail.setEndTime(promotionLuckDraw.getEndTime());
+        detail.setUseStartTime(promotionLuckDraw.getCouponStartTime());
+        detail.setUseEndTime(promotionLuckDraw.getCouponEndTime());
+        detail.setUseRemark(promotionLuckDrawCoupon.getUseRemark());
+        detail.setRemark(promotionLuckDraw.getRemark());
+        detail.setStatus(promotionLuckDrawCouponCode.getStatus());
+        detail.setUse(StringUtils.isNotEmpty(promotionLuckDrawCouponCode.getOrderId()));
+        detail.setUseTime(promotionLuckDrawCouponCode.getUserUseTime());
+        detail.setUserExchangeTime(promotionLuckDrawCouponCode.getUserExchangeTime());
+        detail.setOrderLimitAmount(promotionLuckDrawCoupon.getOrderLimitAmount());
+        detail.setCouponType(promotionLuckDrawCoupon.getCouponType());
+        return detail;
+    }
+
+
+    /**
+     * 回写抽奖活动
+     */
+    public void updatePromotionLuckDrawCode(OrderInfo orderInfo) {
+        try {
+            String userCouponId = orderInfo.getUserCouponId();
+            if (StringUtils.isEmpty(userCouponId)) {
+                return;
+            }
+            PromotionLuckDrawCouponCode code = promotionLuckDrawCouponCodeService.lambdaQuery().eq(PromotionLuckDrawCouponCode::getUserCouponId, userCouponId).one();
+            if (code == null) {
+                return;
+            }
+            code.setUserUseTime(orderInfo.getPayTime());
+            code.setOrderId(orderInfo.getOrderId());
+            code.setUserNickName(orderInfo.getUserName());
+            code.updateById();
+        } catch (Exception e) {
+            log.error("【订单支付回写抽奖活动信息失败】", e);
+        }
+    }
+
+
+}

+ 91 - 30
src/main/java/com/gree/mall/contest/logic/common/CommonLogic.java

@@ -2,6 +2,9 @@ package com.gree.mall.contest.logic.common;
 
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.ListUtil;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
 import com.gree.mall.contest.bean.admin.AdminUserCom;
 import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
 import com.gree.mall.contest.commonmapper.AdminMapper;
@@ -14,11 +17,11 @@ import com.gree.mall.contest.utils.ApplicationContextUtils;
 import com.gree.mall.contest.utils.CommonUtils;
 import com.gree.mall.contest.utils.RedisUtil;
 import com.gree.mall.contest.utils.oss.OSSUtil;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.time.DateUtils;
 import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
@@ -33,39 +36,29 @@ import java.util.stream.Collectors;
 
 @Service
 @Slf4j
+@RequiredArgsConstructor
 public class CommonLogic {
 
     @Value("${spring.profiles.active}")
     private String active;
-    @Autowired
-    WechatLogic wechatLogic;
-    @Autowired
-    RedisUtil redisUtil;
-    @Autowired
-    AdminCompanyWechatService AdminCompanyWechatService;
-    @Autowired
-    CommonFileService commonFileService;
-    //    @Autowired
-//    RegionService regionService;
-    @Autowired
-    AdminUserService adminUserService;
-    @Autowired
-    AdminCompanyWechatService adminCompanyWechatService;
-    @Autowired
-    AdminMapper adminMapper;
-    @Autowired
-    AdminWebsitService adminWebsitService;
-    @Autowired
-    AdminWebsitRegionService adminWebsitRegionService;
-    @Autowired
-    UserService userService;
-    @Autowired
-    OSSUtil ossUtil;
-    @Autowired
-    SysConfigService sysConfigService;
-
-    @Autowired
-    LbsAmapService lbsAmapService;
+    private final WechatLogic wechatLogic;
+    private final RedisUtil redisUtil;
+    private final AdminCompanyWechatService AdminCompanyWechatService;
+    private final CommonFileService commonFileService;
+    private final AdminUserService adminUserService;
+    private final AdminCompanyWechatService adminCompanyWechatService;
+    private final AdminMapper adminMapper;
+    private final AdminWebsitService adminWebsitService;
+    private final AdminWebsitRegionService adminWebsitRegionService;
+    private final UserService userService;
+    private final OSSUtil ossUtil;
+    private final SysConfigService sysConfigService;
+    private final WxSceneService wxSceneService;
+
+    private final LbsAmapService lbsAmapService;
+    private final WxSceneDetailService wxSceneDetailService;
+    //高德的webkey
+    private final String mapWebKey = "69cebe7cded9307a588237b0618856c9";
 
     public Map<String, String> getOSSConfig() throws UnsupportedEncodingException {
         return ossUtil.getConfig();
@@ -371,4 +364,72 @@ public class CommonLogic {
         return currentCompanyWechat;
 
     }
+
+    /**
+     * 查询街道列表
+     */
+    public List<Map> street(String province, String city, String area) {
+        List<Map> street = getStreet(province, area);
+        if (street.size() == 0) {
+            Map map = new HashMap();
+            map.put("name", area);
+            street.add(map);
+        }
+        return street;
+    }
+
+    /**
+     * 获取高德行政地址
+     */
+    public List<Map> getStreet(String province, String area) {
+        try {
+            String s = HttpUtil.get("https://restapi.amap.com/v3/config/district?keywords=" + province + "&subdistrict=1&key=" + mapWebKey);
+            JSONObject jsonObject = JSONUtil.parseObj(s);
+            String status = (String) jsonObject.get("status");
+            if (status.equals("1")) {
+                List<Object> districts = (List<Object>) jsonObject.get("districts");
+                String adcode = (String) ((Map<String, Object>) districts.get(0)).get("adcode");
+
+                String street = HttpUtil.get("https://restapi.amap.com/v3/config/district?keywords=" + area + "&subdistrict=1&key=" + mapWebKey + "&adcode=" + adcode);
+                Object districts1 = JSONUtil.parseObj(street).get("districts");
+                List<Map> maps = JSONUtil.toList(JSONUtil.toJsonStr(districts1), Map.class);
+
+                List<Map> districts2 = JSONUtil.toList(JSONUtil.toJsonStr(maps.get(0).get("districts")), Map.class);
+                return districts2;
+            }
+        } catch (Exception e) {
+            return new ArrayList<>();
+        }
+        return new ArrayList<>();
+    }
+
+    /**
+     * 微信scene
+     */
+    public String getSceneValue(String wxSceneId) {
+        WxScene wxScene = this.getScene(wxSceneId);
+        return wxScene == null ? "" : wxScene.getParam();
+    }
+
+    /**
+     * 微信scene
+     */
+    public WxScene getScene(String wxSceneId) {
+        WxScene wxScene = wxSceneService.getById(wxSceneId);
+        if (!wxScene.getStatus()) {
+            throw new RemoteServiceException("链接无效");
+        }
+        if (wxScene.getEndTime().getTime() < new Date().getTime()) {
+            throw new RemoteServiceException("链接已过期");
+        }
+        return wxScene;
+    }
+
+    /**
+     * 微信scene详情
+     */
+    public List<WxSceneDetail> getSceneDetail(String wxSceneId){
+        List<WxSceneDetail> list = wxSceneDetailService.lambdaQuery().eq(WxSceneDetail::getWxSceneId, wxSceneId).list();
+        return list;
+    }
 }

+ 230 - 43
src/main/java/com/gree/mall/contest/logic/common/WechatLogic.java

@@ -3,22 +3,32 @@ package com.gree.mall.contest.logic.common;
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONUtil;
 import com.github.binarywang.wxpay.bean.profitsharing.*;
 import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
 import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
+import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.ProfitSharingService;
 import com.github.binarywang.wxpay.service.WxPayService;
 import com.gree.mall.contest.bean.common.WechatOpenBean;
+import com.gree.mall.contest.bean.goods.CutGoodsBean;
+import com.gree.mall.contest.bean.pay.PayDetail;
 import com.gree.mall.contest.config.wx.WxConfiguration;
 import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.enums.QrCodeEnum;
 import com.gree.mall.contest.exception.RemoteServiceException;
 import com.gree.mall.contest.plus.entity.OrderShare;
 import com.gree.mall.contest.plus.entity.WxScene;
+import com.gree.mall.contest.plus.entity.WxSceneDetail;
 import com.gree.mall.contest.plus.service.WxSceneService;
+import com.gree.mall.contest.utils.AESUtil;
 import com.gree.mall.contest.utils.ArithUtils;
+import com.gree.mall.contest.utils.MD5Utils;
+import com.gree.mall.contest.utils.Utils;
 import com.gree.mall.contest.utils.oss.OSSUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -37,9 +47,15 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.protocol.HTTP;
 import org.apache.http.util.EntityUtils;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -63,39 +79,49 @@ public class WechatLogic {
     private final OSSUtil ossUtil;
     private final WxSceneService wxSceneService;
 
-    public WxPayService getPayService(String companyWechatId){
+    public WxPayService getPayService(String companyWechatId) {
         return WxConfiguration.wxPayServices.get(companyWechatId);
     }
+
     /**
      * 微信小程序服务
+     *
      * @return
      */
-    public WxMaService getMaService(){
+    public WxMaService getMaService() {
         return this.getMaService("1");
     }
-    public WxMaService getMaService(String companyWechatId){
+
+    public WxMaService getMaService(String companyWechatId) {
         return WxConfiguration.wxMaService.get(companyWechatId);
     }
+
     /**
      * 微信公众号服务
+     *
      * @return
      */
-    public WxMpService getMpService(){
+    public WxMpService getMpService() {
         return getMpService("1");
     }
-    public WxMpService getMpService(String companyWechatId){
+
+    public WxMpService getMpService(String companyWechatId) {
         return WxConfiguration.wxMpServices.get(companyWechatId);
     }
+
     /**
      * 微信公众号服务接收推送消息
+     *
      * @return
      */
-    public WxMpMessageRouter getMpMessage(){
+    public WxMpMessageRouter getMpMessage() {
         return getMpMessage("1");
     }
-    public WxMpMessageRouter getMpMessage(String companyWechatId){
+
+    public WxMpMessageRouter getMpMessage(String companyWechatId) {
         return WxConfiguration.wxMpMessageRouters.get(companyWechatId);
     }
+
     /**
      * 获取微信token
      *
@@ -112,6 +138,7 @@ public class WechatLogic {
 
     /**
      * 小程序授权openid
+     *
      * @param code
      * @return
      * @throws RemoteServiceException
@@ -126,52 +153,51 @@ public class WechatLogic {
             bean.setSessionKey(wxMaJscode2SessionResult.getSessionKey());
             return bean;
         } catch (WxErrorException e) {
-            log.error("【微信授权失败】",e);
+            log.error("【微信授权失败】", e);
             throw new RemoteServiceException("授权失败,请联系相关人员");
         }
     }
 
     /**
      * 授权手机号
+     *
      * @param phoneCode
      * @return
      */
-    public String authMobile(String phoneCode,String appid){
+    public String authMobile(String phoneCode, String appid) {
         String phoneNumber = null;
         try {
 
             phoneNumber = this.getMaService(appid).getUserService().getPhoneNoInfo(phoneCode).getPhoneNumber();
         } catch (WxErrorException e) {
-            log.error("【微信授权手机号失败】",e);
+            log.error("【微信授权手机号失败】", e);
             throw new RemoteServiceException("授权手机号失败,请联系相关人员");
         }
-        return phoneNumber ;
+        return phoneNumber;
     }
 
 
-
-
     /**
      * 获取微信二维码
-     * @param type 二维码类型
-     * @param objId 商品id/券id 之类的
-     * @param userId 生成二维码的用户id
+     *
+     * @param type            二维码类型
+     * @param objId           商品id/券id 之类的
+     * @param userId          生成二维码的用户id
      * @param companyWechatId 商户id
      * @return
      */
-    public String getQrcode(String type,String objId,String userId,String companyWechatId){
+    public String getQrcode(String type, String objId, String userId, String companyWechatId) {
         String param = type + "&" + objId + "&" + userId;
         WxScene wxScene = new WxScene();
         wxScene.setParam(param);
         wxScene.setType(type);
         wxScene.setCreateTime(new Date());
         wxSceneService.save(wxScene);
-        return this.genQrCode(wxScene.getSceneId(),companyWechatId);
+        return this.genQrCode(wxScene.getSceneId(), companyWechatId);
     }
 
 
-
-    private String genQrCode(String sceneId,String companyWechatId){
+    private String genQrCode(String sceneId, String companyWechatId) {
         try {
 
             String token = this.getAccessToken();
@@ -204,46 +230,46 @@ public class WechatLogic {
 
             //String baseStr = Base64.getEncoder().encodeToString(bos.toByteArray());
             long currentTime = System.currentTimeMillis();
-            String filePath = ossUtil.getFilePath()+"/"+ currentTime + UUID.randomUUID().toString() + ".png";
+            String filePath = ossUtil.getFilePath() + "/" + currentTime + UUID.randomUUID().toString() + ".png";
             ossUtil.uploadFile(filePath, bos.toByteArray());
 
             return ossUtil.getAccessUrl() + filePath;
-        }catch (Exception e){
-            log.error("获取微信二维码失败:",e);
+        } catch (Exception e) {
+            log.error("获取微信二维码失败:", e);
         }
         return null;
     }
 
 
-    public String getUrlScheme(String query,String companyWechatId) throws IOException, WxErrorException {
-        if(StringUtils.isBlank(companyWechatId)){
+    public String getUrlScheme(String query, String companyWechatId) throws IOException, WxErrorException {
+        if (StringUtils.isBlank(companyWechatId)) {
             companyWechatId = Constant.GD_COMPANY_WECHAT_ID;
         }
         String accessToken = this.getMaService(companyWechatId).getAccessToken();
-        HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/generatescheme?access_token="+accessToken);
-        httpPost.setHeader("Content-Type","application/json; charset=utf-8");
-        httpPost.setConfig(RequestConfig.custom().setConnectTimeout(10*1000).build());
+        HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/generatescheme?access_token=" + accessToken);
+        httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
+        httpPost.setConfig(RequestConfig.custom().setConnectTimeout(10 * 1000).build());
 
-        TreeMap<String,String>  jump_wxa=new TreeMap<>();
-        jump_wxa.put("query",query);
+        TreeMap<String, String> jump_wxa = new TreeMap<>();
+        jump_wxa.put("query", query);
 
-        TreeMap<String,Object> treeMap=new TreeMap<>();
-        treeMap.put("jump_wxa",jump_wxa);
+        TreeMap<String, Object> treeMap = new TreeMap<>();
+        treeMap.put("jump_wxa", jump_wxa);
         httpPost.setEntity(new StringEntity(JSONUtil.toJsonStr(treeMap)));
 
         HttpResponse httpResponse = HttpClientBuilder.create().build().execute(httpPost);
         String s = EntityUtils.toString(httpResponse.getEntity());
-        if(httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK){
-            log.info("调用微信接口失败:"+httpPost.getURI());
-            log.info("返回数据:"+s);
+        if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+            log.info("调用微信接口失败:" + httpPost.getURI());
+            log.info("返回数据:" + s);
             throw new RuntimeException("调用微信接口失败");
         }
-        log.info("调用微信接口成功:"+httpPost.getURI());
-        log.info("微信接口返回数据:"+s);
+        log.info("调用微信接口成功:" + httpPost.getURI());
+        log.info("微信接口返回数据:" + s);
 
-        try{
+        try {
             return JSONUtil.parseObj(s).get("openlink").toString();
-        }catch (NullPointerException e){
+        } catch (NullPointerException e) {
             return s;
         }
     }
@@ -331,7 +357,7 @@ public class WechatLogic {
         return profitSharingQueryResult;
     }
 
-    private boolean addShareReveiver(String openId,String companyWechatId) {
+    private boolean addShareReveiver(String openId, String companyWechatId) {
         Map<String, Object> receiver = new HashMap<>();
         /**
          *  MERCHANT_ID:商户号
@@ -382,7 +408,7 @@ public class WechatLogic {
     /**
      * 服务商退款
      */
-    public void refund(String orderId, String refundNo, Double totalFee, Double refundFee,String companyWechatId) throws  RemoteServiceException {
+    public void refund(String orderId, String refundNo, Double totalFee, Double refundFee, String companyWechatId) throws RemoteServiceException {
         WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
         wxPayRefundRequest.setOutTradeNo(orderId);
         wxPayRefundRequest.setOutRefundNo(refundNo);
@@ -399,10 +425,171 @@ public class WechatLogic {
             if (!StringUtils.equals(refund.getReturnCode(), "SUCCESS")) {
                 throw new RemoteServiceException(refund.getReturnCode() + refund.getReturnMsg());
             }
-        }catch (WxPayException ex){
-            throw new RemoteServiceException("退款失败:"+ex.getMessage());
+        } catch (WxPayException ex) {
+            throw new RemoteServiceException("退款失败:" + ex.getMessage());
+        }
+
+
+    }
+
+    //xml解析
+    public static Map doXMLParse(String strxml) throws JDOMException, IOException {
+        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
+        if (null == strxml || "".equals(strxml)) {
+            return null;
+        }
+
+        Map m = new HashMap();
+        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
+        SAXBuilder builder = new SAXBuilder();
+        Document doc = builder.build(in);
+        Element root = doc.getRootElement();
+        List list = root.getChildren();
+        for (Object aList : list) {
+            Element e = (Element) aList;
+            String k = e.getName();
+            String v = "";
+            List children = e.getChildren();
+            if (children.isEmpty()) {
+                v = e.getTextNormalize();
+            } else {
+                v = getChildrenText(children);
+            }
+            m.put(k, v);
+        }
+        //关闭流
+        in.close();
+        return m;
+    }
+
+    private static String getChildrenText(List children) {
+        StringBuilder sb = new StringBuilder();
+        if (!children.isEmpty()) {
+            for (Object aChildren : children) {
+                Element e = (Element) aChildren;
+                String name = e.getName();
+                String value = e.getTextNormalize();
+                List list = e.getChildren();
+                sb.append("<").append(name).append(">");
+                if (!list.isEmpty()) {
+                    sb.append(getChildrenText(list));
+                }
+                sb.append(value);
+                sb.append("</").append(name).append(">");
+            }
         }
 
+        return sb.toString();
+    }
+
+    /**
+     * 退款回调加密串解密(使用该解密,需要jdk支持AES256)
+     *
+     * @param reqInfo 微信退款回调返回的加密串
+     * @return
+     * @throws Exception
+     */
+    public Map refundAESStr(String reqInfo) throws Exception {
+        String s = AESUtil.decryptData(reqInfo);
+        Map map = doXMLParse(s);
+        return map;
+    }
+
+    /**
+     * 获取让利二维码
+     */
+    @Transactional
+    public String getCutQrcode(String goodsId, String userId, String companyWechatId, Boolean qrcode, String secKillActivityId, List<CutGoodsBean> cutGoodsBeans) {
+        String type = QrCodeEnum.CUT.toString().toLowerCase();
+        String param = type + "&" + goodsId + "&" + userId;
+        WxScene wxScene = new WxScene();
+        wxScene.setParam(param);
+        wxScene.setType(type);
+        wxScene.setCreateTime(new Date());
+        wxScene.setEndTime(DateUtil.endOfDay(DateUtil.offsetDay(DateUtil.date(), 1)));
+        wxScene.setSecKillActivityId(secKillActivityId);
+        wxScene.insert();
+        //添加参数详情
+        for (CutGoodsBean cutGoodsBean : cutGoodsBeans) {
+            //检查合法性
+            WxSceneDetail wxSceneDetail = new WxSceneDetail();
+            wxSceneDetail.setWxSceneId(wxScene.getSceneId());
+            wxSceneDetail.setGoodsId(cutGoodsBean.getGoodsId());
+            wxSceneDetail.setGoodsSpecId(cutGoodsBean.getGoodsSpecId());
+            wxSceneDetail.setCutAmount(cutGoodsBean.getCutAmount());
+            wxSceneDetail.setCreateTime(new Date());
+            wxSceneDetail.insert();
+        }
+
+        if (qrcode) {
+            return this.genQrCode(wxScene.getSceneId(), companyWechatId);
+        }
+        return wxScene.getSceneId();
+    }
+
+    /**
+     * 微信支付 JSAPI
+     *
+     * @param id
+     * @param payment
+     * @param openId
+     * @return
+     * @throws Exception
+     */
+    public PayDetail payment(String id, BigDecimal payment, String openId, String profitSharingFlag, String ip, String companyWechatId) throws Exception {
+
+        if (payment.doubleValue() <= 0) {
+            //throw new RemoteServiceException("支付金额小于等于0,实际支付金额:"+payment);
+            PayDetail payDetail = new PayDetail();
+            payDetail.setIsPay(false);
+            payDetail.setId(id);
+            return payDetail;
+        }
+
+        WxPayService payService = this.getPayService(companyWechatId);
+
+        WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
+        wxPayUnifiedOrderRequest.setBody("商城支付费用");
+        wxPayUnifiedOrderRequest.setTotalFee((int) ArithUtils.mul(payment.doubleValue(), 100));
+        wxPayUnifiedOrderRequest.setSubOpenid(openId);
+        wxPayUnifiedOrderRequest.setNonceStr(IdUtil.simpleUUID());
+        wxPayUnifiedOrderRequest.setOutTradeNo(id);
+        wxPayUnifiedOrderRequest.setTradeType("JSAPI");
+        wxPayUnifiedOrderRequest.setNotifyUrl(payService.getConfig().getNotifyUrl());
+        wxPayUnifiedOrderRequest.setSpbillCreateIp(ip);
+        wxPayUnifiedOrderRequest.setProfitSharing(profitSharingFlag);
+        WxPayUnifiedOrderResult wxPayUnifiedOrderResult = payService.unifiedOrder(wxPayUnifiedOrderRequest);
+        log.info("服务商调起支付result:{}", JSONUtil.toJsonStr(wxPayUnifiedOrderResult));
+        String returnCode = wxPayUnifiedOrderResult.getReturnCode();
+        if (!StringUtils.equals(returnCode, "SUCCESS")) {
+            throw new RemoteServiceException("微信调起支付失败:" + wxPayUnifiedOrderResult.getReturnMsg());
+        }
+
+        //二次签名
+        String time = System.currentTimeMillis() / 1000 + "";
+        String prepayId = "prepay_id=" + wxPayUnifiedOrderResult.getPrepayId();
+        String nonceStr = wxPayUnifiedOrderResult.getNonceStr();
+        String twoSign = this.getTwoSign(prepayId, nonceStr, time, payService.getConfig().getSubAppId(), payService.getConfig().getMchKey());
+        PayDetail payDetail = new PayDetail();
+        payDetail.setPayPackage(prepayId);
+        payDetail.setPaySign(twoSign);
+        payDetail.setNonceStr(wxPayUnifiedOrderResult.getNonceStr());
+        payDetail.setTimeStamp(time);
+        payDetail.setId(id);
+        log.info("payDetail:{}", JSONUtil.toJsonStr(payDetail));
+        return payDetail;
+    }
 
+    //二次签名
+    public String getTwoSign(String payPackage, String nonceStr, String time,String subAppId,String mchKey) {
+        Map<String, String> map = new HashMap<>();
+        map.put("appId",subAppId);
+        map.put("timeStamp", time);
+        map.put("nonceStr", nonceStr);
+        map.put("package", payPackage);
+        map.put("signType", "MD5");
+        String stringA = Utils.formatUrlMap(map, false, false);
+        String sign = MD5Utils.md5(stringA + "&key=" +mchKey).toUpperCase();
+        return sign;
     }
 }

+ 452 - 80
src/main/java/com/gree/mall/contest/logic/coupon/CouponLogic.java

@@ -2,12 +2,19 @@ package com.gree.mall.contest.logic.coupon;
 
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.gree.mall.contest.bean.ExcelData;
 import com.gree.mall.contest.bean.admin.AdminUserCom;
 import com.gree.mall.contest.bean.coupon.*;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
+import com.gree.mall.contest.bean.user.UserCouponCountBean;
 import com.gree.mall.contest.commonmapper.CouponDateMapper;
+import com.gree.mall.contest.commonmapper.CoustomUserCouponMapper;
+import com.gree.mall.contest.commonmapper.CustomCoupouMapper;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.enums.QrCodeEnum;
 import com.gree.mall.contest.enums.UserTypeEnum;
 import com.gree.mall.contest.enums.coupon.CouponActiveTypeEnum;
 import com.gree.mall.contest.enums.coupon.CouponCrowdEnum;
@@ -15,20 +22,28 @@ import com.gree.mall.contest.enums.coupon.CouponFlagEnum;
 import com.gree.mall.contest.enums.coupon.CouponTypeEnum;
 import com.gree.mall.contest.exception.RemoteServiceException;
 import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.logic.common.WechatLogic;
+import com.gree.mall.contest.logic.user.UserLogic;
 import com.gree.mall.contest.plus.entity.*;
 import com.gree.mall.contest.plus.service.*;
+import com.gree.mall.contest.utils.CommonUtils;
 import com.gree.mall.contest.utils.excel.ExcelUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
+import org.springframework.integration.redis.util.RedisLockRegistry;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.stream.Collectors;
 
 @Service
 @Slf4j
@@ -42,6 +57,11 @@ public class CouponLogic {
     private final CouponUserService couponUserService;
     private final UserService userService;
     private final CommonLogic commonLogic;
+    private final RedisLockRegistry redisLockRegistry;
+    private final CustomCoupouMapper customCoupouMapper;
+    private final WechatLogic wechatLogic;
+    private final UserLogic userLogic;
+    private final CoustomUserCouponMapper coustomUserCouponMapper;
 
     /**
      * 优惠券列表
@@ -531,88 +551,440 @@ public class CouponLogic {
         }
     }
 
+    /**
+     * 优惠券过期提醒
+     */
+    public Boolean timeout(String userId) {
+        Date timeByDay = DateUtil.offsetDay(new Date(), -3);
+        Long count = userCouponService.lambdaQuery().eq(UserCoupon::getUserId, userId)
+                .between(UserCoupon::getActiveEndTime, timeByDay, new Date()).count();
+        return count > 0;
+    }
 
     /**
-     * 导入楼小贤优惠券
+     * 统计 未使用 ,已使用,已过期优惠券
+     *
+     * @param userId
+     * @return
      */
-//    @Transactional
-//    public void importLxx(MultipartFile file) throws IOException, ParseException {
-//        List<Object> datas = ExcelUtils.importExcel(file);
-//        int i = 1;
-//
-//        List<Coupon> couponList = new ArrayList<>();
-//        List<CouponUser> couponUserList = new ArrayList<>();
-//        List<UserCoupon> userCouponList = new ArrayList<>();
-//        for (Object o : datas) {
-//            String companyWechatId = "1";
-//            String companyName = "嘉讯茂商城";
-//            i++;
-//            List<Object> row = (List<Object>) o;
-//            String couponName = (String) row.get(0);
-//            String mobile = (String) row.get(1);
-//            BigDecimal amount = new BigDecimal(row.get(2).toString());
-//            Date startTime = DateUtils.parseDate(row.get(3).toString());
-//            Date endTime = DateUtils.parseDate(row.get(4).toString());
-//            if (StringUtils.isEmpty(couponName) || StringUtils.isEmpty(mobile) || amount == null || startTime == null || endTime == null) {
-//                throw new RemoteServiceException("请检查第" + i + "行数据是否完整");
-//            }
-//            User user = userService.lambdaQuery().eq(User::getMobile, mobile).eq(User::getCompanyWechatId, companyWechatId).one();
-//            if (user == null)
-//                throw new RemoteServiceException("手机号为" + mobile + "的找不到对应用户");
-//
-//            //生成优惠券
-//            String couponId = "C" + com.baomidou.mybatisplus.core.toolkit.IdWorker.getIdStr();
-//            Coupon coupon = new Coupon();
-//            coupon.setCouponId(couponId);
-//            coupon.setCouponName(couponName);
-//            coupon.setCouponType(CouponTypeEnum.SATISFY.toString());
-//            coupon.setActiveStartTime(startTime);
-//            coupon.setActiveEndTime(endTime);
-//            coupon.setReceiveCrowd(2);
-//            coupon.setCouponValue(amount);
-//            coupon.setObtainStartTime(startTime);
-//            coupon.setObtainEndTime(endTime);
-//            coupon.setDisplayTime(startTime);
-//            coupon.setFlag("WAIT");
-//            coupon.setRemark(mobile);
-//            coupon.setShareTimes(1);
-//            coupon.setCompanyWechatId(companyWechatId);
-//            coupon.setCompanyName(companyName);
-//            couponList.add(coupon);
-//
-//            CouponUser couponUser = new CouponUser();
-//            couponUser.setCouponId(couponId);
-//            couponUser.setUserId(user.getUserId());
-//            couponUser.setUserName(user.getNickName());
-//            couponUser.setCreateTime(new Date());
-//            couponUser.setCompanyWechatId(companyWechatId);
-//            couponUser.setCompanyName(companyName);
-//            couponUserList.add(couponUser);
-//
-//            //发到师傅手上的券
-//            UserCoupon userCoupon = new UserCoupon();
-//            userCoupon.setUserId(user.getUserId());
-//            userCoupon.setCouponId(couponId);
-//            userCoupon.setCouponName(couponName);
-//            userCoupon.setCouponValue(coupon.getCouponValue());
-//            userCoupon.setCouponType(coupon.getCouponType());
-//            userCoupon.setStatus(false);
-//            userCoupon.setState(true);
-//            userCoupon.setReceiveTime(new Date());
-//            userCoupon.setActiveStartTime(startTime);
-//            userCoupon.setActiveEndTime(endTime);
-//            userCoupon.setCompanyWechatId(companyWechatId);
-//            userCoupon.setCompanyName(companyName);
-//            userCoupon.setTransferType(false);
-//            userCoupon.setLeftShareTimes(1);
-//            userCouponList.add(userCoupon);
-//        }
-//        //新增券
-//        couponService.saveBatch(couponList);
-//        //新增券和人对应关系
-//        couponUserService.saveBatch(couponUserList);
-//        //发券
-//        userCouponService.saveBatch(userCouponList);
-//    }
+    public UserCouponCountBean count(String userId) {
+        UserCouponCountBean userCouponCountBean = new UserCouponCountBean();
+        List<UserCoupon> list = userCouponService.lambdaQuery().eq(UserCoupon::getUserId, userId).list();
+        if (list.size() == 0) {
+            return userCouponCountBean;
+        }
+        //未使用数量,已使用数量,已过期数量
+        int wsy = 0, ysy = 0, ygq = 0;
+        for (UserCoupon userCoupon : list) {
+
+            if (userCoupon.getStatus()) {
+                ysy++;
+            } else {
+                if (userCoupon.getActiveEndTime().getTime() > new Date().getTime()) {
+                    wsy++;
+                } else {
+                    ygq++;
+                }
+            }
+        }
+        userCouponCountBean.setWsy(wsy);
+        userCouponCountBean.setYsy(ysy);
+        userCouponCountBean.setYgq(ygq);
+        return userCouponCountBean;
+    }
+
+    /**
+     * 我的优惠券
+     *
+     * @param userId
+     * @param pageNum
+     * @param pageSize
+     * @return
+     */
+    public IPage<UserCoupon> miniPage(String userId, Integer type, Integer pageNum, Integer pageSize) {
 
+        Date date = new Date();
+
+        LambdaQueryWrapper<UserCoupon> qw = new LambdaQueryWrapper<>();
+        qw.eq(UserCoupon::getUserId, userId);
+
+        //未使用
+        if (type != null && type == 0) {
+            qw.eq(UserCoupon::getStatus, type).ge(UserCoupon::getActiveEndTime, date);
+        }
+        //已使用
+        if (type != null && type == 1) {
+            qw.eq(UserCoupon::getStatus, type);
+        }
+        //已过期
+        if (type != null && type == 2) {
+            qw.eq(UserCoupon::getStatus, 0);
+            qw.lt(UserCoupon::getActiveEndTime, date);
+        }
+        qw.orderByDesc(UserCoupon::getReceiveTime);
+        IPage<UserCoupon> page = userCouponService.page(new Page<>(pageNum, pageSize), qw);
+        return page;
+    }
+
+    public List<UserCoupon> expiring(String userId) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(new Date());
+        cal.add(Calendar.DATE, 1);
+        return userCouponService.lambdaQuery()
+                .eq(UserCoupon::getUserId, userId)
+                .le(UserCoupon::getActiveEndTime, cal.getTime())
+                .list();
+    }
+
+    public List<CouponObtainBean> allObtainCoupou(String userId, HttpServletRequest request) {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        List<CouponObtainBean> couponObtainBeans = customCoupouMapper.listObtainCoupou(userId, currentCompanyWechat.getCompanyWechatId());
+        //发券
+        try {
+            List<CouponObtainBean> obtions = couponObtainBeans.stream().filter(v -> v.getObtainType() == 1).collect(Collectors.toList());
+            if (obtions.size() > 0) {
+                List<String> couponIds = obtions.stream().map(CouponObtainBean::getCouponId).collect(Collectors.toList());
+                for (String couponId : couponIds) {
+                    Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_COUPON + couponId);
+                    if (!obtain.tryLock(10, TimeUnit.SECONDS)) {
+                        log.error("系统繁忙,领券失败,userId:{},couponId:{}", userId, couponId);
+                        continue;
+                    }
+                    try {
+                        this.obtainCoupou(request, userId, couponId);
+                    } finally {
+                        obtain.unlock();
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.info("【优惠券发放失败】", e);
+        }
+        return couponObtainBeans;
+    }
+
+    /**
+     * 领取优惠券
+     *
+     * @param userId
+     * @throws RemoteServiceException
+     */
+    public void obtainCoupou(HttpServletRequest request, String userId, String couponId) throws RemoteServiceException, InterruptedException {
+        Coupon coupon = couponService.getById(couponId);
+        //限领张数
+        Integer receiveLimitCount = coupon.getReceiveLimitCount();
+        //剩余可领取数量
+        Integer leftAmount = coupon.getLeftAmount();
+        //已领取数量
+        Integer receiveAmount = coupon.getReceiveAmount() == null ? 0 : coupon.getReceiveAmount();
+        if (leftAmount <= 0) {
+            throw new RemoteServiceException("领取优惠券失败,优惠券已被领取完");
+        }
+        Long count = userCouponService.lambdaQuery()
+                .eq(UserCoupon::getUserId, userId)
+                .eq(UserCoupon::getCouponId, couponId).count();
+        if (receiveLimitCount != 0 && count >= receiveLimitCount) {
+            throw new RemoteServiceException("领取失败,你已领取过券");
+        }
+        //实际领取张数
+        int num = leftAmount - receiveLimitCount < 0 ? 0 : leftAmount - receiveLimitCount;
+        //维护券的剩余数量和领取数量
+        coupon.setLeftAmount(leftAmount - num);
+        coupon.setReceiveAmount(receiveAmount + num);
+        coupon.updateById();
+        User user = userService.getById(userId);
+        //Boolean transferType = user.getType().equals(UserTypeEnum.SERVICE.toString());
+        //新增我的优惠券
+        List<UserCoupon> list = new ArrayList<>();
+        Date now = new Date();
+        for (int i = 0; i < receiveLimitCount; i++) {
+            UserCoupon userCoupon = new UserCoupon();
+            userCoupon.setCouponId(couponId);
+            userCoupon.setUserId(userId);
+            userCoupon.setReceiveTime(now);
+            userCoupon.setCouponName(coupon.getCouponName());
+            userCoupon.setCouponType(coupon.getCouponType());
+            userCoupon.setOrderAmount(coupon.getOrderAmount());
+            userCoupon.setCouponValue(coupon.getCouponValue());
+            userCoupon.setStatus(false);
+            //userCoupon.setTransferType(transferType);
+            if (coupon.getActiveDay() == null) {
+                coupon.setActiveDay(0);
+            }
+            if (coupon.getActiveType().equals(CouponActiveTypeEnum.COMMON.getActiveType())) {
+                userCoupon.setActiveStartTime(coupon.getActiveStartTime());
+                userCoupon.setActiveEndTime(coupon.getActiveEndTime());
+            }
+            //当日起多少天
+            if (coupon.getActiveType().equals(CouponActiveTypeEnum.TODAY.getActiveType())) {
+                userCoupon.setActiveStartTime(DateUtil.beginOfDay(DateUtil.date()));
+                userCoupon.setActiveEndTime(DateUtil.beginOfDay(DateUtil.offsetDay(DateUtil.date(), coupon.getActiveDay())));
+            }
+            //次日起多少天
+            if (coupon.getActiveType().equals(CouponActiveTypeEnum.NEXTDAY.getActiveType())) {
+                userCoupon.setActiveStartTime(DateUtil.beginOfDay(DateUtil.offsetDay(DateUtil.date(), 1)));
+                userCoupon.setActiveEndTime(DateUtil.beginOfDay(DateUtil.offsetDay(DateUtil.date(), coupon.getActiveDay() + 1)));
+            }
+            userCoupon.setState(now.before(coupon.getActiveEndTime()) && now.after(coupon.getActiveStartTime()));
+            userCoupon.setCompanyWechatId(user.getCompanyWechatId());
+            userCoupon.setCompanyName(user.getCompanyName());
+            list.add(userCoupon);
+        }
+        userCouponService.saveBatch(list);
+    }
+
+    public List<CustomCoupouBean> useableCoupou(String userId, BigDecimal orderAmount, List<String> goodsSpecIds) {
+
+        List<CustomCoupouBean> customCoupouBeans = customCoupouMapper.listCoupou(userId, orderAmount, goodsSpecIds);
+        return customCoupouBeans;
+    }
+
+    /**
+     * 券详情
+     *
+     * @return
+     */
+    public UserCoupon miniDetail(String userCouponId) {
+
+        return userCouponService.getById(userCouponId);
+    }
+
+
+    public CouponQrcodeBean qrcode(String userCouponId) throws RemoteServiceException {
+
+        UserCoupon userCoupon = userCouponService.lambdaQuery()
+                .eq(UserCoupon::getId, userCouponId)
+                .eq(UserCoupon::getStatus, false)
+                .eq(UserCoupon::getTransferType, true)
+                .ge(UserCoupon::getActiveEndTime, new Date())
+                .one();
+        if (userCoupon == null) {
+            throw new RemoteServiceException("优惠券不可转赠");
+        }
+        String qrcode = userCoupon.getTransferQrcode();
+        if (StringUtils.isEmpty(qrcode)) {
+            qrcode = wechatLogic.getQrcode(QrCodeEnum.COUPON.toString().toLowerCase(Locale.ROOT), userCoupon.getId(), userCoupon.getUserId(), userCoupon.getCompanyWechatId());
+            if (StringUtils.isNotEmpty(qrcode)) {
+                userCoupon.setTransferQrcode(qrcode);
+                userCouponService.updateById(userCoupon);
+            }
+        }
+        Coupon coupon = couponService.getById(userCoupon.getCouponId());
+        CouponQrcodeBean couponQrcodeBean = new CouponQrcodeBean();
+        couponQrcodeBean.setQrcode(qrcode);
+        couponQrcodeBean.setLeftShareTimes(userCoupon.getLeftShareTimes());
+        couponQrcodeBean.setShareTimes(coupon.getShareTimes());
+        return couponQrcodeBean;
+    }
+
+    @Transactional
+    public String transferCoupon(HttpServletRequest request, String userCouponId) throws RemoteServiceException {
+        UserCoupon userCoupon = userCouponService.lambdaQuery()
+                .eq(UserCoupon::getId, userCouponId)
+                .eq(UserCoupon::getStatus, false)
+                .eq(UserCoupon::getTransferType, true)
+                .ge(UserCoupon::getActiveEndTime, new Date())
+                .one();
+        if (userCoupon == null || userCoupon.getLeftShareTimes() < 1) {
+            throw new RemoteServiceException("优惠券已被领完");
+        }
+        String userId = CommonUtils.getUserId(request);
+        if (userId.equals(userCoupon.getUserId())) {
+            throw new RemoteServiceException("不能转赠自己");
+        }
+        User user = userService.lambdaQuery()
+                .eq(User::getUserId, userId)
+                .eq(User::getStatus, true)
+                .one();
+        if (user == null) {
+            throw new RemoteServiceException("用户不存在");
+        }
+        if (user.getType().equals(UserTypeEnum.SERVICE.toString())) {
+            throw new RemoteServiceException("业务员不能领取");
+        }
+
+        Long count = userCouponService.lambdaQuery()
+                .eq(UserCoupon::getUserId, userId)
+                .eq(UserCoupon::getCouponId, userCoupon.getCouponId())
+                .count();
+
+        if (count > 0) {
+            throw new RemoteServiceException("领取失败,领取优惠券超过限制数量");
+        }
+
+
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_COUPON_TRANSFER + userCoupon.getId());
+        String transferCouponId;
+        try {
+            if (!obtain.tryLock(10, TimeUnit.SECONDS)) {
+                throw new RemoteServiceException("系统繁忙,请稍后再试");
+            }
+            UserCoupon reObtainCoupon = this.miniDetail(userCouponId);
+            if (reObtainCoupon.getLeftShareTimes() < 1) {
+                throw new RemoteServiceException("优惠已被用户使用");
+            }
+
+            transferCouponId = this.transfer(user.getUserId(), userCouponId, user.getCompanyWechatId(), user.getCompanyName());
+        } catch (InterruptedException e) {
+            log.error("", e);
+            throw new RemoteServiceException("系统繁忙,请稍后再试");
+        } finally {
+            obtain.unlock();
+        }
+        return transferCouponId;
+    }
+
+    protected String transfer(String userId, String userCouponId, String companyWechatId, String companyName) {
+        UserCoupon userCoupon = userCouponService.lambdaQuery()
+                .eq(UserCoupon::getId, userCouponId)
+                .eq(UserCoupon::getStatus, false)
+                .eq(UserCoupon::getTransferType, true)
+                .ge(UserCoupon::getActiveEndTime, new Date())
+                .one();
+        String transferCouponId = null;
+        if (userCoupon != null && userCoupon.getLeftShareTimes() > 0) {
+            userLogic.bind(userId, userCoupon.getUserId());
+            coustomUserCouponMapper.decreaseLeftTimes(userCoupon.getId());
+            UserCoupon transferUserCoupon = new UserCoupon();
+            BeanUtils.copyProperties(userCoupon, transferUserCoupon, "id");
+            transferUserCoupon.setId(null);
+            transferUserCoupon.setTransferType(false);
+            transferUserCoupon.setReceiveTime(new Date());
+            transferUserCoupon.setTransferQrcode(null);
+            transferUserCoupon.setTransferor(userCoupon.getId());
+            transferUserCoupon.setLeftShareTimes(0);
+            transferUserCoupon.setUserId(userId);
+            transferUserCoupon.setCompanyWechatId(companyWechatId);
+            transferUserCoupon.setCompanyName(companyName);
+            userCouponService.save(transferUserCoupon);
+            transferCouponId = transferUserCoupon.getId();
+
+            userCoupon.setLeftShareTimes(userCoupon.getLeftShareTimes() - 1);
+            userCoupon.setStatus(userCoupon.getLeftShareTimes() == 0);
+            userCoupon.updateById();
+        }
+        return transferCouponId;
+    }
+
+    /**
+     * 检查优惠券是否可用
+     */
+    public UserCouponBean check(String userCouponId, BigDecimal payment, List<String> goodsIds, Boolean use) throws
+            RemoteServiceException, InterruptedException {
+
+        if (StringUtils.isEmpty(userCouponId)) {
+            return null;
+        }
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_COUPON_CHECK + userCouponId);
+        try {
+            if (!obtain.tryLock(5, TimeUnit.SECONDS)) {
+                throw new RemoteServiceException("系统繁忙,请稍后再试");
+            }
+            //使用了优惠券
+            if (StringUtils.isNotEmpty(userCouponId)) {
+                UserCoupon userCoupon = this.miniDetail(userCouponId);
+
+                UserCouponBean userCouponBean = new UserCouponBean();
+
+                if (!userCoupon.getState()) {
+                    throw new RemoteServiceException("下单失败,优惠券不可用");
+                }
+                if (userCoupon.getStatus()) {
+                    throw new RemoteServiceException("优惠券已使用");
+                }
+                if (userCoupon.getTransferType() && userCoupon.getLeftShareTimes() < 1) {
+                    throw new RemoteServiceException("优惠已被用户使用");
+                }
+
+
+                long curTime = new Date().getTime();
+                if (userCoupon.getActiveStartTime().getTime() > curTime || userCoupon.getActiveEndTime().getTime() < curTime) {
+                    throw new RemoteServiceException("优惠券使用时间范围不符合要求");
+                }
+                if (userCoupon.getCouponType().equals(CouponTypeEnum.SATISFY.toString())) {
+                    log.info("【试用优惠券条件】券试用条件:{},订单金额:{}", userCoupon.getOrderAmount().doubleValue(), payment.doubleValue());
+                    if (userCoupon.getOrderAmount().doubleValue() != 0 && userCoupon.getOrderAmount().doubleValue() > payment.doubleValue()) {
+                        throw new RemoteServiceException("优惠券不符合使用要求");
+                    }
+                } else if (userCoupon.getCouponType().equals(CouponTypeEnum.GOODS.toString())) {
+
+                    List<CouponGoods> couponGoodsList = couponGoodsService.lambdaQuery()
+                            .eq(CouponGoods::getCouponId, userCoupon.getCouponId())
+                            .in(CouponGoods::getGoodsId, goodsIds)
+                            .list();
+                    if (couponGoodsList.size() == 0) {
+                        throw new RemoteServiceException("优惠券不符合使用要求");
+                    }
+                    userCouponBean.setGoodsId(couponGoodsList.get(0).getGoodsId());
+                }
+
+                if (userCoupon.getTransferType()) {
+                    if (userCoupon.getLeftShareTimes() < 1) {
+                        throw new RemoteServiceException("优惠已被用户使用");
+                    }
+                }
+                //将券改为已使用状态
+                UserCoupon userCouponRet = this.cancelCoupon(userCoupon.getId(), use);
+                BeanUtils.copyProperties(userCouponRet, userCouponBean, "goodsId");
+
+                return userCouponBean;
+            }
+        } finally {
+            obtain.unlock();
+        }
+        return null;
+    }
+
+    /**
+     * 核销优惠券,可转增的券全部是可使用状态
+     *
+     * @param userCouponId
+     */
+    @Transactional
+    public UserCoupon cancelCoupon(String userCouponId, Boolean use) {
+        UserCoupon userCoupon = userCouponService.getById(userCouponId);
+        UserCoupon userCouponCopy = null;
+        if (userCoupon != null && userCoupon.getTransferType() != null && userCoupon.getTransferType() && userCoupon.getLeftShareTimes() > 0) {
+            //数量减一,增加一条新的记录
+            if (use) {
+                userCoupon.setLeftShareTimes(userCoupon.getLeftShareTimes() - 1);
+                userCoupon.setStatus(userCoupon.getLeftShareTimes() == 0);
+                userCoupon.updateById();
+            }
+        } else {
+            userCouponService.lambdaUpdate()
+                    .set(UserCoupon::getStatus, use)
+                    .set(UserCoupon::getUsedTime, new Date())
+                    .eq(UserCoupon::getId, userCouponId)
+                    .update();
+        }
+        return userCouponCopy == null ? userCoupon : userCouponCopy;
+    }
+
+    /**
+     * 订单取消,恢复优惠券
+     *
+     * @param userCouponIds 优惠券id数组
+     */
+    @Transactional
+    public void updateStatus(List<String> userCouponIds) {
+        userCouponService.lambdaUpdate()
+                .set(UserCoupon::getStatus, false)
+                .set(UserCoupon::getUsedTime, null)
+                .in(UserCoupon::getId, userCouponIds)
+                .update();
+        for (String userCouponId : userCouponIds) {
+            if (StringUtils.isEmpty(userCouponId)) {
+                continue;
+            }
+            log.info("订单取消,恢复用户优惠券id{}", userCouponId);
+            UserCoupon userCoupon = userCouponService.getById(userCouponId);
+            if(userCoupon == null)
+                continue;
+            if(userCoupon.getTransferType() == null || !userCoupon.getTransferType())
+                continue;
+
+            coustomUserCouponMapper.increaseLeftTimes(userCouponId);
+        }
+    }
 }

+ 70 - 0
src/main/java/com/gree/mall/contest/logic/goods/GoodsFavoriteLogic.java

@@ -0,0 +1,70 @@
+package com.gree.mall.contest.logic.goods;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.goods.GoodsFavoriteBean;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
+import com.gree.mall.contest.commonmapper.CustomGoodsMapper;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.GoodsFavorite;
+import com.gree.mall.contest.plus.service.GoodsFavoriteService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class GoodsFavoriteLogic {
+
+    private final GoodsFavoriteService goodsFavoriteService;
+    private final CustomGoodsMapper customGoodsMapper;
+    private final CommonLogic commonLogic;
+
+    public GoodsFavorite saveGoodsFavorite(HttpServletRequest request, String userId, String goodsId, String goodsSpecId) throws Exception {
+        CurrentCompanyWechat companyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        if (StringUtils.isBlank(userId)) {
+            throw new RemoteServiceException("无效的userId");
+        }
+        if (StringUtils.isBlank(goodsId)) {
+            throw new RemoteServiceException("无效的goodsId");
+        }
+        GoodsFavorite goodsFavorite = new GoodsFavorite();
+        goodsFavorite.setGoodsId(goodsId);
+        goodsFavorite.setUserId(userId);
+        goodsFavorite.setGoodsSpecId(goodsSpecId);
+        goodsFavorite.setCreateTime(new Date());
+        goodsFavorite.setCompanyWechatId(companyWechat.getCurrentCompanyWechatId());
+        goodsFavorite.setCompanyName(companyWechat.getCurrentCompanyName());
+        goodsFavoriteService.save(goodsFavorite);
+        return goodsFavorite;
+    }
+
+    public void delGoodsFavorite(String goodFavoriteId) {
+        goodsFavoriteService.lambdaUpdate()
+                .eq(StringUtils.isNotBlank(goodFavoriteId), GoodsFavorite::getGoodsFavoriteId, goodFavoriteId)
+                .remove();
+    }
+
+    public void detailDelGoodsFavorite(String goodsId, String userId, String goodSpecId) throws RemoteServiceException {
+        goodsFavoriteService.lambdaUpdate()
+                .eq(StringUtils.isNotBlank(goodsId), GoodsFavorite::getGoodsId, goodsId)
+                .eq(StringUtils.isNotBlank(userId), GoodsFavorite::getUserId, userId)
+                .eq(StringUtils.isNotBlank(goodSpecId), GoodsFavorite::getGoodsSpecId, goodSpecId)
+                .remove();
+    }
+
+
+    public IPage<GoodsFavoriteBean> pageGoodsFavorite(String userId, Integer pageNum, Integer pageSize) {
+        return customGoodsMapper.queryGoodsFavorite(new Page<>(pageNum, pageSize), userId);
+    }
+
+
+}
+

+ 434 - 24
src/main/java/com/gree/mall/contest/logic/goods/GoodsLogic.java

@@ -12,15 +12,16 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.gree.mall.contest.bean.ExcelData;
 import com.gree.mall.contest.bean.admin.AdminUserCom;
 import com.gree.mall.contest.bean.goods.*;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
 import com.gree.mall.contest.bean.zfire.ZfireParamBean;
 import com.gree.mall.contest.commonmapper.CustomGoodsMapper;
 import com.gree.mall.contest.commonmapper.GoodsCheckMapper;
+import com.gree.mall.contest.commonmapper.GoodsSpecDetailMapper;
 import com.gree.mall.contest.constant.Constant;
-import com.gree.mall.contest.enums.FreightTypeEnum;
-import com.gree.mall.contest.enums.GoodsTypeEnum;
-import com.gree.mall.contest.enums.UMSDiscountCodeEnum;
+import com.gree.mall.contest.enums.*;
 import com.gree.mall.contest.exception.RemoteServiceException;
 import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.logic.common.WechatLogic;
 import com.gree.mall.contest.logic.user.UserLogic;
 import com.gree.mall.contest.plus.entity.*;
 import com.gree.mall.contest.plus.service.*;
@@ -37,6 +38,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.math.BigDecimal;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -65,6 +67,14 @@ public class GoodsLogic {
     private final AdminCompanyWechatService adminCompanyWechatService;
     private final FreightTemplateService freightTemplateService;
     private final FreightTemplateDetailService freightTemplateDetailService;
+    private final GoodsSpecDetailMapper goodsSpecDetailMapper;
+    private final UserService userService;
+    private final PromotionGroupSpecUserService promotionGroupSpecUserService;
+    private final GoodsFavoriteService goodsFavoriteService;
+    private final PubTemplateService pubTemplateService;
+    private final PromotionGroupSpecService promotionGroupSpecService;
+    private final PromotionGroupService promotionGroupService;
+    private final WechatLogic wechatLogic;
     @Value("${goods.share.limit.percent}")
     private String percentLimit;
 
@@ -270,14 +280,15 @@ public class GoodsLogic {
                 .list();
 
         //商品库存
-        Integer goodsStock = goodsSpecs.stream().collect(Collectors.summingInt(x -> x.getStockNum()));
+        Integer goodsStock = goodsSpecs.stream().mapToInt(x -> x.getStockNum()).sum();
 
         GoodsBean goodsBean = new GoodsBean();
         if (goods == null) {
             throw new RemoteServiceException("商品不存在");
         }
         BeanUtils.copyProperties(goods, goodsBean);
-        goodsBean.setGoodsSpecs(goodsSpecs);
+        List<GoodsSpecSecBean> specSecBeans = BeanUtil.copyToList(goodsSpecs, GoodsSpecSecBean.class);
+        goodsBean.setGoodsSpecs(specSecBeans);
         goodsBean.setImages(commonLogic.queryFileByObjId(goods.getGoodsId(), Constant.Img.GOODS_IMG));
         goodsBean.setStock(goodsStock);
         List<GoodsTemplate> goodsTemplates = goodsTemplateService.lambdaQuery()
@@ -384,9 +395,9 @@ public class GoodsLogic {
         final AdminCompanyWechat companyWechat = adminCompanyWechatService.getById(adminUser.getLoginCompanyWechatId());
 
         Date creatDate = new Date();
-        List<GoodsSpec> goodsSpecList = goodsBean.getGoodsSpecs();
+        List<GoodsSpecSecBean> goodsSpecList = goodsBean.getGoodsSpecs();
         if (CollectionUtils.isNotEmpty(goodsSpecList)) {
-            Collections.sort(goodsSpecList, (o1, o2) -> o1.getPrice().compareTo(o2.getPrice()));
+            goodsSpecList.sort(Comparator.comparing(GoodsSpec::getPrice));
             goodsBean.setGoodsPrice(goodsSpecList.get(0).getPrice());
         }
         goodsBean.setUpdateTime(new Date());
@@ -396,17 +407,19 @@ public class GoodsLogic {
         if (CollectionUtils.isNotEmpty(goodsBean.getGoodsSpecs())) {
             goodsSpecList = goodsBean.getGoodsSpecs()
                     .stream()
-                    .map(goodsSpec -> goodsSpec.setGoodsId(goodsBean.getGoodsId())
-                            .setCompanyWechatId(goodsBean.getCompanyWechatId())
-                            .setCompanyName(goodsBean.getCompanyName())
-                            .setCreateTime(creatDate).setDel(false)
-                            .setShareAmount(limitShareAmount(goodsSpec.getPrice(), goodsSpec.getShareAmount())) //限制金额40%
-                            .setShareAmount(limitShareAmount(goodsSpec.getPrice(), goodsSpec.getSharePercent() != null  //分销比列不为0覆盖分销金额
-                                    && goodsSpec.getSharePercent().compareTo(new Double("0.00")) > 0 ?
-                                    goodsSpec.getPrice().multiply(new BigDecimal(goodsSpec.getSharePercent() / 100.0))
-                                            .setScale(2, BigDecimal.ROUND_DOWN)
-                                    : goodsSpec.getShareAmount()))
-                            .setInnerShareAmount(getInnerShareAmount(goodsSpec))//需最后设置
+                    .peek(v -> {
+                                v.setGoodsId(goodsBean.getGoodsId())
+                                        .setCompanyWechatId(goodsBean.getCompanyWechatId())
+                                        .setCompanyName(goodsBean.getCompanyName())
+                                        .setCreateTime(creatDate).setDel(false)
+                                        .setShareAmount(limitShareAmount(v.getPrice(), v.getShareAmount())) //限制金额40%
+                                        .setShareAmount(limitShareAmount(v.getPrice(), v.getSharePercent() != null  //分销比列不为0覆盖分销金额
+                                                && v.getSharePercent().compareTo(new Double("0.00")) > 0 ?
+                                                v.getPrice().multiply(BigDecimal.valueOf(v.getSharePercent() / 100.0))
+                                                        .setScale(2, BigDecimal.ROUND_DOWN)
+                                                : v.getShareAmount()))
+                                        .setInnerShareAmount(getInnerShareAmount(v)); //需最后设置
+                            }
                     )
                     .collect(Collectors.toList());
         }
@@ -424,8 +437,10 @@ public class GoodsLogic {
         }
 
         //批量保存商品规格
-        if (CollectionUtils.isNotEmpty(goodsSpecList))
-            goodsSpecService.saveBatch(goodsSpecList);
+        if (CollectionUtils.isNotEmpty(goodsSpecList)) {
+            final List<GoodsSpec> goodsSpecs = BeanUtil.copyToList(goodsSpecList, GoodsSpec.class);
+            goodsSpecService.saveBatch(goodsSpecs);
+        }
 
         //保存轮播图
         if (goodsBean.getImages() != null && goodsBean.getImages().size() > 0) {
@@ -502,6 +517,12 @@ public class GoodsLogic {
         return goodsSpec.getShareAmount();
     }
 
+    private BigDecimal getInnerShareAmount(GoodsSpecSecBean goodsSpecSecBean) {
+        GoodsSpec goodsSpec = new GoodsSpec();
+        BeanUtils.copyProperties(goodsSpecSecBean, goodsSpec);
+        return this.getInnerShareAmount(goodsSpec);
+    }
+
     /**
      * 编辑商品
      *
@@ -546,8 +567,8 @@ public class GoodsLogic {
 
 
             //更新主表信息
-            List<GoodsSpec> goodsSpecList = goodsBean.getGoodsSpecs();
-            Collections.sort(goodsSpecList, Comparator.comparing(GoodsSpec::getPrice));
+            List<GoodsSpec> goodsSpecList = BeanUtil.copyToList(goodsBean.getGoodsSpecs(), GoodsSpec.class);
+            goodsSpecList.sort(Comparator.comparing(GoodsSpec::getPrice));
 
             goodsBean.setGoodsPrice(goodsSpecList.get(0).getPrice());
         }
@@ -913,12 +934,13 @@ public class GoodsLogic {
         List<GoodsBean> goodsBeans = BeanUtil.copyToList(list, GoodsBean.class);
         for (GoodsBean goodsBean : goodsBeans) {
             List<GoodsSpec> list1 = goodsSpecService.lambdaQuery().eq(GoodsSpec::getGoodsId, goodsBean.getGoodsId()).list();
-            goodsBean.setGoodsSpecs(list1);
+            final List<GoodsSpecSecBean> specSecBeans = BeanUtil.copyToList(list1, GoodsSpecSecBean.class);
+            goodsBean.setGoodsSpecs(specSecBeans);
 
             GoodsCategory goodsCategory = map.get(goodsBean.getCategoryId());
             if (goodsCategory != null) {
                 goodsBean.setCategory2(goodsCategory.getName());
-                goodsBean.setIsVr(goodsBean.getGoodsName().indexOf("清洗") > -1);
+                goodsBean.setIsVr(goodsBean.getGoodsName().contains("清洗"));
             }
             GoodsCategory parentCategory = map.get(goodsCategory.getParentId());
             if (parentCategory != null) {
@@ -1155,4 +1177,392 @@ public class GoodsLogic {
         excelData.setRows(rows);
         ExcelUtils.exportExcel(request, response, fileName, excelData, -1, null);
     }
+
+    /**
+     * 商品列表
+     *
+     * @param keyword
+     * @param pageNum
+     * @param pageSize
+     * @return
+     */
+    public IPage<GoodsNewBean> miniPage(HttpServletRequest request, String keyword, String categoryId, Integer sort, Integer pageNum, Integer pageSize) {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        IPage<GoodsNewBean> page = goodsSpecDetailMapper.queryGoodsList(new Page(pageNum, pageSize),
+                currentCompanyWechat.getUser().getUserId(), currentCompanyWechat.getUser().getType(), keyword, categoryId, sort, currentCompanyWechat.getCompanyWechatId());
+
+        this.supplyGoodsNewBean(page, currentCompanyWechat);
+        return page;
+    }
+
+
+    public void supplyGoodsNewBean(IPage<GoodsNewBean> page, CurrentCompanyWechat currentCompanyWechat) {
+        //判定上坪是否在秒杀中 在就覆盖其价格
+        for (GoodsNewBean goodsNewBean : page.getRecords()) {
+            if (goodsNewBean == null) {
+                continue;
+            }
+            if (goodsNewBean.getGoodsType().equals(GoodsTypeEnum.PACKAGE.toString())) {
+                goodsNewBean.setOrgGoodsPrice(goodsNewBean.getPackageMinAmount());
+                goodsNewBean.setGoodsPrice(goodsNewBean.getPackageMinAmount());
+            }
+
+        }
+
+        //补充标签
+        for (GoodsNewBean goodsNewBean : page.getRecords()) {
+            //查询上标签
+            List<GoodsTagRela> list1 = goodsTagRelaService.lambdaQuery().eq(GoodsTagRela::getGoodsId, goodsNewBean.getGoodsId())
+                    .eq(GoodsTagRela::getType, 1).list();
+            if (list1.size() > 0) {
+                List<String> collect1 = list1.stream().map(GoodsTagRela::getGoodsTagName).collect(Collectors.toList());
+                goodsNewBean.setTags1(collect1);
+            }
+
+            //查询活动专区标签 //查询下标签
+            //活动商品
+            List<GoodsTagRela> list = goodsTagRelaService.lambdaQuery().eq(GoodsTagRela::getGoodsId, goodsNewBean.getGoodsId())
+                    .eq(GoodsTagRela::getType, 2).list();
+            if (list.size() > 0) {
+                List<String> collect1 = list.stream().map(GoodsTagRela::getGoodsTagName).collect(Collectors.toList());
+                goodsNewBean.setTags2(collect1);
+            }
+        }
+    }
+
+    /**
+     * 商品详情
+     */
+    public GoodsBean miniDetail(HttpServletRequest request, String goodsId, String userId, String wxSceneId) {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        List<PromotionGroupSpecUser> list = new ArrayList<>();
+
+        User user = userService.getById(userId);
+        if (user.getPromotionGroupLeader()) {
+            //团长在该活动商品的规格
+            list = promotionGroupSpecUserService.lambdaQuery()
+                    .eq(PromotionGroupSpecUser::getUserId, userId)
+                    .eq(PromotionGroupSpecUser::getGoodsId, goodsId)
+                    .list();
+        } else {
+            String promotionGroupUserId = user.getPromotionGroupUserId();
+            if (com.aliyuncs.utils.StringUtils.isNotEmpty(promotionGroupUserId)) {
+                list = promotionGroupSpecUserService.lambdaQuery()
+                        .eq(PromotionGroupSpecUser::getUserId, promotionGroupUserId)
+                        .eq(PromotionGroupSpecUser::getGoodsId, goodsId)
+                        .list();
+            }
+        }
+
+        GoodsBean goodsBean = null;
+        if (list.size() == 0) {
+            goodsBean = this.commonDetail(goodsId, userId, wxSceneId);
+        } else {
+            goodsBean = this.groupGoodsdetail(list);
+        }
+        //记录用户浏览记录
+        GoodsVisit goodsVisit = new GoodsVisit();
+        goodsVisit.setGoodsId(goodsId);
+        goodsVisit.setGoodsName(goodsBean.getGoodsName());
+        goodsVisit.setUserId(userId);
+        goodsVisit.setUserName(user.getNickName());
+        goodsVisit.setUserPhone(user.getMobile());
+        goodsVisit.setPromotionGroupId(goodsBean.getPromotionGroupId());
+        goodsVisit.setGoodsPrice(goodsBean.getGoodsPrice());
+        goodsVisit.setShareUserId(goodsBean.getGroupUserId());
+        goodsVisit.setCompanyWechatId(currentCompanyWechat.getCurrentCompanyWechatId());
+        goodsVisit.setCompanyName(currentCompanyWechat.getCurrentCompanyName());
+        goodsVisit.setCreateTime(new Date());
+        goodsVisit.insert();
+
+        return goodsBean;
+    }
+
+    /**
+     * 商品详情
+     *
+     * @param goodsId
+     * @return
+     */
+    public GoodsBean commonDetail(String goodsId, String userId, String wxSceneId) throws RemoteServiceException {
+        User user = userService.getById(userId);
+        String serviceId = user.getServiceId();
+        if (user.getType().equals(UserTypeEnum.SERVICE.toString())) {
+            serviceId = user.getUserId();
+        }
+        User service = userService.getById(serviceId);
+        Goods goods = goodsService.lambdaQuery()
+                .eq(Goods::getGoodsId, goodsId)
+                .eq(Goods::getDel, false)
+                .eq(Goods::getStatus, true)
+                .eq(Goods::getPromotionGroup, false)
+                .one();
+        if (goods == null) {
+            throw new RemoteServiceException("商品不存在或已下架");
+        }
+        //商品规格(包含秒杀商品)
+        List<GoodsSpecSecBean> goodsSpecs = goodsSpecDetailMapper.querySpecSec(goodsId);
+        //是否有收藏
+        Long goodsFavoritesCount = goodsFavoriteService.lambdaQuery()
+                .eq(GoodsFavorite::getGoodsId, goodsId)
+                .eq(GoodsFavorite::getUserId, userId)
+                .count();
+
+        //商品库存
+        Integer goodsStock = goodsSpecs.stream().mapToInt(GoodsSpec::getStockNum).sum();
+        GoodsBean goodsBean = new GoodsBean();
+        BeanUtils.copyProperties(goods, goodsBean);
+
+        //内部人员、秒杀活动、让利分享 等对价格分佣金额产生的影响处理
+        goodsSpecs.forEach(o -> {
+            if (o.getOrgPrice().doubleValue() == 0) {
+                o.setOrgPrice(o.getOrgGoodsPrice());
+            }
+            //内部人员
+            if (service != null && service.getInnerr() && service.getType().equals(UserTypeEnum.SERVICE.toString())) {
+                o.setShareAmount(o.getInnerShareAmount());
+            }
+            //秒杀
+            if (o.getSecType()) {
+                o.setPrice(o.getSecPrice());
+                o.setShareAmount(o.getSecShareAmount());
+            }
+        });
+        //让利
+        Map<String, BigDecimal> cutMap = this.cutAmountGoods(wxSceneId);
+        if (cutMap != null) {
+            for (GoodsSpec o : goodsSpecs) {
+                //让利
+                BigDecimal cutAmount = cutMap.get(o.getGoodsSpecId());
+                if (cutAmount != null) {
+                    o.setShareAmount(o.getShareAmount().subtract(cutMap.get(o.getGoodsSpecId())));
+                    o.setPrice(o.getPrice().subtract(cutAmount));
+                }
+            }
+        }
+        if (!CollectionUtils.isEmpty(goodsSpecs)) {
+            goodsBean.setShareAmount(goodsSpecs.get(0).getShareAmount());
+        }
+        if (goodsBean.getGoodsType().equals(GoodsTypeEnum.PACKAGE.toString())) {
+            goodsBean.setShareAmount(goodsBean.getPackageMinShareAmount());
+            goodsBean.setGoodsPrice(goodsBean.getPackageMinAmount());
+            goodsBean.setOrgGoodsPrice(goodsBean.getPackageMinAmount());
+        }
+        goodsBean.setGoodsSpecs(goodsSpecs);
+        goodsBean.setImages(commonLogic.queryFileByObjId(goods.getGoodsId(), Constant.Img.GOODS_IMG));
+        goodsBean.setFavorite(goodsFavoritesCount > 0);
+        goodsBean.setStock(goodsStock);
+        goodsBean.setCommonTemplate(goodsSpecDetailMapper.queryCommonTemplate(goodsId));
+        goodsBean.setPubCommonTemplate(pubTemplateService.getById(goods.getCompanyWechatId()));
+
+        return goodsBean;
+    }
+
+    /**
+     * 返回 降价的金额幅度map key=商品规格id,value = 降价的金额
+     *
+     * @param wxSceneId
+     * @return
+     */
+    public Map<String, BigDecimal> cutAmountGoods(String wxSceneId) {
+        //通过分享的形式购买的
+        Map<String, BigDecimal> cutMap = new HashMap<>();
+        if (com.aliyuncs.utils.StringUtils.isNotEmpty(wxSceneId)) {
+            WxScene scene = commonLogic.getScene(wxSceneId);
+            if (scene.getType().equals(QrCodeEnum.CUT.toString().toLowerCase())) {
+                List<WxSceneDetail> sceneDetail = commonLogic.getSceneDetail(wxSceneId);
+                for (WxSceneDetail wxSceneDetail : sceneDetail) {
+                    cutMap.put(wxSceneDetail.getGoodsSpecId(), wxSceneDetail.getCutAmount());
+                }
+            }
+        }
+
+        if (cutMap.keySet().size() == 0) {
+            return null;
+        }
+        return cutMap;
+    }
+
+    /**
+     * 团购商品详情
+     */
+    public GoodsBean groupGoodsdetail(List<PromotionGroupSpecUser> list) {
+
+        PromotionGroupSpecUser promotionGroupSpecUser = list.get(0);
+
+        //团长id
+        String groupUserId = promotionGroupSpecUser.getUserId();
+        User groupUser = userService.getById(groupUserId);
+        if (!groupUser.getPromotionGroupLeader()) {
+            throw new RemoteServiceException("链接已失效");
+        }
+
+        Goods goods = goodsService.getById(promotionGroupSpecUser.getGoodsId());
+
+        GoodsBean goodsBean = new GoodsBean();
+        BeanUtils.copyProperties(goods, goodsBean);
+        //多规格
+        List<GoodsSpecSecBean> goodsSpecSecBeans = new ArrayList<>();
+        for (int i = 0; i < list.size(); i++) {
+            PromotionGroupSpecUser specUser = list.get(i);
+            PromotionGroupSpec promotionGroupSpec = promotionGroupSpecService.getById(specUser.getPromotionGroupSpecId());
+
+            GoodsSpecSecBean specSecBean = new GoodsSpecSecBean();
+            specSecBean.setPrice(specUser.getGroupPrice());
+            specSecBean.setOrgPrice(specUser.getOrgGoodsPrice());
+            specSecBean.setImgUrl(specUser.getGoodsImgSrc());
+            specSecBean.setShareAmount(specUser.getShareAmount());
+            specSecBean.setStockNum(promotionGroupSpec.getStock());
+            specSecBean.setSoldNum(promotionGroupSpec.getSalesNum());
+            specSecBean.setName(promotionGroupSpec.getGoodsSpecName());
+            specSecBean.setSpecValue(promotionGroupSpec.getGoodsSpecValue());
+            specSecBean.setGoodsSpecId(promotionGroupSpec.getGoodsSpecId());
+            specSecBean.setGoodsId(promotionGroupSpec.getGoodsId());
+
+            if (i == 0) {
+                goodsBean.setStock(promotionGroupSpec.getStock());
+                goodsBean.setSoldNum(promotionGroupSpec.getSalesNum());
+                goodsBean.setPromotionGroupId(promotionGroupSpec.getPromotionGroupId());
+            }
+            goodsSpecSecBeans.add(specSecBean);
+        }
+        goodsBean.setOrgGoodsPrice(promotionGroupSpecUser.getOrgGoodsPrice());
+        goodsBean.setGoodsPrice(promotionGroupSpecUser.getGroupPrice());
+        goodsBean.setImages(commonLogic.queryFileByObjId(goods.getGoodsId(), Constant.Img.GOODS_IMG));
+        goodsBean.setGroupUserId(groupUserId);
+        goodsBean.setGroupUserName(promotionGroupSpecUser.getUserName());
+        goodsBean.setGroupPic(groupUser.getAvatar());
+        goodsBean.setGoodsSpecs(goodsSpecSecBeans);
+        return goodsBean;
+    }
+
+    /**
+     * 团购商品列表
+     *
+     * @param userId
+     * @return
+     */
+    public IPage<PromotionGoodsBean> myGroupGoods(String userId, String goodsCategoryId, String keyword, Integer pageNum, Integer pageSize, HttpServletRequest request) {
+        User user = userService.getById(userId);
+        if (!user.getPromotionGroupLeader()) {
+            userId = user.getPromotionGroupUserId();
+        }
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        String companyWechatId = currentCompanyWechat.getCompanyWechatId();
+        IPage<PromotionGoodsBean> promotionGoodsBeanIPage = goodsSpecDetailMapper.queryPromotionGoods(new Page(pageNum, pageSize), userId, goodsCategoryId, keyword, companyWechatId);
+        return promotionGoodsBeanIPage;
+    }
+
+    /**
+     * 团购活动二维码
+     */
+    public PromotionShareQrCode queryShareQrcode(String userId, HttpServletRequest request) throws IOException {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        User user = userService.getById(currentCompanyWechat.getUserId());
+        if (!user.getStatus() || !user.getPromotionGroupLeader()) {
+            throw new RemoteServiceException("无效的用户");
+        }
+        //查询活动背景图
+        List<PromotionGroup> list = promotionGroupService.lambdaQuery()
+                .le(PromotionGroup::getStartTime, new Date())
+                .ge(PromotionGroup::getEndTime, new Date())
+                .eq(PromotionGroup::getStatus, true)
+                .eq(PromotionGroup::getCompanyWechatId, user.getCompanyWechatId())
+                .orderByDesc(PromotionGroup::getCreateTime)
+                .list();
+        if (list.size() == 0) {
+            return null;
+        }
+        PromotionGroup promotionGroup = list.get(0);
+        String qrcode = wechatLogic.getQrcode(QrCodeEnum.PROMOTION_GROUP.toString().toLowerCase(), promotionGroup.getPromotionGroupId(), userId, user.getCompanyWechatId());
+        PromotionShareQrCode promotionShareQrCode = new PromotionShareQrCode();
+        promotionShareQrCode.setPromotionImgUrl(qrcode);
+        promotionShareQrCode.setQrcode(qrcode);
+        promotionShareQrCode.setPromotionImgUrl(promotionGroup.getImgUrl());
+        promotionShareQrCode.setPosterImgUrl(promotionGroup.getPosterImgUrl());
+        return promotionShareQrCode;
+    }
+
+    /**
+     * 让利二维码或链接
+     */
+    public String getCutQrcode(List<CutGoodsBean> cutGoodsBeans, HttpServletRequest request) {
+        if (CollectionUtils.isEmpty(cutGoodsBeans)) {
+            throw new RemoteServiceException("降价二维码参数异常");
+        }
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        String userId = currentCompanyWechat.getUserId();
+        String companyWechatId = currentCompanyWechat.getCompanyWechatId();
+        String secKillActivityId = cutGoodsBeans.get(0).getSecKillActivityId();
+        String goodsId = cutGoodsBeans.get(0).getGoodsId();
+        Boolean qrcode = cutGoodsBeans.get(0).getQrcode();
+        User user = userService.getById(userId);
+        //检查让利是否合理
+        Goods goods = this.getGoodsById(goodsId);
+        if (goods.getPromotionGroup()) {
+            throw new RemoteServiceException("团购商品不支持降价分享");
+        }
+        //检查合法性
+        for (CutGoodsBean cutGoodsBean : cutGoodsBeans) {
+            this.checkCut(user, cutGoodsBean.getGoodsId(), cutGoodsBean.getGoodsSpecId(), cutGoodsBean.getCutAmount(), companyWechatId);
+        }
+        String cutQrcode = wechatLogic.getCutQrcode(goodsId, userId, companyWechatId, qrcode, secKillActivityId, cutGoodsBeans);
+        return cutQrcode;
+    }
+
+    /**
+     * 判断让利金额是否合
+     */
+    public Boolean checkCut(User user, String goodsId, String goodsSpecId, BigDecimal cutAmount, String companyWechatId) {
+        if (!user.getType().equals(UserTypeEnum.SERVICE.toString())) {
+            throw new RemoteServiceException("非分销员不可降价分享");
+        }
+
+        //检查普通商品
+        GoodsSpec goodsSpec = this.getGoodsSpecById(goodsSpecId);
+        //内部人员价格
+        boolean innerrUserShare = user.getInnerr() && goodsSpec.getInnerShareAmount().doubleValue() < cutAmount.doubleValue();
+        //外部人员价格
+        boolean notInnerrUserShare = !user.getInnerr() && goodsSpec.getShareAmount().doubleValue() < cutAmount.doubleValue();
+        if (innerrUserShare || notInnerrUserShare) {
+            throw new RemoteServiceException("让利降价金额设置不合理");
+        }
+
+        return true;
+    }
+
+    /**
+     * 获取商品
+     *
+     * @param goodsId
+     * @return
+     */
+    public Goods getGoodsById(String goodsId) {
+        return goodsService.getById(goodsId);
+    }
+
+    public GoodsSpec getGoodsSpecById(String goodsSpecId) {
+        return goodsSpecService.getById(goodsSpecId);
+    }
+
+    /**
+     * 团购商品-选购列表
+     */
+    public List<List<GoodsPackageBean>> choicePackage(String goodsId, HttpServletRequest request) {
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        String companyWechatId = currentCompanyWechat.getCompanyWechatId();
+        List<GoodsPackageBean> goodsPackageBeans = goodsSpecDetailMapper.queryGoodsPackage(currentCompanyWechat.getUser().getUserId(), currentCompanyWechat.getUser().getType(), goodsId, companyWechatId);
+        if (CollectionUtils.isEmpty(goodsPackageBeans)) {
+            return new ArrayList();
+        }
+        List<Integer> types = goodsPackageBeans.stream().map(GoodsPackageBean::getType).distinct().collect(Collectors.toList());
+        List<List<GoodsPackageBean>> list = new ArrayList<>();
+        for (Integer type : types) {
+            List<GoodsPackageBean> collect = goodsPackageBeans.stream().filter(v -> v.getType().equals(type)).collect(Collectors.toList());
+            list.add(collect);
+        }
+        return list;
+    }
 }

+ 124 - 0
src/main/java/com/gree/mall/contest/logic/merchant/MerchantLogic.java

@@ -0,0 +1,124 @@
+package com.gree.mall.contest.logic.merchant;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.bean.merchant.WebsitApplyAdd;
+import com.gree.mall.contest.bean.merchant.WebsitApplyDetail;
+import com.gree.mall.contest.bean.merchant.WebsitApplyVO;
+import com.gree.mall.contest.bean.zfire.ZfireParamBean;
+import com.gree.mall.contest.commonmapper.CommonMapper;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.logic.SMSLogic;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.GoodsCategory;
+import com.gree.mall.contest.plus.entity.WebsitApply;
+import com.gree.mall.contest.plus.entity.WebsitApplyArea;
+import com.gree.mall.contest.plus.entity.WebsitApplyLog;
+import com.gree.mall.contest.plus.service.GoodsCategoryService;
+import com.gree.mall.contest.plus.service.WebsitApplyAreaService;
+import com.gree.mall.contest.plus.service.WebsitApplyLogService;
+import com.gree.mall.contest.plus.service.WebsitApplyService;
+import com.gree.mall.contest.utils.zfire.FieldUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class MerchantLogic {
+    private final CommonMapper commonMapper;
+    private final CommonLogic commonLogic;
+    private final SMSLogic smsLogic;
+    private final WebsitApplyService websitApplyService;
+    private final WebsitApplyAreaService websitApplyAreaService;
+    private final GoodsCategoryService goodsCategoryService;
+    private final WebsitApplyLogService websitApplyLogService;
+
+    public Boolean register(String mobile, String code) {
+        smsLogic.checkSmsCode(mobile, code, "BIND");
+        WebsitApply websitApply = websitApplyService.lambdaQuery().eq(WebsitApply::getMobile, mobile).last("limit 1").one();
+
+        if (websitApply != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public WebsitApplyDetail applyMobile(String mobile) {
+        WebsitApply websitApply = websitApplyService.lambdaQuery().eq(WebsitApply::getMobile, mobile).last("limit 1").one();
+        WebsitApplyDetail websitApplyDetail = BeanUtil.copyProperties(websitApply, WebsitApplyDetail.class);
+        List<WebsitApplyArea> list = websitApplyAreaService.lambdaQuery()
+                .eq(WebsitApplyArea::getWebsitApplyId, websitApplyDetail.getWebsitApplyId()).list();
+        websitApplyDetail.setWebsitApplyAreaList(list);
+        return websitApplyDetail;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public WebsitApply applyList(WebsitApplyAdd websitApplyAdd) {
+        List<String> collect = goodsCategoryService.lambdaQuery()
+                .in(GoodsCategory::getCategoryId, websitApplyAdd.getCategoryIds())
+                .list()
+                .stream().map(GoodsCategory::getName)
+                .collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(collect))
+            throw new RemoteServiceException("主营分类未选择");
+
+        String categoryName = String.join(",", collect);
+        String categoryId = String.join(",", websitApplyAdd.getCategoryIds());
+
+        websitApplyAdd.setCategoryId(categoryId);
+        websitApplyAdd.setCategoryName(categoryName);
+        websitApplyAdd.setStatus("WAIT");
+        websitApplyAdd.insertOrUpdate();
+
+        websitApplyAreaService.lambdaUpdate().eq(WebsitApplyArea::getWebsitApplyId, websitApplyAdd.getWebsitApplyId()).remove();
+
+        for (WebsitApplyArea websitApplyArea : websitApplyAdd.getWebsitApplyAreaList()) {
+            websitApplyArea.setWebsitApplyId(websitApplyAdd.getWebsitApplyId());
+        }
+        websitApplyAreaService.saveBatch(websitApplyAdd.getWebsitApplyAreaList());
+
+        return websitApplyAdd;
+    }
+
+    public IPage<WebsitApplyVO> pageApply(ZfireParamBean zfireParamBean) {
+        AdminUserCom adminUser = commonLogic.getAdminUser();
+        FieldUtils.supplyParam(zfireParamBean, WebsitApplyVO.class, adminUser);
+        return commonMapper.pageApply(new Page(zfireParamBean.getPageNum(), zfireParamBean.getPageSize()), zfireParamBean);
+    }
+
+    public WebsitApplyDetail detail(String id) {
+
+        WebsitApply websitApply = websitApplyService.getById(id);
+        WebsitApplyDetail websitApplyDetail = BeanUtil.copyProperties(websitApply, WebsitApplyDetail.class);
+        List<WebsitApplyArea> list = websitApplyAreaService.lambdaQuery().eq(WebsitApplyArea::getWebsitApplyId, websitApplyDetail.getWebsitApplyId()).list();
+        websitApplyDetail.setWebsitApplyAreaList(list);
+
+        List<WebsitApplyLog> websitApplyLogs = websitApplyLogService.lambdaQuery().eq(WebsitApplyLog::getWebsitApplyId, websitApplyDetail.getWebsitApplyId()).list();
+        websitApplyDetail.setWebsitApplyLogList(websitApplyLogs);
+
+        return websitApplyDetail;
+    }
+
+    public void updateIn(String id, String status, String remark) {
+        WebsitApply websitApply = websitApplyService.getById(id);
+        websitApply.setStatus(status);
+        websitApply.setRemark(remark);
+        websitApply.updateById();
+        WebsitApplyLog websitApplyLog = new WebsitApplyLog();
+        websitApplyLog.setWebsitApplyId(id);
+        websitApplyLog.setStatus(status);
+        websitApplyLog.setRemark(remark);
+        websitApplyLog.insert();
+    }
+}

+ 138 - 0
src/main/java/com/gree/mall/contest/logic/order/OrderCommentLogic.java

@@ -0,0 +1,138 @@
+package com.gree.mall.contest.logic.order;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.goods.CommentTagCount;
+import com.gree.mall.contest.bean.goods.GoodsComment;
+import com.gree.mall.contest.bean.goods.GoodsCommentCount;
+import com.gree.mall.contest.bean.order.OrderCommentBean;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
+import com.gree.mall.contest.commonmapper.GoodsSpecDetailMapper;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.CommentTag;
+import com.gree.mall.contest.plus.entity.CommonFile;
+import com.gree.mall.contest.plus.entity.OrderCommentTag;
+import com.gree.mall.contest.plus.entity.OrderInfo;
+import com.gree.mall.contest.plus.service.CommentTagService;
+import com.gree.mall.contest.plus.service.OrderCommentService;
+import com.gree.mall.contest.plus.service.OrderCommentTagService;
+import com.gree.mall.contest.plus.service.OrderInfoService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class OrderCommentLogic {
+
+    private final OrderCommentService orderCommentService;
+    private final OrderCommentTagService orderCommentTagService;
+    private final OrderInfoService orderInfoService;
+    private final CommentTagService commentTagService;
+    private final GoodsSpecDetailMapper goodsSpecDetailMapper;
+    private final CommonLogic commonLogic;
+
+
+    /**
+     * 评价标签列表
+     */
+    public List<CommentTag> list(HttpServletRequest request){
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        String companyWechatId = currentCompanyWechat.getCurrentCompanyWechatId();
+
+        List<CommentTag> list = commentTagService.lambdaQuery().eq(CommentTag::getCompanyWechatId,companyWechatId).list();
+        return list;
+    }
+
+    /**
+     * 商品的评价汇总
+     */
+    public GoodsCommentCount goodsCommentCount(HttpServletRequest request , String goodsId){
+        CurrentCompanyWechat companyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        //0-4的随机数
+        int num = new Random().nextInt(5);
+
+        GoodsCommentCount goodsCommentCount = new GoodsCommentCount();
+        List<CommentTagCount> commentTagCounts = goodsSpecDetailMapper.goodsCommentCount(companyWechat.getCurrentCompanyWechatId(),goodsId);
+        goodsCommentCount.setTagCountList(commentTagCounts);
+        goodsCommentCount.setGoodRate(95 + num);
+        return goodsCommentCount;
+    }
+
+    /**
+     * 商品的评价列表
+     */
+    public IPage<GoodsComment> goodsCommentList(HttpServletRequest request , String goodsId, String tag, Integer pageNo, Integer pageSize){
+
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        IPage<GoodsComment> goodsCommentIPage = goodsSpecDetailMapper.goodsCommentList(new Page(pageNo, pageSize),
+                currentCompanyWechat.getCurrentCompanyWechatId(),goodsId,tag);
+
+        for(GoodsComment goodsComment : goodsCommentIPage.getRecords()){
+            //标签
+            List<OrderCommentTag> tagList = orderCommentTagService.lambdaQuery()
+                    .eq(OrderCommentTag::getOrderId, goodsComment.getOrderId())
+                    .eq(OrderCommentTag::getCompanyWechatId, currentCompanyWechat.getCurrentCompanyWechatId())
+                    .list();
+            if(tagList.size() > 0) {
+                goodsComment.setTags(tagList.stream().map(OrderCommentTag::getTag).collect(Collectors.toList()));
+            }
+            //图片
+            List<CommonFile> commonFiles = commonLogic.queryFileByObjId(goodsComment.getOrderId(), Constant.Img.ORDER_COMMENT);
+            if(commonFiles.size() > 0){
+                goodsComment.setImgs(commonFiles.stream().map(CommonFile::getUrl).collect(Collectors.toList()));
+            }
+        }
+        return goodsCommentIPage;
+    }
+
+    /**
+     * 新增评价
+     * @param orderCommentBean
+     */
+    public void add(HttpServletRequest request , OrderCommentBean orderCommentBean){
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+
+        if(orderCommentBean.getCommentExpress() == 5 && orderCommentBean.getCommentGoods() == 5 && orderCommentBean.getCommentExpress() == 5){
+            orderCommentBean.setIsShow(true);
+        }
+        orderCommentBean.setCreateTime(new Date());
+        orderCommentBean.setCompanyWechatId(currentCompanyWechat.getCurrentCompanyWechatId());
+        orderCommentBean.setCompanyName(currentCompanyWechat.getCurrentCompanyName());
+        orderCommentService.save(orderCommentBean);
+        //保存标签
+        for(String tag : orderCommentBean.getTags()){
+            OrderCommentTag orderCommentTag = new OrderCommentTag();
+            orderCommentTag.setCreateTime(new Date());
+            orderCommentTag.setOrderCommentId(orderCommentBean.getOrderCommentId());
+            orderCommentTag.setOrderId(orderCommentBean.getOrderId());
+            orderCommentTag.setTag(tag);
+            orderCommentTag.setCompanyWechatId(currentCompanyWechat.getCurrentCompanyWechatId());
+            orderCommentTag.setCompanyName(currentCompanyWechat.getCurrentCompanyName());
+            orderCommentTag.insert();
+        }
+        //保存图片
+        commonLogic.bindFile(orderCommentBean.getOrderId(), Constant.Img.ORDER_COMMENT, orderCommentBean.getFileIds());
+
+        //更新订单表
+        OrderInfo orderInfo = new OrderInfo();
+        orderInfo.setOrderId(orderCommentBean.getOrderId());
+        orderInfo.setCommentGoods(orderCommentBean.getCommentGoods());
+        orderInfo.setCommentService(orderCommentBean.getCommentService());
+        orderInfo.setCommentExpress(orderCommentBean.getCommentExpress());
+        orderInfo.updateById();
+    }
+
+
+
+}

+ 1503 - 19
src/main/java/com/gree/mall/contest/logic/order/OrderLogic.java

@@ -1,33 +1,43 @@
 package com.gree.mall.contest.logic.order;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.IdUtil;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.gree.mall.contest.bean.ExcelData;
 import com.gree.mall.contest.bean.admin.AdminUserCom;
-import com.gree.mall.contest.bean.order.BatchRemarkBean;
-import com.gree.mall.contest.bean.order.DeliverGoodsBean;
-import com.gree.mall.contest.bean.order.OrderDetailBean;
-import com.gree.mall.contest.bean.order.OrderDetailListBean;
+import com.gree.mall.contest.bean.coupon.UserCouponBean;
+import com.gree.mall.contest.bean.goods.AckGoodsBean;
+import com.gree.mall.contest.bean.order.*;
+import com.gree.mall.contest.bean.pay.PayDetail;
+import com.gree.mall.contest.bean.user.CurrentCompanyWechat;
+import com.gree.mall.contest.commonmapper.AppMapper;
 import com.gree.mall.contest.commonmapper.CoustomUserCouponMapper;
 import com.gree.mall.contest.commonmapper.CustomWebsitMapper;
 import com.gree.mall.contest.commonmapper.OrderMapper;
 import com.gree.mall.contest.constant.Constant;
-import com.gree.mall.contest.enums.OrderShareStatusEnum;
-import com.gree.mall.contest.enums.OrderStatusEnum;
-import com.gree.mall.contest.enums.RefundFlagEnum;
+import com.gree.mall.contest.enums.*;
+import com.gree.mall.contest.enums.coupon.CouponTypeEnum;
 import com.gree.mall.contest.exception.RemoteServiceException;
 import com.gree.mall.contest.logic.ExpressLogic;
+import com.gree.mall.contest.logic.FreightLogic;
 import com.gree.mall.contest.logic.StorageLogic;
+import com.gree.mall.contest.logic.activity.PromotionGroupLogic;
 import com.gree.mall.contest.logic.common.CommonLogic;
 import com.gree.mall.contest.logic.common.WechatLogic;
+import com.gree.mall.contest.logic.coupon.CouponLogic;
+import com.gree.mall.contest.logic.goods.GoodsLogic;
+import com.gree.mall.contest.logic.pay.PayLogic;
 import com.gree.mall.contest.logic.user.MsgSubscriptLogic;
 import com.gree.mall.contest.logic.user.UserLogic;
 import com.gree.mall.contest.plus.entity.*;
 import com.gree.mall.contest.plus.service.*;
 import com.gree.mall.contest.utils.ArithUtils;
+import com.gree.mall.contest.utils.CommonUtils;
+import com.gree.mall.contest.utils.IpUtil;
 import com.gree.mall.contest.utils.excel.ExcelUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -43,10 +53,12 @@ import org.springframework.web.multipart.MultipartFile;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 @Service
@@ -78,6 +90,21 @@ public class OrderLogic {
     private final OrderMapper orderMapper;
     private final ExpressLogic expressLogic;
     private final AdminCompanyWechatService adminCompanyWechatService;
+    private final FreightLogic freightLogic;
+    private final GoodsPackagePopService goodsPackagePopService;
+    private final CouponLogic couponLogic;
+    private final GoodsLogic goodsLogic;
+    private final PromotionDiscountService promotionDiscountService;
+    private final GoodsPackageUserRelaService goodsPackageUserRelaService;
+    private final GiftExchangeCodeService giftExchangeCodeService;
+    private final PromotionGroupSpecUserService promotionGroupSpecUserService;
+    private final PromotionGroupLogic promotionGroupLogic;
+    private final UserAddressService userAddressService;
+    private final GoodsCategoryService goodsCategoryService;
+    private final ShoppingCartService shoppingCartService;
+    private final PayLogic payLogic;
+    private final AppMapper appMapper;
+    private final AdminWebsitService adminWebsitService;
 
     /**
      * 订单列表
@@ -199,18 +226,57 @@ public class OrderLogic {
         if (!orderInfo.getOrderStatus().equals(OrderStatusEnum.NOPAY.toString())) {
             throw new RemoteServiceException("订单已支付,不可取消");
         }
-//        if(!orderInfo.getOrderStatus().equals(OrderStatusEnum.NOPAY.toString())) {
-//
-//            log.info("【退款开始:】退款金额{}", orderInfo.getPayAmount().doubleValue());
-//            wechatLogic.refund(orderInfo.getOrderId(), orderInfo.getOrderId()+"T", orderInfo.getPayAmount().doubleValue()
-//                    , orderInfo.getPayAmount().doubleValue());
-//
-//            orderShareLogic.reloadAmount(orderId, null);
-//        }
+
         this.updateOrderStatus(orderId, OrderStatusEnum.CLOSE.toString());
     }
 
     /**
+     * 取消订单
+     */
+    @Transactional
+    public void cancel(List<String> orderIds) throws RemoteServiceException {
+        List<OrderInfo> list = orderInfoService.lambdaQuery().in(OrderInfo::getOrderId, orderIds).list();
+        List<String> userCouponIds = list.stream().map(OrderInfo::getUserCouponId).collect(Collectors.toList());
+        //1.取消订单
+        orderInfoService.lambdaUpdate()
+                .set(OrderInfo::getOrderStatus, OrderStatusEnum.TIMEOUT.toString())
+                .in(OrderInfo::getOrderId, orderIds).update();
+
+        //2.恢复商品规格库存
+        List<OrderDetail> orderDetails = orderDetailService.lambdaQuery().in(OrderDetail::getOrderId, orderIds).list();
+        for (OrderDetail orderDetail : orderDetails) {
+            //非秒杀订单 还原库存
+            this.recoveryStock(orderDetail.getGoodsSpecId(), orderDetail.getNum());
+        }
+        //3.恢复优惠券的使用状态
+        if (CollectionUtil.isEmpty(userCouponIds)) {
+            couponLogic.updateStatus(userCouponIds);
+        }
+        //4.恢复礼品兑换码
+        for (OrderInfo orderInfo : list) {
+            String exchangeCode = orderInfo.getExchangeCode();
+            if (StringUtils.isNotEmpty(orderInfo.getExchangeCode())) {
+                Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_GIFT + ":" + orderInfo.getExchangeCode());
+                try {
+                    if (!obtain.tryLock(5, TimeUnit.SECONDS)) {
+                        continue;
+                    }
+                    giftExchangeCodeService.lambdaUpdate()
+                            .set(GiftExchangeCode::getStatus, 0)
+                            .eq(GiftExchangeCode::getId, exchangeCode)
+                            .update();
+                } catch (InterruptedException e) {
+                    log.error("订单超时取消,恢复兑换码失败。orderId:{}", orderInfo.getOrderId());
+                } finally {
+                    obtain.unlock();
+                }
+            }
+        }
+
+
+    }
+
+    /**
      * 确定收款
      */
     public void ackPay(String orderId) {
@@ -916,11 +982,11 @@ public class OrderLogic {
     /**
      * 修改物流单号
      */
-    public void updateExpress(String orderId,String logisticsNo,String companyCode) throws RemoteServiceException {
+    public void updateExpress(String orderId, String logisticsNo, String companyCode) throws RemoteServiceException {
         //查询物流公司
         String company = expressLogic.expressCompanyName(companyCode);
         OrderInfo orderInfo = orderInfoService.getById(orderId);
-        if(!orderInfo.getOrderStatus().equals(OrderStatusEnum.YFH.toString())){
+        if (!orderInfo.getOrderStatus().equals(OrderStatusEnum.YFH.toString())) {
             throw new RemoteServiceException("非已发货订单不可修改物流单号");
         }
         orderInfo.setCompanyCode(companyCode);
@@ -932,12 +998,1430 @@ public class OrderLogic {
     /**
      * 查看物流
      */
-    public List<ExpressInfo> queryExpressInfo(String logisticsNo){
+    public List<ExpressInfo> queryExpressInfo(String logisticsNo) {
         List<ExpressInfo> list = expressInfoService.lambdaQuery().eq(ExpressInfo::getLogisticsNo, logisticsNo).list();
-        if (CollectionUtil.isNotEmpty(list)){
+        if (CollectionUtil.isNotEmpty(list)) {
             return list;
         }
         return null;
 
     }
+
+    /**
+     * 确认订单内容
+     */
+    public OrderAckBean ackOrder(OrderBuyBean orderBuyBean, HttpServletRequest request) throws InterruptedException {
+        OrderAckBean orderAckBean = this.ackOrder2(orderBuyBean, request);
+
+        //运费
+        BigDecimal freightAmountV2 = freightLogic.getFreightAmountV2(orderBuyBean, request);
+
+        orderAckBean.setFreight(freightAmountV2);
+        orderAckBean.setTotalAmount(orderAckBean.getTotalAmount());
+        //实际支付金额
+        BigDecimal payAmount = orderAckBean.getTotalAmount()
+                .subtract(orderAckBean.getExchangeAmount())
+                .subtract(orderAckBean.getDiscountAmount())
+                .add(orderAckBean.getFreight());
+        if (payAmount.doubleValue() <= 0) {
+            payAmount = BigDecimal.valueOf(0);
+        }
+        orderAckBean.setPayAmount(payAmount);
+        return orderAckBean;
+    }
+
+    /**
+     * 确认订单内容
+     */
+    public OrderAckBean ackOrder2(OrderBuyBean orderBuyBean, HttpServletRequest request) throws RemoteServiceException, InterruptedException {
+
+        //是否有礼品卡兑换码
+        Boolean hasGiftCode = false;
+        //是否为团购活动
+        Boolean isPromotionGroup = false;        //是否为套购商品
+        Boolean isGoodsPackage = StringUtils.isNotEmpty(orderBuyBean.getGoodsPackageId());
+
+        //购买人
+        String userId = CommonUtils.getUserId(request);
+        User user = userService.getById(userId);
+        //兑换码
+        String exchangeCode = orderBuyBean.getExchangeCode();
+        //非团购商品的分佣金额
+        BigDecimal notPromotionShareAmount = BigDecimal.valueOf(0);
+        //总的商品金额
+        BigDecimal totalAmount = new BigDecimal(0);
+        //总的商品数量
+        Integer totalNum = 0;
+
+        OrderAckBean orderAckBean = new OrderAckBean();
+        orderAckBean.setDiscountAmount(BigDecimal.valueOf(0));
+        orderAckBean.setOpenId(user.getOpenId());
+        List<AckGoodsBean> list = new ArrayList<>();
+        //业务员
+        User service = user.getType().equals(UserTypeEnum.SERVICE.toString()) ? user : null;
+        if (service == null) {
+            service = userService.getById(user.getServiceId());
+        }
+        //填充必要参数
+        List<BuyGood> buyGoods = this.supplyBuyBoods(orderBuyBean, user);
+        boolean isRecordBuyer = false;
+        for (BuyGood buyGood : buyGoods) {
+
+            Goods goods = buyGood.getGoods();
+            GoodsSpec goodsSpec = buyGood.getGoodsSpec();
+
+            if (StringUtils.isNotBlank(goods.getUmsDiscountCode()) && StringUtils.isNotBlank(goodsSpec.getBarCode())) {
+                isRecordBuyer = true;
+            }
+            //礼品卡商品处理
+            Boolean isGift = this.giftGoods(user.getUserId(), orderBuyBean.getExchangeCode(), false, goods, goodsSpec, buyGood.getNum());
+
+            //总购买数量
+            totalNum += buyGood.getNum();
+
+            AckGoodsBean ackGoodsBean = new AckGoodsBean();
+            BeanUtils.copyProperties(goods, ackGoodsBean);
+            ackGoodsBean.setSpecImgUrl(goodsSpec.getImgUrl());
+            ackGoodsBean.setSpecValue(goodsSpec.getSpecValue());
+            ackGoodsBean.setNum(buyGood.getNum());
+            ackGoodsBean.setPrice(buyGood.getPrice());
+            ackGoodsBean.setOrgPrice(goods.getOrgGoodsPrice());
+            ackGoodsBean.setSecKillId(buyGood.getSecKillId());
+            ackGoodsBean.setPromotionGroupId(buyGood.getPromotionGroupId());
+            if (isGift) {
+                //ackGoodsBean.setPrice(goodsSpec.getPrice());
+                ackGoodsBean.setIsGift(true);
+                hasGiftCode = true;
+
+                //商品需要支付的金额(累加小计金额)
+                totalAmount = totalAmount.add(this.getChildTotalPrice(ackGoodsBean, goodsSpec.getPrice()));
+            } else {
+                //商品需要支付的金额(累加小计金额)
+                totalAmount = totalAmount.add(this.getChildTotalPrice(ackGoodsBean));
+            }
+
+            //普通商品佣金
+            BigDecimal shareAmount = buyGood.getShareAmount();
+            //内部人员佣金
+            if (service != null && service.getType().equals(UserTypeEnum.SERVICE.toString()) && service.getInnerr()) {
+                shareAmount = goodsSpec.getInnerShareAmount();
+            }
+            //非团购商品的分佣金额
+            if (!buyGood.getIsPromotionGroup()) {
+                notPromotionShareAmount = notPromotionShareAmount.add(shareAmount.multiply(BigDecimal.valueOf(ackGoodsBean.getNum())));
+            }
+            if (buyGood.getIsPromotionGroup() && !isGoodsPackage)
+                isPromotionGroup = true;
+
+            list.add(ackGoodsBean);
+        }
+        //检查优惠券的使用合法性(团购和礼品卡和兑换码不可使用)
+        if (!isPromotionGroup && !hasGiftCode) {
+            List<String> goodsIds = buyGoods.stream().map(BuyGood::getGoodsId).collect(Collectors.toList());
+            UserCouponBean userCoupon = couponLogic.check(orderBuyBean.getUserCouponId(), totalAmount, goodsIds, false);
+            if (userCoupon != null && this.checkPackageUseCoupon(orderBuyBean.getGoodsPackageId(), userCoupon)) {
+                orderAckBean.setExchangeAmount(userCoupon.getCouponValue());
+            }
+        }
+        //优惠码的金额
+        if (!hasGiftCode && !isGoodsPackage) {
+            //兑换码商品(此处会重置规格的价格)
+            BigDecimal subAmount = this.subGoods(userId, exchangeCode, false, notPromotionShareAmount, null);
+            if (subAmount != null && subAmount.doubleValue() > 0) {
+                orderAckBean.setExchangeAmount(subAmount);
+            }
+        }
+        if (orderAckBean.getExchangeAmount() == null) {
+            orderAckBean.setExchangeAmount(BigDecimal.valueOf(0));
+        }
+        //计算折扣活动的金额(任何活动商品都可以参与,允许与优惠券/兑换码同用)-------------start
+        //1.订单商品的实际支付金额
+        BigDecimal payAmount = totalAmount.subtract(orderAckBean.getExchangeAmount());
+        //2.折扣活动 需要扣减的金额
+        if (!isGoodsPackage) {
+            PromotionDiscountBean promotionDiscountBean = this.discountPromotion(buyGoods, payAmount);
+            if (promotionDiscountBean != null) {
+                orderAckBean.setDiscountAmount(promotionDiscountBean.getSubDiscountAmount());
+                orderAckBean.setPromotionDiscountRate(promotionDiscountBean.getDiscountRate());
+            } else {
+                if (orderAckBean.getDiscountAmount() == null) {
+                    orderAckBean.setDiscountAmount(BigDecimal.valueOf(0));
+                }
+                orderAckBean.setPromotionDiscountRate(BigDecimal.valueOf(0));
+            }
+        }
+        //计算折扣活动的金额(任何活动商品都可以参与,允许与优惠券/兑换码同用)-------------end
+        orderAckBean.setGoods(list);
+        orderAckBean.setTotalAmount(totalAmount);
+        orderAckBean.setTotalNum(totalNum);
+        orderAckBean.setIsRecordBuyer(isRecordBuyer);
+        return orderAckBean;
+    }
+
+    /**
+     * 折扣活动处理
+     *
+     * @param buyGoods  购买的商品
+     * @param payAmount 实际支付的金额
+     */
+    private PromotionDiscountBean discountPromotion(List<BuyGood> buyGoods, BigDecimal payAmount) {
+
+        List<String> promotionIds = buyGoods.stream().filter(v -> StringUtils.isNotEmpty(v.getPromotionGroupId()))
+                .map(BuyGood::getPromotionGroupId)
+                .collect(Collectors.toList());
+
+        //全团购商品
+        Boolean allPromotionGroup = CollectionUtil.isNotEmpty(promotionIds) && promotionIds.size() == buyGoods.size();
+
+        BigDecimal discountAmount;
+        //1.判断是否达到折扣条件
+        List<PromotionDiscount> list = promotionDiscountService.lambdaQuery()
+                .ge(PromotionDiscount::getLimitUpperAmount, payAmount)
+                .le(PromotionDiscount::getLimitLowerAmount, payAmount)
+                .eq(PromotionDiscount::getStatus, true)
+                .in(allPromotionGroup, PromotionDiscount::getPromotionGroupId, promotionIds)
+                .eq(allPromotionGroup, PromotionDiscount::getType, 2)
+                .eq(!allPromotionGroup, PromotionDiscount::getType, 1)
+                .orderByAsc(PromotionDiscount::getDiscountRate)
+                .list();
+        if (CollectionUtil.isEmpty(list)) {
+            return null;
+        }
+        PromotionDiscount promotionDiscount = list.get(0);
+        //2.判断是否有非该团购活动之外的商品
+        long count = buyGoods.stream().filter(v -> !StringUtils.equals(v.getPromotionGroupId(), promotionDiscount.getPromotionGroupId())).count();
+        if (count > 0) {
+            throw new RemoteServiceException("检测到有非折扣商品,请联系相关人员");
+        }
+        BigDecimal subRate = BigDecimal.valueOf(1).subtract(promotionDiscount.getDiscountRate());
+        //实际折扣扣减的金额
+        discountAmount = payAmount.multiply(subRate).setScale(2, BigDecimal.ROUND_HALF_UP);
+
+        PromotionDiscountBean bean = new PromotionDiscountBean();
+        BeanUtils.copyProperties(promotionDiscount, bean);
+        bean.setSubDiscountAmount(discountAmount);
+        return bean;
+    }
+
+    /**
+     * 整理 购买商品 的必要参数
+     *
+     * @param orderBuyBean
+     * @param user
+     * @return
+     */
+    public List<BuyGood> supplyBuyBoods(OrderBuyBean orderBuyBean, User user) throws InterruptedException {
+        String goodsPackageId = orderBuyBean.getGoodsPackageId();
+
+        List<BuyGood> buyGoods = orderBuyBean.getBuyGoods();
+        //套购商品
+        Boolean isGoodsPackage = false;
+        if (StringUtils.isNotEmpty(goodsPackageId)) {
+            Goods goodsPackage = goodsLogic.getGoodsById(goodsPackageId);
+            if (goodsPackage != null) {
+                //套购商品检查
+                this.checkPackageGoods(buyGoods, user, goodsPackage);
+                //是否为套购商品
+                isGoodsPackage = StringUtils.equals(goodsPackage.getGoodsType(), GoodsTypeEnum.PACKAGE.toString());
+            }
+        }
+
+        for (BuyGood buyGood : buyGoods) {
+            //String promotionGroupId = buyGood.getPromotionGroupId();
+            Goods goods = goodsLogic.getGoodsById(buyGood.getGoodsId());
+            GoodsSpec goodsSpec = goodsLogic.getGoodsSpecById(buyGood.getGoodsSpecId());
+            if (goods == null || goodsSpec == null) {
+                throw new RemoteServiceException("商品不存在,请重新添加");
+            }
+            buyGood.setGoods(goods);
+            buyGood.setGoodsSpec(goodsSpec);
+
+            //非套购商品
+            if (!isGoodsPackage) {
+
+                buyGood.setOrgPrice(goodsSpec.getOrgPrice());
+                buyGood.setPrice(goodsSpec.getPrice());
+                buyGood.setShareAmount(goodsSpec.getShareAmount());
+                //团购活动
+                this.groupPromotion(buyGood, goodsSpec, user);
+
+            } else {
+                //套购商品
+                GoodsPackagePop goodsPackagePop = goodsPackagePopService.lambdaQuery()
+                        .eq(GoodsPackagePop::getGoodsPackageId, goodsPackageId)
+                        .eq(GoodsPackagePop::getGoodsSpecId, buyGood.getGoodsSpecId()).one();
+                buyGood.setOrgPrice(goodsSpec.getOrgPrice());
+                buyGood.setPrice(goodsPackagePop.getPrice());
+                buyGood.setShareAmount(goodsPackagePop.getShareRate().multiply(buyGood.getPrice()));
+                buyGood.setIsGoodsPackage(true);
+            }
+
+        }
+        return buyGoods;
+    }
+
+    /**
+     * 团购活动
+     *
+     * @param buyGood
+     * @param goodsSpec
+     */
+    private void groupPromotion(BuyGood buyGood, GoodsSpec goodsSpec, User user) {
+        String promotionGroupUserId = user.getPromotionGroupUserId();
+        if (user.getPromotionGroupLeader()) {
+            promotionGroupUserId = user.getUserId();
+        }
+
+        PromotionGroupSpecUser promotionGroupSpecUser = promotionGroupSpecUserService.lambdaQuery()
+                .eq(PromotionGroupSpecUser::getGoodsSpecId, goodsSpec.getGoodsSpecId())
+                .eq(PromotionGroupSpecUser::getUserId, promotionGroupUserId)
+                .ge(PromotionGroupSpecUser::getEndTime, new Date())
+                .le(PromotionGroupSpecUser::getStartTime, new Date())
+                .one();
+        if (promotionGroupSpecUser == null) {
+            return;
+        }
+        if (promotionGroupLogic.checkPromotionGroup(promotionGroupSpecUser.getPromotionGroupId())) {
+            buyGood.setPrice(promotionGroupSpecUser.getGroupPrice());
+            buyGood.setShareAmount(promotionGroupSpecUser.getShareAmount());
+            buyGood.setOrgPrice(promotionGroupSpecUser.getOrgGoodsPrice());
+            buyGood.setPromotionImgUrl(promotionGroupSpecUser.getGoodsImgSrc());
+            buyGood.setIsPromotionGroup(true);
+        }
+    }
+
+
+    /**
+     * 礼品卡商品
+     */
+    private Boolean giftGoods(String userId, String exchangeCode, Boolean order, Goods goods, GoodsSpec goodsSpec, int num) throws RemoteServiceException, InterruptedException {
+        if (StringUtils.isEmpty(exchangeCode)) {
+            return false;
+        }
+
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_GIFT + ":" + exchangeCode);
+        if (!obtain.tryLock(5, TimeUnit.SECONDS)) {
+            throw new RemoteServiceException("系统繁忙,请稍后再试");
+        }
+        try {
+            GiftExchangeCode giftExchangeCode = giftExchangeCodeService.getById(exchangeCode);
+            if (giftExchangeCode == null)
+                return false;
+            User user = userService.getById(userId);
+            if (user.getExchangeNum() > 10) {
+                throw new RemoteServiceException(1100, "该用户已被禁用兑换码");
+            }
+            if (giftExchangeCode.getType().equals(ExchangeCodeTypeEnum.GIFT.toString())) {
+                //礼品兑换码
+                if (goods == null || goodsSpec == null) {
+                    throw new RemoteServiceException("商品不存在");
+                }
+                BigDecimal amount = this.giftExchangeCodeGoods(giftExchangeCode, goods.getGoodsId(), user, num, order);
+                goodsSpec.setPrice(amount);
+                return true;
+            }
+        } finally {
+            obtain.unlock();
+        }
+        return false;
+    }
+
+
+    /**
+     * 抵扣兑换码商品
+     */
+    private BigDecimal subGoods(String userId, String exchangeCode, Boolean order, BigDecimal totalShareAmount, String orderId) throws RemoteServiceException, InterruptedException {
+        if (StringUtils.isEmpty(exchangeCode)) {
+            return BigDecimal.valueOf(0);
+        }
+
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_GIFT + ":" + exchangeCode);
+        if (!obtain.tryLock(5, TimeUnit.SECONDS)) {
+            throw new RemoteServiceException("系统繁忙,请稍后再试");
+        }
+        try {
+            GiftExchangeCode giftExchangeCode = giftExchangeCodeService.getById(exchangeCode);
+            if (giftExchangeCode == null)
+                return BigDecimal.valueOf(0);
+            User user = userService.getById(userId);
+            if (user.getExchangeNum() > 10) {
+                throw new RemoteServiceException(1100, "该用户已被禁用兑换码");
+            }
+            if (giftExchangeCode.getType().equals(ExchangeCodeTypeEnum.SUB.toString()) && totalShareAmount != null) {
+                //金额抵扣码
+                return this.subExchangeCodeGoods(giftExchangeCode, user, order, totalShareAmount, orderId);
+            }
+        } finally {
+            obtain.unlock();
+        }
+        return BigDecimal.valueOf(0);
+    }
+
+
+    /**
+     * 礼品卡兑换码
+     */
+    private BigDecimal giftExchangeCodeGoods(GiftExchangeCode giftExchangeCode, String goodsId, User user, Integer num, Boolean order) {
+        if (giftExchangeCode == null || giftExchangeCode.getStatus() == 1 || giftExchangeCode.getStartTime().getTime() > new Date().getTime()
+                || giftExchangeCode.getEndTime().getTime() < new Date().getTime() || !giftExchangeCode.getGoodsId().equals(goodsId)) {
+            user.setExchangeNum(user.getExchangeNum() + 1);
+            user.updateById();
+            throw new RemoteServiceException(1100, "兑换码无效");
+        }
+        if (num > 1)
+            throw new RemoteServiceException(1100, "该兑换码只支持购买1个商品");
+        //生成订单需要核销
+        if (order) {
+            //分享人id
+//            String userId = giftExchangeCode.getUserId();
+//            User service = userService.getById(userId);
+//            if(service != null) {
+//                giftExchangeCode.setNickName(service.getWorkName());
+//                giftExchangeCode.setAvatar(service.getAvatar());
+//                giftExchangeCode.setWebsitId()
+//            }
+            //使用人
+            giftExchangeCode.setUseUserId(user.getUserId());
+            giftExchangeCode.setUseNickName(user.getNickName());
+            giftExchangeCode.setUseTime(new Date());
+            giftExchangeCode.setUseAvatar(user.getAvatar());
+            giftExchangeCode.setCompanyWechatId(user.getCompanyWechatId());
+            giftExchangeCode.setCompanyName(user.getCompanyName());
+
+            giftExchangeCode.setStatus(1);
+            giftExchangeCode.updateById();
+
+            user.setExchangeNum(0);
+            user.updateById();
+        }
+        return giftExchangeCode.getAmount();
+    }
+
+
+    /**
+     * 抵扣兑换码
+     *
+     * @return 可抵扣的金额
+     * @throws InterruptedException
+     */
+    private BigDecimal subExchangeCodeGoods(GiftExchangeCode giftExchangeCode, User user, Boolean order, BigDecimal totalShareAmount, String orderId) throws InterruptedException {
+        String serviceId = user.getServiceId();
+        if (user.getType().equals(UserTypeEnum.SERVICE.toString())) {
+            serviceId = user.getUserId();
+        }
+        if (giftExchangeCode == null || giftExchangeCode.getStatus() == 1 || giftExchangeCode.getStartTime().getTime() > new Date().getTime()
+                || giftExchangeCode.getEndTime().getTime() < new Date().getTime() || !giftExchangeCode.getUserId().equals(serviceId)) {
+            user.setExchangeNum(user.getExchangeNum() + 1);
+            user.updateById();
+            throw new RemoteServiceException(1100, "兑换码无效");
+        }
+        if (giftExchangeCode.getAmount().doubleValue() > totalShareAmount.doubleValue()) {
+            throw new RemoteServiceException(1100, "兑换码不适用目前所购买的商品");
+        }
+        //生成订单需要核销
+        if (order) {
+
+            //使用人
+            giftExchangeCode.setUseUserId(user.getUserId());
+            giftExchangeCode.setUseNickName(user.getNickName());
+            giftExchangeCode.setUseTime(new Date());
+            giftExchangeCode.setUseAvatar(user.getAvatar());
+
+            giftExchangeCode.setOrderId(orderId);
+            giftExchangeCode.setCompanyWechatId(user.getCompanyWechatId());
+            giftExchangeCode.setCompanyName(user.getCompanyName());
+            giftExchangeCode.setStatus(1);
+            giftExchangeCode.updateById();
+
+            user.setExchangeNum(0);
+            user.updateById();
+        }
+        return giftExchangeCode.getAmount();
+    }
+
+    /**
+     * 已开发票
+     */
+    public void tax(String orderId, String orderTaxId) {
+        orderInfoService.lambdaUpdate()
+                .set(OrderInfo::getTax, true)
+                .set(OrderInfo::getOrderTaxId, orderTaxId)
+                .eq(OrderInfo::getOrderId, orderId).update();
+    }
+
+    //获取商品小计金额
+    private BigDecimal getChildTotalPrice(AckGoodsBean ackGoodsBean) {
+        return ackGoodsBean.getPrice().multiply(new BigDecimal(ackGoodsBean.getNum()));
+    }
+
+    private BigDecimal getChildTotalPrice(AckGoodsBean ackGoodsBean, BigDecimal price) {
+        return price.multiply(new BigDecimal(ackGoodsBean.getNum()));
+    }
+
+    /**
+     * 套购--检查是否可使用优惠券
+     */
+    private Boolean checkPackageUseCoupon(String goodsPackageId, UserCoupon userCoupon) {
+        if (StringUtils.isEmpty(goodsPackageId)) {
+            return true;
+        }
+        //使用优惠券   0=不可使用 1=所有优惠券 2=满减券 3=商品券
+        Goods goods = goodsLogic.getGoodsById(goodsPackageId);
+        if (!StringUtils.equals(goods.getGoodsType(), GoodsTypeEnum.PACKAGE.toString())) {
+            return true;
+        }
+        Integer useCoupon = goods.getUseCoupon();
+        if (useCoupon == 0) {
+            throw new RemoteServiceException("当前套购商品不可使用优惠券");
+        }
+        if (useCoupon == 2 && !userCoupon.getCouponType().equals(CouponTypeEnum.SATISFY.toString())) {
+            throw new RemoteServiceException("当前套购商品不可使用满减券");
+        }
+        if (useCoupon == 3 && !userCoupon.getCouponType().equals(CouponTypeEnum.GOODS.toString())) {
+            throw new RemoteServiceException("当前套购商品不可使用商品券");
+        }
+        return true;
+    }
+
+    /**
+     * 套购商品
+     */
+    public void checkPackageGoods(List<BuyGood> buyGoods, User user, Goods goodsPackage) {
+        List<GoodsPackagePop> list = goodsPackagePopService.lambdaQuery()
+                .eq(GoodsPackagePop::getGoodsPackageId, goodsPackage.getGoodsId())
+                .orderByAsc(GoodsPackagePop::getType).list();
+        //判断是否都为套购内商品
+        Map<String, GoodsPackagePop> goodsSpecMap = list.stream().collect(Collectors.toMap(GoodsPackagePop::getGoodsSpecId, v -> v));
+        for (BuyGood buyGood : buyGoods) {
+            Goods goods = goodsLogic.getGoodsById(buyGood.getGoodsId());
+            GoodsSpec goodsSpec = goodsLogic.getGoodsSpecById(buyGood.getGoodsSpecId());
+            GoodsPackagePop goodsPackagePop = goodsSpecMap.get(buyGood.getGoodsSpecId());
+            if (goodsPackagePop == null) {
+                throw new RemoteServiceException("存在不包含当前套购的商品");
+            }
+            //检查是否超出了购买上限
+            if (buyGood.getNum() > goodsPackagePop.getLimitNum()) {
+                throw new RemoteServiceException(goods.getGoodsName() + "-" + goodsSpec.getSpecValue() + "最大支持购买" + goodsPackagePop.getLimitNum() + "个");
+            }
+        }
+        //检查配比是否正确
+        String[] splitPop = goodsPackage.getPackagePop().split(":");
+        this.checkPackagePop(buyGoods, splitPop);
+        //检查是否有权限购买
+        this.checkPackagePower(user, goodsPackage);
+    }
+
+    /**
+     * 套购--检查配比是否正确
+     *
+     * @param list
+     * @param splitPop 配比比例
+     */
+    private void checkPackagePop(List<BuyGood> list, String[] splitPop) {
+        //检查配提购买的机型是否大于等于限定机型购买的倍数,
+        //倍数 num = 购买的数量 / 所属比例
+        double num = -1;
+        for (int i = 1; i <= splitPop.length; i++) {
+            int a = i;
+            int rate = Integer.parseInt(splitPop[i - 1]);
+            int sum = list.stream().filter(v -> v.getPopType() == a).mapToInt(BuyGood::getNum).sum();
+            double div = sum / rate;
+            if (div == 0)
+                throw new RemoteServiceException("检查到配比不正确");
+
+            if (sum % rate != 0)
+                throw new RemoteServiceException("购买数量必须是套餐对应比例的整倍数");
+
+            if (num != -1 && num != div)
+                throw new RemoteServiceException("检查到配比不正确");
+
+            if (i == 1)
+                num = div;
+        }
+    }
+
+    /**
+     * 套购--检查是否有权限购买
+     */
+    private void checkPackagePower(User user, Goods goods) {
+        if (!StringUtils.equals(goods.getGoodsType(), GoodsTypeEnum.PACKAGE.toString())) {
+            return;
+        }
+        // 0=指定人员 1=所有人 2=普通用户 3=业务员
+        if (goods.getPackageUserType() == null) {
+            return;
+        }
+        //指定人员
+        if (goods.getPackageUserType() == 0) {
+            Long count = goodsPackageUserRelaService.lambdaQuery()
+                    .eq(GoodsPackageUserRela::getGoodsId, goods.getGoodsId()).eq(GoodsPackageUserRela::getUserId, user.getUserId()).count();
+            if (count > 0) {
+                return;
+            }
+        }
+        //所有人
+        if (goods.getPackageUserType() == 1) {
+            return;
+        }
+        //普通用户
+        if (goods.getPackageUserType() == 2 && StringUtils.equals(user.getType(), UserTypeEnum.GENERAL.toString())) {
+            return;
+        }
+        //业务员
+        if (goods.getPackageUserType() == 3 && StringUtils.equals(user.getType(), UserTypeEnum.SERVICE.toString())) {
+            return;
+        }
+        throw new RemoteServiceException("暂不可购买该商品");
+    }
+
+    /**
+     * 立即购买
+     */
+    @Transactional
+    public PayDetail buy(OrderBuyBean orderBuyBean, HttpServletRequest request) throws Exception {
+        String ip = IpUtil.getIpAddr(request);
+        if (StringUtils.isEmpty(orderBuyBean.getUserAddressId())) {
+            throw new RemoteServiceException(1100, "请选择收货地址");
+        }
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        //订单
+        OrderInfo orderInfo = new OrderInfo();
+        String orderId = currentCompanyWechat.getOrderPrefix() + IdWorker.getIdStr();
+        orderInfo.setOrderId(orderId);
+        orderInfo.setPayType("微信支付");
+        //购买人
+        String userId = currentCompanyWechat.getUserId();
+        User user = currentCompanyWechat.getUser();
+
+        //判断该手机号是否为业务员
+        user = userLogic.userToService(user, currentCompanyWechat);
+
+        //分销员
+        String serviceId = user.getServiceId();
+        if (user.getType().equals(UserTypeEnum.SERVICE.toString())) {
+            serviceId = user.getUserId();
+        }
+        User service = userService.getById(serviceId);
+        //商品总金额(不包含运费,优惠金额,非实际支付金额)
+        BigDecimal totalPrice = BigDecimal.valueOf(0);
+        //运费总额
+        BigDecimal totalFreight = BigDecimal.valueOf(0);
+        //订单标题
+        String orderTitle = "";
+        //分账金额
+        BigDecimal shareAmount = BigDecimal.valueOf(0);
+        //非团购商品的分账金额
+        BigDecimal notPromotionShareAmount = BigDecimal.valueOf(0);
+        //总数量
+        Integer totalNum = 0;
+        //兑换码
+        String exchangeCode = orderBuyBean.getExchangeCode();
+        //是否有使用了礼品码的商品
+        Boolean hasGiftCode = false;
+        //收获地址
+        UserAddress userAddress = userAddressService.getById(orderBuyBean.getUserAddressId());
+        //团长id
+        String promotionGroupUserId = "";
+        String orderPromotionGroupId = "";
+        //是否为套购商品
+        Boolean isGoodsPackage = StringUtils.isNotEmpty(orderBuyBean.getGoodsPackageId());
+        //是否有活动商品
+        Boolean isPromotion = false;
+        List<String> goodsIds = orderBuyBean.getBuyGoods().stream().map(BuyGood::getGoodsId).collect(Collectors.toList());
+        //购物车ids
+        List<String> shoppingCartIds = orderBuyBean.getBuyGoods().stream().map(BuyGood::getShoppingCartId).collect(Collectors.toList());
+        //如果用户存在,则判断是否为业务员,并更新相关信息
+        if (!user.getStatus()) {
+            throw new RemoteServiceException(1100, "用户已被冻结");
+        }
+
+        //这里面会处理各个活动对单价以及佣金金额的处理
+        List<BuyGood> buyGoods = this.supplyBuyBoods(orderBuyBean, user);
+
+        // 云闪付优惠码
+        boolean isExistCode = false;
+
+        //订单详情
+        List<OrderDetail> orderDetails = new ArrayList<>();
+        for (BuyGood buyGood : buyGoods) {
+            String promotionGroupId = buyGood.getPromotionGroupId();
+            if (StringUtils.isEmpty(orderPromotionGroupId)) {
+                orderPromotionGroupId = buyGood.getPromotionGroupId();
+            }
+            String secKillId = buyGood.getSecKillId();
+            //商品
+            Goods goods = buyGood.getGoods();
+            if (!goods.getStatus()) {
+                throw new RemoteServiceException("存在已下架商品:" + goods.getGoodsName());
+            }
+            //处理库存并返回商品规格
+            GoodsSpec goodsSpec = this.stock(buyGood.getGoodsSpecId(), goods.getGoodsName(), buyGood.getNum());
+            //礼品卡商品(此处会重置规格的价格)
+            Boolean isGift = this.giftGoods(userId, exchangeCode, true, goods, goodsSpec, buyGood.getNum());
+            if (!hasGiftCode)
+                hasGiftCode = isGift;
+
+            String goodsId = goodsSpec.getGoodsId();
+            String goodsSpecId = goodsSpec.getGoodsSpecId();
+            String goodsSpecName = goodsSpec.getName();
+            String goodsSpecValue = goodsSpec.getSpecValue();
+            String goodsName = goods.getGoodsName();
+            //BigDecimal goodsShareAmount = goodsSpec.getShareAmount();
+            //BigDecimal goodsSecPrice = goodsSpec.getPrice();
+            BigDecimal goodsShareAmount = buyGood.getShareAmount();
+            BigDecimal goodsSecPrice = buyGood.getPrice();
+            String catagoryId = goods.getCategoryId();
+            String userCouponId = orderBuyBean.getUserCouponId();
+            BigDecimal cutAmount = new BigDecimal("0");
+
+            //内部价格处理
+            if (service != null
+                    && service.getType().equals(UserTypeEnum.SERVICE.toString())
+                    && service.getInnerr()
+                    && !buyGood.getIsPromotionGroup()
+                    && !buyGood.getIsSecKill()
+                    && !buyGood.getIsGoodsPackage()) {
+                goodsShareAmount = goodsSpec.getInnerShareAmount();
+            }
+
+            log.info("是否为礼品卡商品:{},金额:{}", isGift, goodsSecPrice);
+
+            //活动和优惠券不能一起使用
+            if (StringUtils.isNotEmpty(userCouponId)
+                    //               && (StringUtils.isNotEmpty(promotionGroupId) || StringUtils.isNotEmpty(secKillId) || isGift || StringUtils.isNotEmpty(exchangeCode))){
+                    && (isGift || StringUtils.isNotEmpty(exchangeCode) || buyGood.getIsPromotionGroup())) {
+                throw new RemoteServiceException(1100, "活动商品不可使用优惠券");
+            }
+
+            OrderDetail orderDetail = new OrderDetail();
+            //默认的分佣人是分销员
+            orderDetail.setShareUserId(serviceId);
+            orderDetail.setUmsDiscountCode(goods.getUmsDiscountCode());
+            orderDetail.setBarCode(goodsSpec.getBarCode());
+            if (StringUtils.isNotBlank(goods.getUmsDiscountCode()) && StringUtils.isNotBlank(goodsSpec.getBarCode())) {
+                isExistCode = true;
+            }
+
+            //团购活动
+            if (buyGood.getIsPromotionGroup()) {
+                PromotionGroupSpecUser promotionGroupSpecUser = promotionGroupLogic.getStock(user.getPromotionGroupUserId(), buyGood.getGoodsSpecId(), buyGood.getNum());
+                promotionGroupUserId = promotionGroupSpecUser.getUserId();
+                isPromotion = true;
+
+                orderDetail.setPromotionGroupId(promotionGroupId);
+                orderDetail.setShareUserId(promotionGroupUserId);
+            }
+
+            orderDetail.setOrderId(orderId);
+            orderDetail.setPromotionGroupId(promotionGroupId);
+            orderDetail.setGoodsId(goodsId);
+            orderDetail.setGoodsName(goodsName);
+            orderDetail.setGoodsSpecId(goodsSpecId);
+            orderDetail.setGoodsSpecName(goodsSpecName);
+            orderDetail.setGoodsSpecValue(goodsSpecValue);
+            orderDetail.setIsGift(isGift);
+
+            if (StringUtils.isNotEmpty(catagoryId)) {
+                GoodsCategory goodsCategory = goodsCategoryService.getById(catagoryId);
+                if (goodsCategory != null) {
+                    orderDetail.setGoodsCategoryId(goodsCategory.getCategoryId());
+                    orderDetail.setGoodsCategoryName(goodsCategory.getName());
+                }
+            }
+            orderDetail.setPrice(goodsSecPrice);
+            orderDetail.setNeedPayAmount(goodsSecPrice);
+            orderDetail.setPayAmount(goodsSecPrice.multiply(new BigDecimal(buyGood.getNum())));
+            orderDetail.setNum(buyGood.getNum());
+            orderDetail.setTotalPrice(goodsSecPrice.multiply(new BigDecimal(buyGood.getNum())));
+            orderDetail.setShareAmount(goodsShareAmount.multiply(new BigDecimal(buyGood.getNum())));
+            orderDetail.setCutAmount(cutAmount);
+            orderDetail.setTotalCutAmount(cutAmount.multiply(new BigDecimal(buyGood.getNum())));
+            orderDetail.setProfitAmount(orderDetail.getTotalPrice().subtract(orderDetail.getShareAmount()));
+            orderDetail.setImgUrl(goodsSpec.getImgUrl());
+            orderDetail.setCreateTime(new Date());
+            orderDetail.setMarketingId(goods.getMarketingId());
+            orderDetail.setMarketingName(goods.getMarketingName());
+            BigDecimal freightAmountItem = this.getFreightAmountItem(userAddress.getProvince(), userAddress.getCity(), userAddress.getArea(), buyGood, currentCompanyWechat);
+            orderDetail.setFreight(freightAmountItem);
+
+            orderDetails.add(orderDetail);
+
+            if (!isGift) {
+                //非礼品卡商品
+                totalPrice = totalPrice.add(goodsSecPrice.multiply(new BigDecimal(buyGood.getNum())));
+            } else {
+                //礼品卡商品
+                totalPrice = totalPrice.add(goodsSpec.getPrice().multiply(new BigDecimal(buyGood.getNum())));
+            }
+
+            totalFreight = totalFreight.add(freightAmountItem);
+            orderTitle += goods.getGoodsName() + ",";
+            totalNum += buyGood.getNum();
+            orderTitle += goods.getGoodsName() + ",";
+            shareAmount = orderDetail.getShareAmount().add(shareAmount);
+            if (StringUtils.isEmpty(orderDetail.getPromotionGroupId())) {
+                notPromotionShareAmount = notPromotionShareAmount.add(orderDetail.getShareAmount());
+            }
+        }
+        if (!orderBuyBean.getWechatPay() && isExistCode && orderDetails.size() > 1) {
+            // 云闪付支付的参加以旧换新商品只能单品下单
+            throw new RemoteServiceException("订单存在“以旧换新”活动商品,只能单品下单");
+        }
+        if (!orderBuyBean.getWechatPay() && isExistCode && orderDetails.size() == 1 && orderDetails.get(0).getNum() > 1) {
+            // 云闪付支付的参加以旧换新商品只能数量=1单品下单
+            throw new RemoteServiceException("订单存在“以旧换新”活动商品,只能数量“1”单品下单");
+        }
+        //补充网点师傅相关信息
+        this.supplyWorkUser(orderInfo, user);
+
+        //实际需要支付的金额(包含运费)
+        BigDecimal payAmount = totalPrice.add(totalFreight);
+
+        //抵扣的兑换码
+        BigDecimal subAmount = new BigDecimal("0");
+        if (StringUtils.isNotEmpty(exchangeCode) && !hasGiftCode) {
+            if (service == null || !service.getType().equals(UserTypeEnum.SERVICE.toString())) {
+                throw new RemoteServiceException(1100, "无效的兑换码");
+            }
+            //兑换码商品
+            subAmount = this.subGoods(userId, exchangeCode, true, notPromotionShareAmount, orderId);
+            payAmount = payAmount.subtract(subAmount);
+            shareAmount = shareAmount.subtract(subAmount);
+            hasGiftCode = true;
+
+            //处理订单详情的抵扣金额明细
+            this.supplyOrderDetail(orderDetails, subAmount, notPromotionShareAmount);
+        }
+
+        //生成订单
+        orderInfo.setBuyerMsg(orderBuyBean.getBuyerMsg());
+        orderInfo.setOrderStatus(OrderStatusEnum.NOPAY.toString());
+        orderInfo.setUserId(user.getUserId());
+        orderInfo.setUserName(user.getNickName());
+        orderInfo.setPhone(user.getMobile());
+        orderInfo.setExchangeCode(exchangeCode);
+        orderInfo.setExchangeSubAmount(subAmount);
+        orderInfo.setPromotionGroupId(orderPromotionGroupId);
+        orderInfo.setPromotionGroupUserId(promotionGroupUserId);
+        orderInfo.setReceUserName(userAddress.getName());
+        orderInfo.setReceAddress(userAddress.getAddress());
+        orderInfo.setRecePhone(userAddress.getPhone());
+        orderInfo.setProvince(userAddress.getProvince());
+        orderInfo.setCity(userAddress.getCity());
+        orderInfo.setArea(userAddress.getArea());
+        orderInfo.setStreet(userAddress.getStreet());
+        orderInfo.setHouseNo(userAddress.getHouseNo());
+        orderInfo.setCreateTime(new Date());
+        orderInfo.setFreight(totalFreight);
+        orderInfo.setTotalAmount(totalPrice);
+        orderInfo.setPayAmount(payAmount);
+        orderInfo.setPromotionPackageGoodsId(orderBuyBean.getGoodsPackageId());
+        orderInfo.setBuyerName(orderBuyBean.getBuyerName());
+        orderInfo.setBuyerIdCard(orderBuyBean.getBuyerIdCard());
+        orderInfo.setBuyerMobile(user.getMobile());
+        //检查优惠券的使用合法性
+
+        if (!isPromotion && !hasGiftCode) {
+            UserCouponBean userCoupon = couponLogic.check(orderBuyBean.getUserCouponId(), totalPrice, goodsIds, true);
+            if (userCoupon != null && this.checkPackageUseCoupon(orderBuyBean.getGoodsPackageId(), userCoupon)) {
+                orderInfo.setDiscountAmount(userCoupon.getCouponValue());
+                orderInfo.setCouponId(userCoupon.getCouponId());
+                orderInfo.setCouponName(userCoupon.getCouponName());
+                orderInfo.setCouponValue(userCoupon.getCouponValue());
+                orderInfo.setUserCouponId(userCoupon.getId());
+
+                //检查是否优惠超过50%,如果超过,拒绝下单
+                BigDecimal divide = totalPrice.divide(userCoupon.getCouponValue(), 3, RoundingMode.HALF_UP);
+                if (divide.doubleValue() < 2) {
+                    throw new RemoteServiceException("优惠券抵扣额度不可超订单价格50%");
+                }
+
+                //扣减优惠券金额
+                orderInfo.setPayAmount(
+                        orderInfo.getPayAmount().subtract(orderInfo.getDiscountAmount()).doubleValue()
+                                < 0 ? BigDecimal.valueOf(0)
+                                : orderInfo.getPayAmount().subtract(orderInfo.getDiscountAmount()));
+            }
+        }
+        orderInfo.setTotalNum(totalNum);
+        orderInfo.setTotalShareAmount(shareAmount);
+        orderInfo.setOrderTitle(orderTitle);
+        orderInfo.setTotalProductAmount(totalPrice);
+        orderInfo.setCompanyWechatId(currentCompanyWechat.getCompanyWechatId());
+        orderInfo.setCompanyWechatName(currentCompanyWechat.getCompanyName());
+        orderInfo.setDispatchType(orderBuyBean.getDispatchType());
+        orderInfo.setClientIp(ip);
+        //团购的折扣活动
+        if (!isGoodsPackage) {
+            PromotionDiscountBean promotionDiscountBean = this.discountPromotion(orderBuyBean.getBuyGoods(), orderInfo.getPayAmount());
+            //减掉折扣优惠金额
+            if (promotionDiscountBean != null) {
+                payAmount = orderInfo.getPayAmount().subtract(promotionDiscountBean.getSubDiscountAmount());
+                orderInfo.setPromotionDiscountId(promotionDiscountBean.getId());
+                orderInfo.setPromotionDiscountLimitAmount(promotionDiscountBean.getLimitUpperAmount());
+                orderInfo.setPromotionDiscountAmount(promotionDiscountBean.getSubDiscountAmount());
+                orderInfo.setPromotionDiscountRate(promotionDiscountBean.getDiscountRate());
+                orderInfo.setPayAmount(payAmount);
+            }
+        }
+
+        orderInfoService.save(orderInfo);
+        //新增订单详情
+        orderDetailService.saveBatch(orderDetails);
+        //检查清掉购物车
+        if (CollectionUtil.isNotEmpty(shoppingCartIds)) {
+            shoppingCartService.lambdaUpdate().in(ShoppingCart::getShoppingCartId, shoppingCartIds).remove();
+        }
+        //代客下单
+        if (orderBuyBean.getProxyUser()) {
+            PayDetail payDetail = new PayDetail();
+            payDetail.setIsPay(false);
+            payDetail.setId(orderInfo.getOrderId());
+            payDetail.setCodeUrl(wechatLogic.getQrcode(QrCodeEnum.ORDER.toString().toLowerCase(Locale.ROOT),
+                    orderInfo.getOrderId(), orderInfo.getUserId(), user.getCompanyWechatId()));
+            return payDetail;
+        }
+        //调起支付
+        String profitSharingFlag = "N";
+        BigDecimal zero = new BigDecimal("0.00");
+        if (shareAmount.compareTo(zero) > 0) {
+            profitSharingFlag = "Y";
+        }
+
+        PayDetail payDetail = new PayDetail();
+        payDetail.setId(orderInfo.getOrderId());
+
+
+        payDetail = wechatLogic.payment(orderInfo.getOrderId(), orderInfo.getPayAmount(), user.getOpenId(),
+                profitSharingFlag, ip, user.getCompanyWechatId());
+        //不需要支付
+        if (!payDetail.getIsPay()) {
+            payLogic.payCall(payDetail.getId(), "", BigDecimal.valueOf(0));
+        }
+
+        return payDetail;
+    }
+
+    /**
+     * 处理商品规格库存
+     *
+     * @param goodsSpecId
+     * @param goodsName
+     * @param num
+     * @return
+     * @throws RemoteServiceException
+     * @throws InterruptedException
+     */
+    private GoodsSpec stock(String goodsSpecId, String goodsName, Integer num) throws RemoteServiceException, InterruptedException {
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_ORDER_STOCK + ":" + goodsSpecId);
+        try {
+            if (!obtain.tryLock(10, TimeUnit.SECONDS)) {
+                throw new RemoteServiceException("系统繁忙,请稍后再试");
+            }
+            GoodsSpec goodsSpec = goodsLogic.getGoodsSpecById(goodsSpecId);
+            if (goodsSpec.getStockNum() < num) {
+                throw new RemoteServiceException(1100, goodsName + "库存不足");
+            }
+            goodsSpec.setStockNum(goodsSpec.getStockNum() - num);
+            goodsSpec.setSoldNum(goodsSpec.getSoldNum() + num);
+            goodsSpec.updateById();
+
+            return goodsSpec;
+        } finally {
+            obtain.unlock();
+        }
+    }
+
+    /**
+     * 还原库存
+     */
+    private void recoveryStock(String goodsSpecId, Integer num) {
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_ORDER_STOCK + goodsSpecId);
+        try {
+            if (!obtain.tryLock(10, TimeUnit.SECONDS)) {
+                log.error("还原库存获取锁失败");
+                return;
+            }
+            GoodsSpec goodsSpec = goodsLogic.getGoodsSpecById(goodsSpecId);
+            goodsSpec.setStockNum(goodsSpec.getStockNum() + num);
+            goodsSpec.updateById();
+        } catch (Exception e) {
+            log.info("还原库存失败goodsSpecId:" + goodsSpecId, e);
+        } finally {
+            obtain.unlock();
+        }
+    }
+
+    /**
+     * 订单数量
+     */
+    public OrderStatusBean count(String orderId) {
+        OrderStatusBean orderStatusBean = appMapper.orderStatusCount(orderId);
+        return orderStatusBean;
+    }
+
+    /**
+     * 补充网点师傅信息
+     *
+     * @param orderInfo
+     * @param user
+     */
+    private void supplyWorkUser(OrderInfo orderInfo, User user) {
+        if (user.getType().equals(UserTypeEnum.SERVICE.toString())) {
+            final AdminWebsit adminWebsit = adminWebsitService.lambdaQuery()
+                    .eq(AdminWebsit::getCompanyWechatId, user.getCompanyWechatId())
+                    .eq(AdminWebsit::getWebsitId, user.getWebsitId())
+                    .one();
+            orderInfo.setWorkerId(user.getWorkUserId());
+            orderInfo.setWorkerName(user.getWorkName());
+            orderInfo.setWorkerPhone(user.getWorkPhone());
+            orderInfo.setWebsitId(user.getWebsitId() + "");
+            orderInfo.setWebsitNumber(Objects.nonNull(adminWebsit) ? adminWebsit.getWebsitNumber() : null);
+            orderInfo.setWebsitName(user.getWebsitName());
+            orderInfo.setPosition(user.getPosition());
+
+        } else {
+            //非业务员那就找到业务员
+            if (StringUtils.isNotEmpty(user.getServiceId())) {
+                User workUser = userService.getById(user.getServiceId());
+                if (workUser != null) {
+                    final AdminWebsit adminWebsit = adminWebsitService.lambdaQuery()
+                            .eq(AdminWebsit::getCompanyWechatId, workUser.getCompanyWechatId())
+                            .eq(AdminWebsit::getWebsitId, workUser.getWebsitId())
+                            .one();
+
+                    orderInfo.setWorkerId(workUser.getWorkUserId());
+                    orderInfo.setWorkerName(workUser.getWorkName());
+                    orderInfo.setWorkerPhone(workUser.getWorkPhone());
+                    orderInfo.setPosition(workUser.getPosition());
+                    orderInfo.setWebsitId(workUser.getWebsitId());
+                    orderInfo.setWebsitName(workUser.getWebsitName());
+                    orderInfo.setWebsitNumber(Objects.nonNull(adminWebsit) ? adminWebsit.getWebsitNumber() : null);
+
+                    if (workUser.getCustomerServiceFlag() != null && workUser.getCustomerServiceFlag()) {
+                        orderInfo.setSource("svip邀请");
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 支付待支付订单
+     */
+    @Transactional
+    public PayDetail waitPayOrder(String userId, String orderId, String ip, String payType, String buyerName,
+                                  String buyerIdCard, HttpServletRequest request) throws Exception {
+        OrderInfo orderInfo = orderInfoService.getById(orderId);
+        //订单
+        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat(request);
+        String newOrderId = currentCompanyWechat.getOrderPrefix() + IdWorker.getIdStr();
+
+        if (orderInfo.getPromotionApplyStatus() != null && orderInfo.getPromotionApplyStatus() == 1) {
+            throw new RemoteServiceException("正在申请优惠中请耐心等待");
+        }
+        if (StringUtils.isNotEmpty(userId) && !userId.equals(orderInfo.getUserId())) {
+            User user = userService.getById(userId);
+            if (user.getType().equals(UserTypeEnum.SERVICE.toString())) {
+                throw new RemoteServiceException("业务员不可替业务员下单");
+            }
+
+            //绑定客户关系
+            if (StringUtils.isEmpty(user.getServiceId())) {
+                user.setServiceId(orderInfo.getUserId());
+                user.setServiceTime(new Date());
+                user.updateById();
+            }
+
+            if (!orderInfo.getOrderStatus().equals(OrderStatusEnum.NOPAY.toString())) {
+                throw new RemoteServiceException("订单已超时取消或已支付成功");
+            }
+
+            //将订单绑定给用户
+            orderInfo.setUserId(userId);
+            orderInfo.setUserName(user.getNickName());
+            orderInfo.setPhone(user.getMobile());
+
+            orderInfo.updateById();
+
+        }
+
+        orderInfoService.lambdaUpdate()
+                .set(OrderInfo::getOrderId, newOrderId)
+                .set(StringUtils.isNotBlank(buyerName), OrderInfo::getBuyerName, buyerName)
+                .set(StringUtils.isNotBlank(buyerIdCard), OrderInfo::getBuyerIdCard, buyerIdCard)
+                .set(OrderInfo::getClientIp, ip)
+                .eq(OrderInfo::getOrderId, orderId)
+                .update();
+        orderDetailService.lambdaUpdate().set(OrderDetail::getOrderId, newOrderId).eq(OrderDetail::getOrderId, orderId).update();
+
+        User user = userService.getById(orderInfo.getUserId());
+        String profitSharingFlag = "N";
+        BigDecimal zero = new BigDecimal("0.00");
+        if (orderInfo.getTotalShareAmount().compareTo(zero) > 0) {
+            profitSharingFlag = "Y";
+        }
+
+        return wechatLogic.payment(newOrderId, orderInfo.getPayAmount(), user.getOpenId(),
+                profitSharingFlag, ip, user.getCompanyWechatId());
+
+    }
+
+
+    /**
+     * 订单列表
+     *
+     * @return
+     */
+    public IPage<OrderDetailBean> list(String userId, String orderStatus, Integer promotionApplyStatus, Integer pageNum, Integer pageSize) {
+        User user = userService.getById(userId);
+        List<Integer> promotionApplyStatusList = new ArrayList<>();
+        if (promotionApplyStatus != null) {
+            if (promotionApplyStatus == 100) {
+                promotionApplyStatusList.add(0);
+                promotionApplyStatusList.add(1);
+                promotionApplyStatusList.add(2);
+                promotionApplyStatusList.add(3);
+                promotionApplyStatusList.add(4);
+            } else if (promotionApplyStatus == 2) {
+                promotionApplyStatusList.add(2);
+                promotionApplyStatusList.add(4);
+            } else {
+                promotionApplyStatusList.add(promotionApplyStatus);
+            }
+            if (user.getPromotionApplyExamineby()) {
+                user.setWorkUserId(null);
+            }
+
+            userId = null;
+        }
+
+        IPage page = orderInfoService.lambdaQuery()
+                .eq(StringUtils.isNotEmpty(orderStatus), OrderInfo::getOrderStatus, orderStatus)
+                .eq(StringUtils.isNotEmpty(userId), OrderInfo::getUserId, userId)
+                .in(CollectionUtil.isNotEmpty(promotionApplyStatusList), OrderInfo::getPromotionApplyStatus, promotionApplyStatusList)
+                .eq(CollectionUtil.isNotEmpty(promotionApplyStatusList) && user.getWorkUserId() != null, OrderInfo::getWorkerId, user.getWorkUserId())
+                .and(promotionApplyStatus != null && promotionApplyStatus == 100, v ->
+                        v.eq(OrderInfo::getOrderStatus, OrderStatusEnum.NOPAY.toString())
+                                .or().ne(OrderInfo::getOrderStatus, OrderStatusEnum.NOPAY.toString()).ne(OrderInfo::getPromotionApplyStatus, 0)
+                )
+                .orderByDesc(OrderInfo::getCreateTime)
+                .page(new Page<>(pageNum, pageSize));
+
+        List<OrderDetailBean> list = new ArrayList<>();
+        for (Object o : page.getRecords()) {
+            OrderInfo orderInfo = (OrderInfo) o;
+            OrderDetailBean orderDetailBean = new OrderDetailBean();
+            BeanUtils.copyProperties(orderInfo, orderDetailBean);
+            List<OrderShare> list1 = orderShareService
+                    .lambdaQuery()
+                    .eq(OrderShare::getOrderId, orderInfo.getOrderId())
+                    .list();
+            if (list1 != null && list1.size() > 0) {
+                //此处list1目前最多2条
+                for (OrderShare orderShare : list1) {
+                    orderDetailBean.setOrderShareStatus(orderShare.getStatus());
+                }
+            }
+            //查询订单详情
+            List<OrderDetail> orderDetails = orderDetailService.lambdaQuery().eq(OrderDetail::getOrderId, orderInfo.getOrderId()).list();
+
+            List<OrderDetailGoodsBean> orderDetailGoodsBeans = BeanUtil.copyToList(orderDetails, OrderDetailGoodsBean.class);
+
+            this.getGoodsByGoodsId(orderDetailGoodsBeans);
+
+            final OrderDetail detail = orderDetails.get(0);
+
+            orderDetailBean.setOrderDetailList(orderDetailGoodsBeans);
+            list.add(orderDetailBean);
+        }
+        page.setRecords(list);
+        return page;
+    }
+
+    private void getGoodsByGoodsId(List<OrderDetailGoodsBean> orderDetailGoodsBeans) {
+        final List<String> goodsIds = orderDetailGoodsBeans.stream().map(OrderDetailGoodsBean::getGoodsId).collect(Collectors.toList());
+        final List<Goods> goodsList = goodsService.lambdaQuery()
+                .in(Goods::getGoodsId, goodsIds)
+                .list();
+        final Map<String, Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(Goods::getGoodsId, Function.identity(), (key1, key2) -> key2));
+
+        for (OrderDetailGoodsBean detailGoodsBean : orderDetailGoodsBeans) {
+            final Goods goods = goodsMap.get(detailGoodsBean.getGoodsId());
+            detailGoodsBean.setGoods(goods);
+        }
+    }
+
+    /**
+     * 根据件数以及地区计算运费
+     */
+    private BigDecimal getFreightAmountItem(String province, String city, String area, BuyGood buyGood, AdminCompanyWechat adminCompanyWechat) throws RemoteServiceException {
+        return freightLogic.computeAmountItem(province, city, area, buyGood, adminCompanyWechat);
+    }
+
+    /**
+     * 维护兑换码抵扣金额对订单详情商品的影响
+     */
+    private void supplyOrderDetail(List<OrderDetail> orderDetails, BigDecimal exchangeAmount, BigDecimal notPromotionGroupAmount) {
+
+        List<OrderDetail> ods = orderDetails.stream().filter(v -> StringUtils.isEmpty(v.getPromotionGroupId())).collect(Collectors.toList());
+        if (ods.size() == 0) {
+            return;
+        }
+        if (exchangeAmount == null || exchangeAmount.doubleValue() == 0) {
+            return;
+        }
+        BigDecimal totalSubAmount = BigDecimal.valueOf(0);
+        for (int i = 0; i < ods.size(); i++) {
+            OrderDetail orderDetail = ods.get(i);
+            if (i == ods.size() - 1) {
+                //最后一个,直接用总数减掉
+                BigDecimal subtract = exchangeAmount.subtract(totalSubAmount);
+                orderDetail.setShareAmount(orderDetail.getShareAmount().subtract(subtract));
+                orderDetail.setTotalExchangeAmount(subtract);
+
+                double div = ArithUtils.div(subtract.doubleValue(), orderDetail.getNum(), 2);
+                orderDetail.setExchangeAmount(BigDecimal.valueOf(div));
+                break;
+            }
+            //该商品的总抵扣金额
+            double div = ArithUtils.div(orderDetail.getShareAmount().doubleValue(), notPromotionGroupAmount.doubleValue(), 4);
+            double mul = ArithUtils.mul(div, exchangeAmount.doubleValue());
+            BigDecimal subAmount = BigDecimal.valueOf(mul);
+            //该商品数量1情况下的抵扣金额
+            double div1 = ArithUtils.div(subAmount.doubleValue(), orderDetail.getNum(), 2);
+
+            orderDetail.setShareAmount(orderDetail.getShareAmount().subtract(subAmount));
+            orderDetail.setTotalExchangeAmount(subAmount);
+            orderDetail.setExchangeAmount(BigDecimal.valueOf(div1));
+            //记录已经分配了多少抵扣金额
+            totalSubAmount = totalSubAmount.add(subAmount);
+        }
+    }
+
+    /**
+     * 确认收货
+     */
+    //@Transactional
+    public void ack(String orderId) throws Exception {
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_ORDER + ":" + orderId);
+        if (!obtain.tryLock(5, TimeUnit.SECONDS)) {
+            throw new RemoteServiceException("系统繁忙,请勿重复操作");
+        }
+        try {
+            OrderInfo orderInfo = orderInfoService.getById(orderId);
+            if (!orderInfo.getOrderStatus().equals(OrderStatusEnum.YFH.toString())) {
+                throw new RemoteServiceException("非已发货订单不可确认收货");
+            }
+            orderInfo.setOrderStatus(OrderStatusEnum.OVER.toString());
+            orderInfo.setOverTime(new Date());
+            orderInfo.updateById();
+
+            orderShareService.lambdaUpdate()
+                    .set(OrderShare::getOrderStatus, orderInfo.getOrderStatus())
+                    .eq(OrderShare::getOrderId, orderId)
+                    .set(OrderShare::getOrderFinishTime, new Date())
+                    .update();
+        } finally {
+            obtain.unlock();
+        }
+    }
+
+    /**
+     * 撤销申请
+     */
+    @Transactional
+    public String cancelRefund(String refundOrderId) throws RemoteServiceException {
+        OrderRefund orderRefund = orderRefundService.getById(refundOrderId);
+        if (orderRefund == null || !orderRefund.getOrderStatus().equals(OrderStatusEnum.DSJCL.toString())) {
+            throw new RemoteServiceException("已非待商家处理状态不可撤销申请");
+        }
+        //还原订单为完成状态
+        String orderId = orderRefund.getOrderId();
+        orderRefund.deleteById();
+        return orderId;
+    }
+
+    /**
+     * 申请售后
+     */
+    @Transactional
+    public String applyAfterSale(RefundGoodsBean refundGoodsBean) throws Exception {
+        String orderId = refundGoodsBean.getOrderId();
+        String refundReason = refundGoodsBean.getRefundReason();
+        String refundType = refundGoodsBean.getRefundType();
+        String refundExplain = refundGoodsBean.getRefundExplain();
+        List<String> imgIds = refundGoodsBean.getImgIds();
+        List<RefundGoods> refundGoods = refundGoodsBean.getRefundGoods();
+
+        OrderInfo orderInfo = orderInfoService.getById(orderId);
+
+        Long count = orderShareService.lambdaQuery()
+                .eq(OrderShare::getOrderId, orderId)
+                .and(o -> o.eq(OrderShare::getStatus, OrderShareStatusEnum.OVER.toString())
+                        .or()
+                        .eq(OrderShare::getStatus, OrderShareStatusEnum.CANCEL.toString()))
+                .count();
+
+        if (count > 0) {
+            throw new RemoteServiceException("已结算订单,不能申请售后");
+        }
+
+        if (OrderStatusEnum.NOPAY.toString().equals(orderInfo.getOrderStatus())) {
+            throw new RemoteServiceException("未付款,不需要售后");
+        }
+//        if (OrderStatusEnum.OVER.toString().equals(orderInfo.getOrderStatus())) {
+//            throw new RemoteServiceException("订单已完成,不能售后");
+//        }
+        Long refundCount = orderRefundService.lambdaQuery()
+                .eq(OrderRefund::getOrderId, orderId)
+                .in(OrderRefund::getOrderStatus, Arrays.asList(OrderStatusEnum.DSJCL.toString(),
+                        OrderStatusEnum.DMJCL.toString(), OrderStatusEnum.DSJSH.toString(), OrderStatusEnum.REFUND.toString(), OrderStatusEnum.OVER.toString()))
+                .ne(OrderRefund::getExamineStatus, OrderExamineEnum.FAIL.toString())
+                .count();
+        if (refundCount > 0) {
+            throw new RemoteServiceException("请勿重新申请售后");
+        }
+
+        BigDecimal totalRefundAmount = new BigDecimal("0.00");
+        BigDecimal totalRefundShareAmount = new BigDecimal("0.00");
+        for (RefundGoods bean : refundGoods) {
+            OrderDetail orderDetail = orderDetailService.getById(bean.getOrderDetailId());
+            if (orderInfo.getPromotionPackageGoods() && orderDetail.getNum().intValue() != bean.getNum().intValue()) {
+                throw new RemoteServiceException("套购商品只支持全量退");
+            }
+            if (bean.getNum() > orderDetail.getNum()) {
+                throw new RemoteServiceException("申请售后的商品数量错误");
+            }
+            //商品需要退款的金额
+            BigDecimal refundAmount = BigDecimal.valueOf(ArithUtils.mul(ArithUtils.div(orderDetail.getPayAmount().doubleValue(), orderDetail.getNum(), 4), bean.getNum(), 2));
+            //计算退款的数量占比
+            double div = ArithUtils.div(bean.getNum(), orderDetail.getNum(), 4);
+            //按比例退运费(这里涉及到运费模板规则问题,暂不明确退款的规则运费怎么处理,目前以不退运费为准)
+//            if (orderInfo.getOrderStatus().equals(OrderStatusEnum.DFH.toString()) ||
+//                    (orderInfo.getOrderStatus().equals(OrderStatusEnum.REFUND.toString()) &&
+//                            org.apache.commons.lang3.StringUtils.isEmpty(orderInfo.getLogisticsNo()))) {//待发货状态退运费
+//                BigDecimal refundFreight = BigDecimal.valueOf(ArithUtils.mul(orderDetail.getFreight().doubleValue(), div, 2));
+//                if (refundFreight == null) {
+//                    refundFreight = new BigDecimal("0.00");
+//                }
+//                refundAmount = refundAmount.add(refundFreight);
+//            }
+            orderDetail.setRefundNum(bean.getNum());
+            orderDetail.setRefundAmount(refundAmount);
+            orderDetail.setRefundTime(new Date());
+            orderDetail.setRefund(true);
+            orderDetail.updateById();
+            //总退款金额
+            totalRefundAmount = totalRefundAmount.add(refundAmount);
+            totalRefundShareAmount = totalRefundShareAmount.add(orderDetail.getShareAmount());
+        }
+        //扣除运费后的金额
+        //totalRefundAmount=totalRefundAmount.subtract(orderInfo.getFreight());
+        //绑定附件
+        commonLogic.bindFile(orderId, Constant.Img.ORDER_REFUND_TKPZ, imgIds);
+        //新增售后申请信息
+        OrderRefund orderRefund = orderRefundService.lambdaQuery()
+                .eq(OrderRefund::getOrderId, orderId)
+                .one();
+        if (orderRefund == null) {
+            orderRefund = new OrderRefund();
+            orderRefund.setCreateTime(new Date());
+            BeanUtils.copyProperties(orderInfo, orderRefund);
+        }
+        orderRefund.setOrderStatus(OrderStatusEnum.DSJCL.toString());
+        orderRefund.setRefundType(refundType);
+        orderRefund.setRefundReason(refundReason);
+        orderRefund.setRefundExplain(refundExplain);
+        orderRefund.setRefundAmount(totalRefundAmount);
+        orderRefund.setRefundShareAmount(totalRefundShareAmount);
+        orderRefund.setWebsitName(orderInfo.getWebsitName());
+        orderRefund.setWebsitNumber(orderInfo.getWebsitNumber());
+        orderRefund.setCreateTime(new Date());
+        orderRefundService.saveOrUpdate(orderRefund);
+        orderInfo.setLastOrderStatus(orderInfo.getOrderStatus());
+        orderInfo.setOrderStatus(OrderStatusEnum.REFUND.toString());
+        orderInfo.setRefundFlag(RefundFlagEnum.ING.toString());
+        orderInfoService.updateById(orderInfo);
+
+        return orderRefund.getOrderRefundId();
+    }
+
+    /**
+     * 售后订单列表
+     */
+    public IPage<OrderRefundDetailBean> refundList(String userId, String orderStatus, Integer pageNo, Integer pageSize) {
+        IPage page = orderRefundService.lambdaQuery()
+                .eq(OrderRefund::getUserId, userId)
+                .eq(StringUtils.isNotEmpty(orderStatus), OrderRefund::getOrderStatus, orderStatus)
+                .orderByDesc(OrderRefund::getCreateTime)
+                .page(new Page<>(pageNo, pageSize));
+
+        List<OrderRefundDetailBean> list = new ArrayList<>();
+        for (Object o : page.getRecords()) {
+            OrderRefund orderRefund = (OrderRefund) o;
+            OrderRefundDetailBean orderRefundDetailBean = new OrderRefundDetailBean();
+            BeanUtils.copyProperties(orderRefund, orderRefundDetailBean);
+            orderRefundDetailBean.setOrderDetails(orderDetailService.lambdaQuery()
+                    .eq(OrderDetail::getOrderId, orderRefund.getOrderId()).list());
+            list.add(orderRefundDetailBean);
+        }
+        page.setRecords(list);
+        return page;
+    }
+
+    /**
+     * 售后订单详情
+     *
+     * @param orderRefundId
+     * @return
+     */
+    public OrderRefundDetailBean orderRefundDetail(String orderRefundId) {
+        OrderRefund orderRefund = orderRefundService.getById(orderRefundId);
+        List<OrderDetail> list = orderDetailService.lambdaQuery().eq(OrderDetail::getOrderId, orderRefund.getOrderId()).list();
+
+        OrderRefundDetailBean orderRefundDetailBean = new OrderRefundDetailBean();
+        BeanUtils.copyProperties(orderRefund, orderRefundDetailBean);
+        orderRefundDetailBean.setOrderDetails(list);
+        //售后凭证
+        List<CommonFile> commonFiles = commonLogic.queryFileByObjId(orderRefundId, Constant.Img.ORDER_REFUND_WLPZ);
+        orderRefundDetailBean.setFiles(commonFiles);
+
+        return orderRefundDetailBean;
+    }
+
+    /**
+     * 售后订单-补充退货资料
+     */
+    @Transactional
+    public void supplyData(String orderRefundId, String logisticsNo, String expressCompany, List<String> imgIds) throws RemoteServiceException {
+        OrderRefund orderRefund = orderRefundService.getById(orderRefundId);
+        if (!orderRefund.getOrderStatus().equals(OrderStatusEnum.DMJCL.toString())) {
+            throw new RemoteServiceException("请耐心等待商家审核");
+        }
+        orderRefund.setLogisticsNo(logisticsNo);
+        orderRefund.setExpressCompanyCode(expressCompany);
+        orderRefund.setOrderStatus(OrderStatusEnum.DSJSH.toString());
+        orderRefund.updateById();
+
+        //凭证
+        commonLogic.bindFile(orderRefundId, Constant.Img.ORDER_REFUND_WLPZ, imgIds);
+    }
+
+    /**
+     * 提醒发货
+     */
+    public void notice(String orderId) {
+        OrderInfo orderInfo = orderInfoService.getById(orderId);
+        Notice notice = new Notice();
+        notice.setObjId(orderId);
+        notice.setCreateTime(new Date());
+        notice.setContent("客户催促您发货了,订单号:" + orderId);
+        notice.setType(1);
+        notice.setTitle("订单发货");
+        notice.setWebsitName(orderInfo.getWebsitName());
+        notice.setWebsitNumber(orderInfo.getWebsitNumber());
+        notice.insert();
+    }
 }

+ 310 - 0
src/main/java/com/gree/mall/contest/logic/order/OrderTaxLogic.java

@@ -0,0 +1,310 @@
+package com.gree.mall.contest.logic.order;
+
+import cn.hutool.core.lang.TypeReference;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.order.OrderDetailBean;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.OrderTax;
+import com.gree.mall.contest.plus.service.GoodsCategoryService;
+import com.gree.mall.contest.plus.service.GoodsService;
+import com.gree.mall.contest.plus.service.GoodsSpecService;
+import com.gree.mall.contest.plus.service.OrderTaxService;
+import com.gree.mall.contest.utils.RedisUtil;
+import com.gree.mall.contest.utils.email.EmailUtilsNew;
+import com.gree.mall.contest.utils.excel.ExcelUtils;
+import com.gree.mall.contest.utils.http.HttpUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.xssf.usermodel.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class OrderTaxLogic {
+    private final OrderTaxService orderTaxService;
+    private final GoodsService goodsService;
+    private final GoodsSpecService goodsSpecService;
+    private final GoodsCategoryService goodsCategoryService;
+    private final RedisUtil redisUtil;
+    private final CommonLogic commonLogic;
+    OrderLogic orderLogic;
+    @Value("${tax.token.appid}")
+    private String clientId;
+    @Value("${tax.token.appSecret}")
+    private String clientSecret;
+    @Value("${tax.token.url}")
+    private String taxTokenUrl;
+    @Value("${tax.invoice.title.url}")
+    private String taxTitleUrl;
+
+
+    private final static String EMAIL_SUBJECT = "发票";
+    private final static String EMAIL_PREFIX = "请点击链接下载您的发票:\n\t";
+
+    @Transactional
+    public OrderTax saveTax(OrderTax orderTaxBean) {
+        OrderDetailBean orderDetail = orderLogic.detail(orderTaxBean.getOrderId());
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
+        if (Integer.parseInt(sdf.format(orderDetail.getPayTime())) < 2022) {
+            throw new RemoteServiceException("2022年之前的订单请联系客服开票");
+        }
+        String orderId = orderTaxBean.getOrderId();
+        OrderDetailBean detail = orderLogic.detail(orderId);
+        orderTaxBean.setCompanyWechatId(detail.getCompanyWechatId());
+        orderTaxBean.setCompanyWechatName(detail.getCompanyWechatName());
+        orderTaxBean.setWebsitId(detail.getWebsitId());
+        orderTaxBean.setWebsitName(detail.getWebsitName());
+        orderTaxBean.setOrderTaxId(null);
+        orderTaxBean.setStatus(false);
+        orderTaxBean.setCreateTime(new Date());
+        if (orderTaxBean.getTaxNo() != null) {
+            orderTaxBean.setTaxNo(orderTaxBean.getTaxNo().replaceAll(" ", ""));
+        }
+        orderTaxService.save(orderTaxBean);
+        //标记订单已开发票
+        orderLogic.tax(orderTaxBean.getOrderId(), orderTaxBean.getOrderTaxId());
+        return orderTaxBean;
+    }
+
+    public void updateTax(OrderTax orderTaxBean) {
+        orderTaxService.lambdaUpdate().eq(OrderTax::getOrderTaxId, orderTaxBean.getOrderTaxId())
+                .set(StringUtils.isNotBlank(orderTaxBean.getAccount()), OrderTax::getAccount, orderTaxBean.getAccount())
+                .set(orderTaxBean.getType() != null, OrderTax::getType, orderTaxBean.getType())
+                .set(orderTaxBean.getTaxType() != null, OrderTax::getTaxType, orderTaxBean.getTaxType())
+                .set(StringUtils.isNotBlank(orderTaxBean.getName()), OrderTax::getName, orderTaxBean.getName())
+                .set(StringUtils.isNotBlank(orderTaxBean.getTaxNo()), OrderTax::getTaxNo, orderTaxBean.getTaxNo())
+                .set(StringUtils.isNotBlank(orderTaxBean.getRegisterAddress()), OrderTax::getRegisterAddress, orderTaxBean.getRegisterAddress())
+                .set(StringUtils.isNotBlank(orderTaxBean.getRegisterPhone()), OrderTax::getRegisterPhone, orderTaxBean.getRegisterPhone())
+                .set(StringUtils.isNotBlank(orderTaxBean.getBank()), OrderTax::getBank, orderTaxBean.getBank())
+                .set(StringUtils.isNotBlank(orderTaxBean.getAccount()), OrderTax::getAccount, orderTaxBean.getAccount())
+                .set(StringUtils.isNotBlank(orderTaxBean.getReceiverPhone()), OrderTax::getReceiverPhone, orderTaxBean.getReceiverPhone())
+                .set(StringUtils.isNotBlank(orderTaxBean.getReceiverEmail()), OrderTax::getReceiverEmail, orderTaxBean.getReceiverEmail())
+                .set(StringUtils.isNotBlank(orderTaxBean.getContent()), OrderTax::getContent, orderTaxBean.getContent())
+                .update();
+    }
+
+    public String delTax(String orderTaxId) {
+        orderTaxService.lambdaUpdate()
+                .eq(OrderTax::getOrderTaxId, orderTaxId)
+                .remove();
+        return orderTaxId;
+    }
+
+    public String sendTax(String orderTaxId) {
+        OrderTax orderTax = orderTaxService.lambdaQuery()
+                .eq(OrderTax::getOrderTaxId, orderTaxId)
+                .one();
+
+        String taxLink = orderTax == null ? null : orderTax.getTaxLink();
+        if (StringUtils.isNotBlank(taxLink)) {
+            new EmailUtilsNew().send(orderTax.getReceiverEmail(), EMAIL_SUBJECT, EMAIL_PREFIX + taxLink);
+        }
+        return taxLink;
+    }
+
+
+    public IPage<OrderTax> page(String userId, Integer pageNum, Integer pageSize) {
+        return orderTaxService.lambdaQuery()
+                .eq(StringUtils.isNotBlank(userId), OrderTax::getUserId, userId)
+                .orderByDesc(OrderTax::getCreateTime).page(new Page(pageNum, pageSize));
+
+    }
+
+    public OrderTax detail(String orderTaxId) {
+        return orderTaxService.lambdaQuery()
+                .eq(StringUtils.isNotBlank(orderTaxId), OrderTax::getOrderTaxId, orderTaxId)
+                .one();
+
+    }
+
+    public Object title(String companyName) throws RemoteServiceException {
+        HashMap<String, String> params = new HashMap();
+        HashMap<String, String> heads = new HashMap();
+        heads.put("authorization_token", getTaxToken());
+        params.put("companyName", companyName);
+        String content = HttpUtils.requestGet(taxTitleUrl, params, heads);
+        if (content == null) {
+            throw new RemoteServiceException("网络错误");
+        }
+        Map<String, Object> resMap = JSONUtil.toBean(content, new TypeReference<HashMap<String, Object>>() {
+        }, false);
+
+        int code = (int) resMap.get("code");
+        if (code == 1) {
+            return resMap.get("data");
+        }
+        return null;
+    }
+
+    public String getTaxToken() throws RemoteServiceException {
+        Object token = redisUtil.get(Constant.RedisPrefix.TOKEN_TAX);
+
+        if (token != null) {
+            return token.toString();
+        }
+        //过期就重新获取
+        return this.deleteAndAddWxToken();
+    }
+
+    public String deleteAndAddWxToken() throws RemoteServiceException {
+        HashMap<String, String> params = new HashMap();
+        params.put("clientId", this.clientId);
+        params.put("clientSecret", this.clientSecret);
+        String content = HttpUtils.requestGet(taxTokenUrl, params, null);
+        Map<String, Object> resMap = JSONUtil.toBean(content, new TypeReference<HashMap<String, Object>>() {
+        }, false);
+
+        int code = (int) resMap.get("code");
+        if (code == 1) {
+            String token = (String) resMap.get("data");
+            redisUtil.set(Constant.RedisPrefix.TOKEN_TAX, token, 2 * 60 * 60);//2小时过期
+            return token;
+        }
+        return null;
+    }
+
+
+    public void yonge(MultipartFile file) throws RemoteServiceException, IOException {
+
+        List<Object> datas = ExcelUtils.importExcel(file);
+        List<String> tableHeaders = (List<String>) datas.get(0);
+
+        Map<String, List<List<Object>>> map = new HashMap<>();
+        for (int i = 1; i < datas.size(); i++) {
+            List<Object> row = (List<Object>) datas.get(i);
+            //邮箱
+            String email = (String) row.get(0);
+
+            List<List<Object>> objects = map.get(email);
+            if (objects == null) {
+                objects = new ArrayList<>();
+            }
+            row.remove(0);
+            objects.add(row);
+
+            if (!map.containsKey(email)) {
+                map.put(email, objects);
+            }
+        }
+        for (String email : map.keySet()) {
+            //String filePath = "B:/"+email+"-"+file.getName()+".xlsx";
+            String filePath = "/app/tmp/" + email + "-" + file.getName() + ".xlsx";
+            this.createExcel(tableHeaders, map.get(email), filePath);
+            new EmailUtilsNew().send(email, file.getName(), "请查收", filePath);
+        }
+
+    }
+
+
+    /**
+     * 创建Excel
+     */
+    public void createExcel(List<String> tableHeaders, List<List<Object>> rows, String fileFullPath) {
+        OutputStream out = null;
+        try {
+            out = new FileOutputStream(new File(fileFullPath));
+            write(out, tableHeaders, rows);
+
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 生成Excel文件的方法write
+     *
+     * @param outputStream
+     */
+    public void write(OutputStream outputStream, List<String> tableHeaders, List<List<Object>> rows) {
+        // 初始一个workbook
+        XSSFWorkbook workbook = new XSSFWorkbook();
+        // 创建单个sheet
+        XSSFSheet sheet = workbook.createSheet("sheet1");
+        // 创建第一行,设置列名
+        XSSFRow row0 = sheet.createRow(0);
+        row0.setHeight((short) 400);             // 设置行高
+        XSSFCell cell = null;                    // Excel的列
+        XSSFFont font = workbook.createFont();// 设置字体
+        font.setColor(HSSFFont.COLOR_NORMAL);           // 设置单元格字体的颜色
+        XSSFCellStyle style = workbook.createCellStyle();// 样式
+        // 设置边框颜色
+        style.setTopBorderColor(IndexedColors.BLACK.getIndex());
+        style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
+        style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
+        style.setRightBorderColor(IndexedColors.BLACK.getIndex());
+        // 设置文本格式
+        XSSFDataFormat format = workbook.createDataFormat();
+        style.setDataFormat(format.getFormat("@"));
+
+        short cellNumber = (short) tableHeaders.size();  // 表的列数
+        for (int k = 0; k < cellNumber; k++) {
+            if (k == 0) {
+                continue;
+            }
+            int i = 0;
+            i += k - 1;
+            short b = 5000;
+            cell = row0.createCell(i);          // 创建第0行第k列
+            cell.setCellValue(tableHeaders.get(k));  // 设置第0行第k列的值
+            sheet.setColumnWidth(i, b);         // 设置列的宽度
+            style.setFont(font);                // 设置字体风格
+            cell.setCellStyle(style);
+        }
+
+
+        XSSFCellStyle styleContext = workbook.createCellStyle();
+        styleContext.setWrapText(true);
+        // 设置边框颜色
+        styleContext.setTopBorderColor(IndexedColors.BLACK.getIndex());
+        styleContext.setBottomBorderColor(IndexedColors.BLACK.getIndex());
+        styleContext.setLeftBorderColor(IndexedColors.BLACK.getIndex());
+        styleContext.setRightBorderColor(IndexedColors.BLACK.getIndex());
+        // 设置文本格式
+        styleContext.setDataFormat(format.getFormat("@"));
+
+        if (rows.size() > 0) {
+            // 创建剩余行
+            for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
+                XSSFRow row = sheet.createRow(rowIndex + 1);
+                List<Object> values = rows.get(rowIndex);
+                // 创建多列
+                for (int cellIndex = 0; cellIndex < values.size(); cellIndex++) {
+                    cell = row.createCell(cellIndex);
+                    cell.setCellStyle(styleContext);               // 设置风格
+                    cell.setCellValue(values.get(cellIndex).toString());
+                }
+            }
+        }
+        try {
+            workbook.write(outputStream);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+}

+ 288 - 0
src/main/java/com/gree/mall/contest/logic/pay/PayLogic.java

@@ -0,0 +1,288 @@
+package com.gree.mall.contest.logic.pay;
+
+import com.aliyuncs.utils.StringUtils;
+import com.gree.mall.contest.commonmapper.SoldNumMapper;
+import com.gree.mall.contest.enums.OrderShareStatusEnum;
+import com.gree.mall.contest.enums.OrderStatusEnum;
+import com.gree.mall.contest.logic.activity.PromotionLuckDrawLogic;
+import com.gree.mall.contest.logic.common.WechatLogic;
+import com.gree.mall.contest.plus.entity.*;
+import com.gree.mall.contest.plus.service.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PayLogic {
+
+    private final WechatLogic wechatLogic;
+    private final PromotionLuckDrawLogic promotionLuckDrawLogic;
+    private final UserService userService;
+    private final OrderInfoService orderInfoService;
+    private final OrderDetailService orderDetailService;
+    private final OrderShareService orderShareService;
+    private final GoodsService goodsService;
+    private final SoldNumMapper soldNumMapper;
+    private final PromotionGroupSpecService promotionGroupSpecService;
+
+
+    public void payCall(String id, String transactionId, BigDecimal totalFee) throws Exception {
+
+        OrderInfo orderInfo = orderInfoService.getById(id);
+        if (!orderInfo.getOrderStatus().equals(OrderStatusEnum.NOPAY.toString())) {
+            log.info("请勿重复支付,orderId:{}", id);
+            return;
+        }
+        orderInfo.setOrderStatus(OrderStatusEnum.DFH.toString());
+        orderInfo.setPayType("微信支付");
+        orderInfo.setPayTime(new Date());
+        orderInfo.setTransactionId(transactionId);
+        orderInfo.setDiscountAmount(orderInfo.getTotalAmount().add(orderInfo.getFreight()).subtract(orderInfo.getPayAmount()));
+
+        //计算各订单详情的优惠金额以及实际支付金额
+        BigDecimal lastDiscountAmount = new BigDecimal(0);
+        //实际支付金额
+        BigDecimal payAmount = orderInfo.getPayAmount();
+
+        //判断金额是否支付正确
+        if (totalFee.doubleValue() != payAmount.doubleValue()) {
+            log.error("【支付回调严重bug】......orderId:{},回调金额:{},订单实际支付金额:{}", id, totalFee, payAmount);
+            return;
+        }
+
+        payAmount = payAmount.subtract(orderInfo.getFreight());
+        //商品总金额(不包含运费)
+        BigDecimal totalProductAmount = orderInfo.getTotalProductAmount();
+        //订单总商品数
+        Integer totalNum = orderInfo.getTotalNum();
+        List<OrderDetail> orderDetails = orderDetailService.lambdaQuery()
+                .eq(OrderDetail::getOrderId, orderInfo.getOrderId())
+                .orderByAsc(OrderDetail::getPrice)
+                .list();
+
+        for (OrderDetail orderDetail : orderDetails) {
+            Goods goods = goodsService.getById(orderDetail.getGoodsId());
+            Goods goodsUp = new Goods();
+            goodsUp.setGoodsId(goods.getGoodsId());
+            goodsUp.setSoldNum(goods.getSoldNum() + orderDetail.getNum());
+            goodsUp.updateById();
+
+        }
+
+        //总的折扣优惠金额
+        BigDecimal totalZkAmount = orderInfo.getPromotionDiscountAmount();
+        //总的申请优惠金额
+        BigDecimal totalApplyAmount = orderInfo.getPromotionApplyAmount();
+
+        //订单优惠总金额(不包含商品券的优惠金额)
+        BigDecimal orderDiscountAmount = totalProductAmount.subtract(payAmount);
+
+        if (totalProductAmount.doubleValue() > 0) {
+            BigDecimal orderDiscountPercent = orderDiscountAmount.divide(totalProductAmount, 4, BigDecimal.ROUND_DOWN);
+            //公共的处理(可能涉及到修改了支付金额的)
+            for (int i = 0; i < orderDetails.size(); i++) {
+                OrderDetail orderDetail = orderDetails.get(i);
+                updateSaleNum(orderDetail);
+                //单价
+                BigDecimal price = orderDetail.getNeedPayAmount() != null &&
+                        orderDetail.getNeedPayAmount().compareTo(new BigDecimal("0.00")) > 0 ?
+                        orderDetail.getNeedPayAmount() : orderDetail.getPrice();
+                //当前商品所有优惠的优惠金额
+                BigDecimal discount = orderDiscountPercent.multiply(price.multiply(BigDecimal.valueOf(orderDetail.getNum())))
+                        .setScale(2, BigDecimal.ROUND_DOWN);
+                //当前商品的实际支付金额比例
+                BigDecimal percent = price.divide(orderDetail.getTotalPrice(), 4, BigDecimal.ROUND_DOWN);
+
+                //当前商品折扣金额的优惠总金额
+                BigDecimal singleZkAmount = totalZkAmount.multiply(percent);
+                BigDecimal singleApplyAmount = totalApplyAmount.multiply(percent);
+
+                if (i == orderDetails.size() - 1) {
+                    //最后一个,直接拿总的减
+                    BigDecimal subtract = orderDiscountAmount.subtract(lastDiscountAmount);
+                    orderDetail.setDiscountAmount(orderDetail.getDiscountAmount().add(subtract));
+                    orderDetail.setPayAmount(orderDetail.getPayAmount().subtract(orderDetail.getDiscountAmount()));
+
+                    orderDetail.setZkAmount(totalZkAmount.subtract(singleZkAmount.multiply(BigDecimal.valueOf(i))));
+                    orderDetail.setApplyAmount(totalApplyAmount.subtract(singleApplyAmount.multiply(BigDecimal.valueOf(i))));
+                    continue;
+                }
+
+                //产品总金额
+                BigDecimal amount = orderDetail.getPayAmount();
+                //优惠后的实际支付金额
+                BigDecimal truePrice = amount.subtract(discount);
+                orderDetail.setPayAmount(truePrice);
+                orderDetail.setDiscountAmount(orderDetail.getDiscountAmount().add(discount));
+                orderDetail.setZkAmount(singleZkAmount);
+                orderDetail.setApplyAmount(singleApplyAmount);
+                lastDiscountAmount = lastDiscountAmount.add(discount);
+            }
+            orderDetailService.updateBatchById(orderDetails);
+        }
+
+        List<OrderDetail> promotionOrderDetails = orderDetails.stream().filter(v -> StringUtils.isNotEmpty(v.getPromotionGroupId())).collect(Collectors.toList());
+        List<OrderDetail> commonlyOrderDetails = orderDetails.stream().filter(v -> StringUtils.isEmpty(v.getPromotionGroupId())).collect(Collectors.toList());
+        //团购商品分佣金
+        if (promotionOrderDetails.size() > 0)
+            this.addOrderShare(orderInfo, promotionOrderDetails, 2);
+        //普通商品分佣金
+        if (commonlyOrderDetails.size() > 0)
+            this.addOrderShare(orderInfo, commonlyOrderDetails, 1);
+
+        orderInfo.updateById();
+
+        //回写抽奖活动
+        promotionLuckDrawLogic.updatePromotionLuckDrawCode(orderInfo);
+
+        log.info("===============支付回调结束:{}==============", id);
+    }
+
+    /**
+     * 修改销量
+     */
+    private void updateSaleNum(OrderDetail orderDetail) {
+        try {
+            //团购活动销售数量
+            List<PromotionGroupSpec> list = promotionGroupSpecService.lambdaQuery().eq(PromotionGroupSpec::getGoodsSpecId, orderDetail.getGoodsSpecId()).list();
+            for (PromotionGroupSpec promotionGroupSpec : list) {
+                promotionGroupSpec.setSalesNum(promotionGroupSpec.getSalesNum() + orderDetail.getNum());
+                promotionGroupSpec.updateById();
+            }
+
+            soldNumMapper.updateGoodsSpec(orderDetail);
+            soldNumMapper.updateGoods(orderDetail);
+        } catch (Exception e) {
+
+        }
+    }
+
+
+    public String refundCall(String body) throws Exception {
+
+        log.info("【微信退款回调开始】{}", body);
+        Map<String, String> params = wechatLogic.doXMLParse(body);
+        if (params.get("return_code").equals("SUCCESS")) {
+            String reqInfo = params.get("req_info");
+            Map map = wechatLogic.refundAESStr(reqInfo);
+            String orderId = (String) map.get("out_trade_no");
+            List<OrderDetail> list = orderDetailService.lambdaQuery()
+                    .eq(OrderDetail::getOrderId, orderId)
+                    .eq(OrderDetail::getRefund, true).list();
+            BigDecimal totalShareAmount = new BigDecimal(0);
+            for (OrderDetail orderDetail : list) {
+                totalShareAmount = totalShareAmount.add(orderDetail.getShareAmount());
+            }
+            OrderShare orderShare = orderShareService.lambdaQuery().eq(OrderShare::getOrderId, orderId).one();
+            if (orderShare != null) {
+                orderShare.setAmount(orderShare.getAmount().subtract(totalShareAmount));
+                orderShare.updateById();
+            }
+        }
+        log.info("【微信退款回调结束】{}", body);
+        return body;
+    }
+
+
+    /**
+     * 新增结算记录
+     *
+     * @param orderInfo
+     * @param list
+     * @param flag      1=分销员带货  2=团购佣金
+     */
+    public void addOrderShare(OrderInfo orderInfo, List<OrderDetail> list, int flag) {
+        try {
+            BigDecimal shareAmount = list.stream().map(OrderDetail::getShareAmount).reduce(BigDecimal::add).orElse(BigDecimal.valueOf(0));
+            if (shareAmount.doubleValue() < 0) {
+                return;
+            }
+
+            String remark = "分销员带货";
+            if (flag == 2) {
+                remark = "团购佣金";
+            }
+
+            OrderShare orderShare = new OrderShare();
+            orderShare.setFlag(flag);
+            orderShare.setPayType(orderInfo.getPayType());
+            //分账
+            if (flag == 1) {
+                String workUserId = orderInfo.getWorkerId();
+                if (StringUtils.isEmpty(workUserId)) {
+                    return;
+                }
+                List<User> workUserList = userService.lambdaQuery()
+                        .eq(User::getWorkUserId, workUserId)
+                        .eq(User::getCompanyWechatId, orderInfo.getCompanyWechatId())
+                        .list();
+                if (workUserList.size() == 0) {
+                    log.info("【结算团购】未找到分销员,分销员id:{}", workUserId);
+                    return;
+                }
+                User workUser = workUserList.get(0);
+                orderShare.setWorkUserId(workUserId);
+                orderShare.setOpenId(workUser.getOpenId());
+                orderShare.setWorkUserName(orderInfo.getWorkerName());
+                orderShare.setWorkUserPhone(orderInfo.getWorkerPhone());
+                orderShare.setWebsitName(orderInfo.getWebsitName());
+                orderShare.setWebsitNumber(orderInfo.getWebsitNumber());
+            } else if (flag == 2) {
+                orderShare.setPromotionGroupId(orderInfo.getPromotionGroupId());
+                orderShare.setWorkUserId(orderInfo.getPromotionGroupUserId());
+                User promotionGroupUser = userService.getById(orderInfo.getPromotionGroupUserId());
+                if (promotionGroupUser == null) {
+                    log.info("【结算团购】未找到团长,团长id:{}", orderInfo.getPromotionGroupUserId());
+                    return;
+                }
+                orderShare.setOpenId(promotionGroupUser.getOpenId());
+                orderShare.setWorkUserName(promotionGroupUser.getNickName());
+                orderShare.setWorkUserPhone(promotionGroupUser.getMobile());
+            }
+
+            BigDecimal totalShareAmount = list.stream().map(OrderDetail::getShareAmount)
+                    .reduce(BigDecimal::add).orElse(new BigDecimal(0));
+            //新增待分账记录
+            orderShare.setOrderId(orderInfo.getOrderId());
+            orderShare.setStatus(OrderShareStatusEnum.ING.toString());
+            if (orderInfo.getPayType().equals("云闪付")) {
+                // 云闪付线下结算
+                orderShare.setStatus("OFFLINE");
+            }
+            orderShare.setAmount(totalShareAmount);
+            orderShare.setUserId(orderInfo.getUserId());
+            orderShare.setUserName(orderInfo.getUserName());
+            orderShare.setPhone(orderInfo.getPhone());
+            orderShare.setPayment(orderInfo.getPayAmount());
+            orderShare.setOrderStatus(orderInfo.getOrderStatus());
+            orderShare.setTransactionId(orderInfo.getTransactionId());
+            orderShare.setPosition(orderInfo.getPosition());
+            orderShare.setPayTime(orderInfo.getPayTime());
+            StringBuffer address = new StringBuffer();
+            address.append(orderInfo.getProvince())
+                    .append(orderInfo.getCity())
+                    .append(orderInfo.getArea())
+                    .append(orderInfo.getStreet())
+                    .append(orderInfo.getReceAddress()).append(orderInfo.getHouseNo());
+            orderShare.setAddress(address.toString());
+            orderShare.setRemark(remark);
+
+            orderShare.setCreateTime(new Date());
+            orderShare.setCompanyWechatId(orderInfo.getCompanyWechatId());
+            orderShare.setCompanyWechatName(orderInfo.getCompanyWechatName());
+            orderShare.insert();
+
+            log.info("新增结算记录成功{}", orderInfo.getOrderId());
+        } catch (Exception e) {
+            log.error("结算添加失败", e);
+        }
+    }
+}

+ 60 - 0
src/main/java/com/gree/mall/contest/utils/AESUtil.java

@@ -0,0 +1,60 @@
+package com.gree.mall.contest.utils;
+
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+public class AESUtil {
+
+    /**
+     * 密钥算法
+     */
+    private static final String ALGORITHM = "AES";
+    /**
+     * 加解密算法/工作模式/填充方式
+     */
+    private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding";
+    /**
+     * 生成key TODO
+     */
+//    private static SecretKeySpec key = new SecretKeySpec(MD5.MD5Encode(WeChatLogic.SECURE_KEY).toLowerCase().getBytes(), ALGORITHM);
+    private static SecretKeySpec key = null;
+
+    /**
+     * AES加密
+     *
+     * @param data
+     * @return
+     * @throws Exception
+     */
+    public static String encryptData(String data) throws Exception {
+        // 创建密码器
+        Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
+        // 初始化
+        cipher.init(Cipher.ENCRYPT_MODE, key);
+        return Base64Util.encode(cipher.doFinal(data.getBytes()));
+    }
+
+    /**
+     * AES解密
+     *
+     * @param base64Data
+     * @return
+     * @throws Exception
+     */
+    public static String decryptData(String base64Data) throws Exception {
+        Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
+        cipher.init(Cipher.DECRYPT_MODE, key);
+        return new String(cipher.doFinal(Base64Util.decode(base64Data)));
+    }
+
+    public static void main(String[] args) throws Exception {
+        //解密
+        String req_info = "Zwl9G9N5Ypw5/X/sgkQaQ7UjjsoT3so+sn6J6pAuGMlzSy9PwHXWCs1S6VP8XMjC6UYdaa3zQD8FfYYvawzuk6eygpjRJ3p3tldtL61RUwxL8l4RPMXzooUVA3gwwJnhM6AbbpEcAbcHZtdCVHV66zwpvmCjLo+XhRhejBM2ye9iUU630INsKytLpS0FLJqPeDNug1v++NiLPcmhNDgqWA0TvTSeu72357bJ9nZUw79unNR5RAF4axOtqWb57wwNwx5t43qCw5WFCqR0O/A2/8IS//WNrVqaUbPVwMKAYcow27DP6NIhO2snoF77FZjUkoHJYTeMPW+mevaHoIQwtU84CRMAr0kFS+OBsV53R1Hqj4rzoHjuXRMRq/KyocPkYYGCBqmDjI1hET3gsYEmCakcPmYMM6QZMNweo1f+tW5AXqGsiZFiTqHzhdaZEGmCAuIaqwiQPLOSVeJECFwaebMi5e1Ebg4AmRSVrq/hOKtddmuClrFnP2jYvdAAKT7PwTqmqYzXuN0EUbz9fKKwcm9YoZ5gsN3CaFw60uQn8FIQlAhBIfyp+bKkt9XiqI/dwHpYY0Hvd6c/Ko9A4vPELCkMN3A5cwOlyYF/3Z3n+i9ScLRKKtMPPs+2wVsI8dZeluqCL8oiZqSzPtr+wjFj6pXxN6V9yWQOqLUEUbBRyhcf9OrcLX5y9LpMYMe1FpLIwGc+ojgqAv4WjIYJDRHAXUAZS9iERBnQAVgTU58XsM4HCoEIyvOGMJrIdciDdy9Uf0cX1l0VDUXaaxMV9MK3E4pGMjhg3NsKkt8S5w33XhmK+z1bv5bzsOltgbktCR9R4owsJwnGhb+CwwOPP5mdyc+2pP3hYxkylxVyVWgoFhIS5xpK33MBb8c6MR1yJx7+NFPljL8ZfcVsdLRwNkjsZBqw9RfU8ErhwBnoZ+Li6WHwj8YdYbxQdrEzNkXL+1XmsfHCILz707TytUXMRgkgWQhdUOLZAY6EL/t/3aXS6l8LqBKjwT6c+Kqjg6aanbZ16iV587zAQzkgTKXoUyv6mYflTws0El+TnYhdwzuq5nI=";
+        String B = AESUtil.decryptData(req_info);
+        System.out.println(B);
+
+    }
+
+
+    }

+ 46 - 0
src/main/java/com/gree/mall/contest/utils/Base64Util.java

@@ -0,0 +1,46 @@
+package com.gree.mall.contest.utils;
+
+import java.util.Base64;
+
+public class Base64Util {
+
+    public static byte[] decode(String encodedText){
+        final Base64.Decoder decoder = Base64.getDecoder();
+        return decoder.decode(encodedText);
+    }
+
+    public static String encode(byte[] data){
+        final Base64.Encoder encoder = Base64.getEncoder();
+        return encoder.encodeToString(data);
+    }
+
+    /**
+     * @param args
+     */
+    public static void main(String[] args) {
+        try {
+            final Base64.Decoder decoder = Base64.getDecoder();
+            final Base64.Encoder encoder = Base64.getEncoder();
+            final String text = "字串文字";
+            final byte[] textByte = text.getBytes("UTF-8");
+            //编码
+            final String encodedText = encoder.encodeToString(textByte);
+            System.out.println(encodedText);
+            //解码
+            System.out.println(new String(decoder.decode(encodedText), "UTF-8"));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+//        final Base64.Decoder decoder = Base64.getDecoder();
+//        final Base64.Encoder encoder = Base64.getEncoder();
+//        final String text = "字串文字";
+//        final byte[] textByte = text.getBytes("UTF-8");
+//        //编码
+//        final String encodedText = encoder.encodeToString(textByte);
+//        System.out.println(encodedText);
+//        //解码
+//        System.out.println(new String(decoder.decode(encodedText), "UTF-8"));
+
+    }
+}

+ 92 - 0
src/main/java/com/gree/mall/contest/utils/MD5Utils.java

@@ -0,0 +1,92 @@
+package com.gree.mall.contest.utils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Created by chijr on 17/6/13.
+ */
+public class MD5Utils {
+
+    private static String byteArrayToHexString(byte b[]) {
+        StringBuilder resultSb = new StringBuilder();
+        for (int i = 0; i < b.length; i++)
+            resultSb.append(byteToHexString(b[i]));
+
+        return resultSb.toString();
+    }
+
+    //32位大写toUpperCase()
+    public static String md5(String string) {
+        String re_md5 = new String();
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            md.update(string.getBytes());
+            byte b[] = md.digest();
+
+            int i;
+
+            StringBuilder buf = new StringBuilder("");
+            for (byte value : b) {
+                i = value;
+                if (i < 0)
+                    i += 256;
+                if (i < 16)
+                    buf.append("0");
+                buf.append(Integer.toHexString(i));
+            }
+
+            re_md5 = buf.toString().toUpperCase();
+
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+        return re_md5;
+
+    }
+
+    private static String byteToHexString(byte b) {
+        int n = b;
+        if (n < 0)
+            n += 256;
+        int d1 = n / 16;
+        int d2 = n % 16;
+        return hexDigits[d1] + hexDigits[d2];
+    }
+
+    public static String MD5Encode(String origin, String charsetname) {
+        String resultString = null;
+        try {
+            resultString = new String(origin);
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            if (charsetname == null || "".equals(charsetname))
+                resultString = byteArrayToHexString(md.digest(resultString
+                        .getBytes()));
+            else
+                resultString = byteArrayToHexString(md.digest(resultString
+                        .getBytes(charsetname)));
+        } catch (Exception ignored) {
+        }
+        return resultString;
+    }
+
+
+    public static String convertMD5(String inStr) {
+        char[] a = inStr.toCharArray();
+        for (int i = 0; i < a.length; i++) {
+            a[i] = (char) (a[i] ^ 't');
+        }
+        String s = new String(a);
+        return s;
+    }
+
+
+
+    private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
+            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
+
+
+
+
+
+}

+ 33 - 0
src/main/java/com/gree/mall/contest/utils/ShaUtils.java

@@ -0,0 +1,33 @@
+package com.gree.mall.contest.utils;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
+public class ShaUtils {
+
+    public static String getSha1(String str){
+        if(str==null||str.length()==0){
+            return null;
+        }
+        char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9',
+                'a','b','c','d','e','f'};
+        try {
+            MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
+            mdTemp.update(str.getBytes(StandardCharsets.UTF_8));
+
+            byte[] md = mdTemp.digest();
+            int j = md.length;
+            char[] buf = new char[j*2];
+            int k = 0;
+            for (byte byte0 : md) {
+                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
+                buf[k++] = hexDigits[byte0 & 0xf];
+            }
+            return new String(buf);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+
+}

+ 213 - 0
src/main/java/com/gree/mall/contest/utils/Utils.java

@@ -0,0 +1,213 @@
+package com.gree.mall.contest.utils;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.security.*;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.*;
+
+/**
+ * @Author: duke
+ * @Date: 2019-08-07 09:29
+ */
+public class Utils {
+
+
+    /**
+     * 输出堆栈信息
+     * @param e
+     * @return
+     */
+    public static String excptionStackToString(Exception e) {
+
+        StringWriter sw = null;
+        PrintWriter pw = null;
+        try {
+            sw = new StringWriter();
+            pw = new PrintWriter(sw);
+            //将出错的栈信息输出到printWriter中
+            e.printStackTrace(pw);
+            pw.flush();
+            sw.flush();
+        } finally {
+            if (sw != null) {
+                try {
+                    sw.close();
+                } catch (IOException e1) {
+                    e1.printStackTrace();
+                }
+            }
+            if (pw != null) {
+                pw.close();
+            }
+        }
+
+        return pw.toString();
+
+    }
+
+    /**
+     * 返回http请求URL
+     * @param request
+     * @return
+     */
+    public static String getRequstUrl(HttpServletRequest request){
+        if(request.getQueryString() != null){
+            return request.getMethod() + " " + request.getRequestURL() + "?" + request.getQueryString();
+        }
+
+        return request.getMethod() + " " + request.getRequestURL().toString();
+    }
+
+    /**
+     * 解密:对加密后的十六进制字符串(hex)进行解密,并返回字符串
+     *
+     * @return 解密后的字符串
+     */
+    public static String decrypt(String data, String key, String iv, String encodingFormat){
+
+        //被加密的数据
+        byte[] dataByte = Base64.decodeBase64(data);
+        //加密秘钥
+        byte[] keyByte = Base64.decodeBase64(key);
+        //偏移量
+        byte[] ivByte = Base64.decodeBase64(iv);
+
+        try {
+
+            Security.addProvider(new BouncyCastleProvider());
+
+            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
+
+            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
+
+            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
+            parameters.init(new IvParameterSpec(ivByte));
+
+            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
+
+            System.out.println("dataByte:"+dataByte);
+
+            byte[] resultByte = cipher.doFinal(dataByte);
+
+            if (null != resultByte && resultByte.length > 0) {
+                String result = new String(resultByte, encodingFormat);
+                return result;
+            }
+            return null;
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        } catch (NoSuchPaddingException e) {
+            e.printStackTrace();
+        } catch (InvalidParameterSpecException e) {
+            e.printStackTrace();
+        } catch (InvalidKeyException e) {
+            e.printStackTrace();
+        } catch (InvalidAlgorithmParameterException e) {
+            e.printStackTrace();
+        } catch (IllegalBlockSizeException e) {
+            e.printStackTrace();
+        } catch (BadPaddingException e) {
+            e.printStackTrace();
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    /**
+     * 根据指定大小分割list
+     * 非均分,最后一组数量可能小于其它组数量
+     * @param list
+     * @param unitSize
+     * @return
+     */
+    public static <T> List<List<T>> splitList(List<T> list, int unitSize) {
+        if (list == null || list.isEmpty() || unitSize < 1)
+            return null;
+
+        int wholeSize = list.size();
+        int groupNum = wholeSize / unitSize;
+        List<List<T>> result = new ArrayList<List<T>>();
+
+        for (int i = 0; i < groupNum; i++) {
+            List<T> subList = list.subList(i * unitSize, (i + 1) * unitSize);
+            result.add(subList);
+        }
+
+        if(unitSize * groupNum < wholeSize){
+            List<T> restList = list.subList(unitSize * groupNum, wholeSize);
+            result.add(restList);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * 方法用途: 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序),并且生成url参数串<br>
+     * 实现步骤: <br>
+     *
+     * @param paraMap    要排序的Map对象
+     * @param urlEncode  是否需要URLENCODE
+     * @param keyToLower 是否需要将Key转换为全小写
+     *                   true:key转化成小写,false:不转化
+     * @return
+     */
+    public static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode, boolean keyToLower) {
+        String buff = "";
+        Map<String, String> tmpMap = paraMap;
+        try {
+            List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());
+            // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
+            Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
+                @Override
+                public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
+                    return (o1.getKey()).toString().compareTo(o2.getKey());
+                }
+            });
+            // 构造URL 键值对的格式
+            StringBuilder buf = new StringBuilder();
+            for (Map.Entry<String, String> item : infoIds) {
+                if (StringUtils.isNotBlank(item.getKey())) {
+                    String key = item.getKey();
+                    String val = item.getValue();
+                    if (urlEncode) {
+                        val = URLEncoder.encode(val, "utf-8");
+                    }
+                    if (keyToLower) {
+                        buf.append(key.toLowerCase() + "=" + val);
+                    } else {
+                        buf.append(key + "=" + val);
+                    }
+                    buf.append("&");
+                }
+
+            }
+            buff = buf.toString();
+            if (buff.isEmpty() == false) {
+                buff = buff.substring(0, buff.length() - 1);
+            }
+        } catch (Exception e) {
+            return null;
+        }
+        return buff;
+    }
+
+
+}

+ 201 - 0
src/main/java/com/gree/mall/contest/utils/WeChatUtils.java

@@ -0,0 +1,201 @@
+package com.gree.mall.contest.utils;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * Created by chijr on 17/6/13.
+ */
+public class WeChatUtils {
+
+    /**
+     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
+     *
+     * @return boolean
+     */
+    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
+        StringBuffer sb = new StringBuffer();
+        Set es = packageParams.entrySet();
+        Iterator it = es.iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String k = (String) entry.getKey();
+            String v = (String) entry.getValue();
+            if (!"sign".equals(k) && null != v && !"".equals(v)) {
+                sb.append(k + "=" + v + "&");
+            }
+        }
+
+        sb.append("key=" + API_KEY);
+
+        //算出摘要
+        String mysign = MD5Utils.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
+        String tenpaySign = ((String) packageParams.get("sign")).toLowerCase();
+
+        //System.out.println(tenpaySign + "    " + mysign);
+        return tenpaySign.equals(mysign);
+    }
+
+    /**
+     * @param
+     * @return
+     * @author
+     * @date 2016-4-22
+     * @Description:sign签名 编码格式
+     */
+    public static String createSign(SortedMap<Object, Object> packageParams, String API_KEY) {
+        StringBuffer sb = new StringBuffer();
+        Set es = packageParams.entrySet();
+        Iterator it = es.iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String k = (String) entry.getKey();
+            String v = (String) entry.getValue();
+            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
+                sb.append(k + "=" + v + "&");
+            }
+        }
+        sb.append("key=" + API_KEY);
+        String sign = MD5Utils.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
+        return sign;
+    }
+
+    /**
+     * @param parameters 请求参数
+     * @return
+     * @author
+     * @date 2016-4-22
+     * @Description:将请求参数转换为xml格式的string
+     */
+    public static String getRequestXml(SortedMap<Object, Object> parameters) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("<xml>");
+        Set es = parameters.entrySet();
+        Iterator it = es.iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String k = (String) entry.getKey();
+            String v = (String) entry.getValue();
+            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
+                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
+            } else {
+                sb.append("<" + k + ">" + v + "</" + k + ">");
+            }
+        }
+        sb.append("</xml>");
+        return sb.toString();
+    }
+
+    /**
+     * 取出一个指定长度大小的随机正整数.
+     *
+     * @param length int 设定所取出随机数的长度。length小于11
+     * @return int 返回生成的随机数。
+     */
+    public static int buildRandom(int length) {
+        int num = 1;
+        double random = Math.random();
+        if (random < 0.1) {
+            random = random + 0.1;
+        }
+        for (int i = 0; i < length; i++) {
+            num = num * 10;
+        }
+        return (int) ((random * num));
+    }
+
+    /**
+     * 获取当前时间 yyyyMMddHHmmss
+     *
+     * @return String
+     */
+    public static String getCurrTime() {
+        Date now = new Date();
+        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+        String s = outFormat.format(now);
+        return s;
+    }
+
+    /**
+     * 获取当前时间 微信随机字符串
+     *
+     * @return String
+     */
+
+
+    static public String nonceString() {
+
+        String currTime = String.format("%d", (long) System.currentTimeMillis() / 1000);
+
+        String strTime = currTime.substring(8, currTime.length());
+
+        Random random = new Random();
+        int num = (int) (random.nextDouble() * (1000000 - 100000) + 100000);
+        String code = String.format("%06d", num);
+
+        String nonce_str = currTime.substring(2) + code;
+        return nonce_str;
+
+    }
+
+    /**
+     * 返回微信秒为单位时间戳
+     *
+     * @return String
+     */
+
+
+    static public String timeStamp() {
+
+        String currTime = String.format("%d", (long) System.currentTimeMillis() / 1000);
+
+        return currTime;
+
+
+    }
+
+    /**
+     * 得到本地机器的IP
+     *
+     * @return
+     */
+    public static String getHostIp() {
+        String ip = "";
+        try {
+            ip = InetAddress.getLocalHost().getHostAddress();
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+        }
+        return ip;
+    }
+
+    /**
+     * @param
+     * @return
+     * @author
+     * @date 2016-4-22
+     * @Description:sign签名 JS_SDK使用权限签名算法
+     */
+    public static String signature(SortedMap<Object, Object> packageParams) {
+        StringBuffer sb = new StringBuffer();
+        Set es = packageParams.entrySet();
+        Iterator it = es.iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String k = (String) entry.getKey();
+            String v = (String) entry.getValue();
+            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
+                sb.append(k + "=" + v + "&");
+            }
+        }
+
+        //最后一个&截掉
+        String s = sb.substring(0, sb.lastIndexOf("&"));
+        String sign = ShaUtils.getSha1(s);
+        return sign;
+    }
+
+
+}

+ 148 - 0
src/main/java/com/gree/mall/contest/utils/email/EmailUtilsNew.java

@@ -0,0 +1,148 @@
+package com.gree.mall.contest.utils.email;
+
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
+import javax.mail.*;
+import javax.mail.internet.*;
+import java.util.Properties;
+
+@Slf4j
+public class EmailUtilsNew {
+
+    private static final String ALIDM_SMTP_HOST = "smtpdm.aliyun.com";
+    private static final int ALIDM_SMTP_PORT = 80;// 或80
+
+
+    // 发件人的账号 和 密码
+    private String user;
+    private String password;
+
+    public EmailUtilsNew() {
+        this("zhongju@gm.zfire.top", "PRivate123");
+    }
+
+    public EmailUtilsNew(String user, String password) {
+        this.user = user;
+        this.password = password;
+    }
+
+    public static void main(String[] args) {
+        new EmailUtilsNew().send("448797381@qq.com", "测试1", "nihao显示");
+        new EmailUtilsNew().send("448797381@qq.com", "测试1", "市劳动纠纷联赛积分了","B:/21.11仓储维修.xls");
+    }
+
+    /**
+     * 发送邮件
+     * @param toEmail  收件人邮箱地址
+     * @param subject 邮件标题
+     * @param content  邮件内容 可以是html内容
+     */
+    public void send(String toEmail, String subject, String content) {
+        Session session = loadMailSession();
+        // session.setDebug(true);
+        // 创建邮件消息
+        MimeMessage message = new MimeMessage(session);
+        try {
+            // 设置发件人
+            message.setFrom(new InternetAddress(user));
+            Address[] a = new Address[1];
+            a[0] = new InternetAddress(user);
+            message.setReplyTo(a);
+            // 设置收件人
+            InternetAddress to = new InternetAddress(toEmail);
+            message.setRecipient(MimeMessage.RecipientType.TO, to);
+            // 设置邮件标题
+            message.setSubject(subject);
+            // 设置邮件的内容体
+            message.setContent(content, "text/html;charset=UTF-8");
+            // 发送邮件
+            Transport.send(message);
+        } catch (Exception e) {
+            log.error("【发送邮件失败】",e);
+        }
+    }
+
+
+    /**
+     * 发送邮件 带附件
+     * @param toEmail  收件人邮箱地址
+     * @param subject  邮件标题
+     * @param content  邮件内容 可以是html内容
+     * @param attachPath 附件路径
+     */
+    public void send(String toEmail, String subject, String content, String attachPath) {
+        Session session = loadMailSession();
+
+        MimeMessage mm = new MimeMessage(session);
+        try {
+            //发件人
+            mm.setFrom(new InternetAddress(user));
+            //收件人
+            mm.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmail)); // 设置收件人
+            // mm.setRecipient(Message.RecipientType.CC, new
+            // InternetAddress("XXXX@qq.com")); //设置抄送人
+            //标题
+            mm.setSubject(subject);
+            //内容
+            Multipart multipart = new MimeMultipart();
+            //body部分
+            BodyPart contentPart = new MimeBodyPart();
+            contentPart.setContent(content, "text/html;charset=utf-8");
+            multipart.addBodyPart(contentPart);
+
+            //附件部分
+            BodyPart attachPart = new MimeBodyPart();
+            FileDataSource fileDataSource = new FileDataSource(attachPath);
+            attachPart.setDataHandler(new DataHandler(fileDataSource));
+            attachPart.setFileName(MimeUtility.encodeText(fileDataSource.getName()));
+            multipart.addBodyPart(attachPart);
+
+            mm.setContent(multipart);
+            Transport.send(mm);
+        } catch (Exception e) {
+            String err = e.getMessage();
+            // 在这里处理message内容, 格式是固定的
+            System.out.println(err);
+        }
+
+    }
+
+    private Session loadMailSession() {
+        try {
+            // 配置发送邮件的环境属性
+            final Properties props = new Properties();
+            // 表示SMTP发送邮件,需要进行身份验证
+            props.put("mail.smtp.auth", "true");
+            props.put("mail.smtp.host", ALIDM_SMTP_HOST);
+            // props.put("mail.smtp.port", ALIDM_SMTP_PORT);
+            // 如果使用ssl,则去掉使用25端口的配置,进行如下配置,
+            props.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");
+            props.put("mail.smtp.socketFactory.port", "465");
+            props.put("mail.smtp.port", "465");
+            // 发件人的账号
+            props.put("mail.user", user);
+            // 访问SMTP服务时需要提供的密码
+            props.put("mail.password", password);
+            // 构建授权信息,用于进行SMTP进行身份验证
+            Authenticator authenticator = new Authenticator() {
+                @Override
+                protected PasswordAuthentication getPasswordAuthentication() {
+                    // 用户名、密码
+                    String userName = props.getProperty("mail.user");
+                    String password = props.getProperty("mail.password");
+                    return new PasswordAuthentication(userName, password);
+                }
+            };
+            // 使用环境属性和授权信息,创建邮件会话
+            return Session.getInstance(props, authenticator);
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println("mail session is null");
+        }
+        return null;
+    }
+
+}

+ 319 - 0
src/main/java/com/gree/mall/contest/utils/http/HttpUtils.java

@@ -0,0 +1,319 @@
+package com.gree.mall.contest.utils.http;
+
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import okhttp3.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *
+ */
+public class HttpUtils {
+
+    private static HttpUtils httpUtils;
+    private OkHttpClient okHttpClient;
+
+    @Value("${web.imageService}")
+    private static String wanPath;
+
+    private final static Logger logger = LoggerFactory.getLogger(HttpUtils.class);
+
+    private HttpUtils() {
+        okHttpClient = new OkHttpClient();
+        okHttpClient.newBuilder().
+                connectTimeout(60, TimeUnit.SECONDS).
+                readTimeout(60, TimeUnit.SECONDS)
+                .writeTimeout(60, TimeUnit.SECONDS);
+
+    }
+
+    //创建 单例模式(OkHttp官方建议如此操作)
+    public static HttpUtils getInstance() {
+        if (httpUtils == null) {
+            httpUtils = new HttpUtils();
+        }
+        return httpUtils;
+    }
+
+    static private Request.Builder getGetBuilder(String url, Map<String, String> params, Map<String, String> heads) {
+        Request.Builder reqBuild = new Request.Builder();
+        HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
+
+        if (params != null) {
+            for (String key : params.keySet()) {
+                urlBuilder.addQueryParameter(key, params.get(key));
+            }
+        }
+
+
+        reqBuild.url(urlBuilder.build());
+        if (heads != null) {
+            for (String key : heads.keySet()) {
+                reqBuild.addHeader(key, heads.get(key));
+            }
+        }
+
+        return reqBuild;
+    }
+
+
+
+
+
+
+    static public String requestGet(String url, Map<String, String> params, Map<String, String> heads) throws RemoteServiceException {
+        Request.Builder reqBuild = getGetBuilder(url, params, heads);
+        Request req = reqBuild.build();
+
+        try {
+            Response response = OkHttpUtil.execute(req);
+            //判断请求是否成功
+            if (response.isSuccessful()) {
+                String content = response.body().string();
+                logger.info("GET Response" + content);
+                return content;
+                //打印服务端返回结果
+            } else {
+                String content = response.body().string();
+                logger.info("ERROR Response" + content);
+                throw new RemoteServiceException(response.code(), response.message());
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            logger.error(HttpUtils.class.getName(), e);
+            throw new RemoteServiceException(505, e.getMessage());
+        }
+    }
+
+    static public String requestGetYB(String url, Map<String, String> params, Map<String, String> heads, Callback callback) {
+        Request.Builder reqBuild = getGetBuilder(url, params, heads);
+        Request req = reqBuild.build();
+
+        try {
+            OkHttpUtil.enqueue(req, callback);
+        } catch (Exception e) {
+            e.printStackTrace();
+            logger.error(HttpUtils.class.getName(), e);
+        }
+        return "ok";
+    }
+
+
+    static private Request.Builder getBodyBuilder(String url, String json, Map<String, String> heads) {
+        logger.info("requestPostBody url:" + url);
+        logger.info("body:" + json);
+        MediaType JSONType = MediaType.parse("application/json; charset=utf-8");
+        RequestBody body = RequestBody.create(JSONType, json);
+        Request.Builder builder = new Request.Builder();
+
+        builder.url(url);
+
+        if (heads != null) {
+            for (String key : heads.keySet()) {
+
+                builder.addHeader(key, heads.get(key));
+            }
+        }
+        builder.post(body);
+        return builder;
+    }
+
+    static private Request.Builder getFormBuilder(String url, Map<String, String> params, Map<String, String> heads) {
+        FormBody.Builder bodyBuilder = new FormBody.Builder();
+
+        logger.info("form url:" + url);
+        logger.info("params:" + params.toString());
+
+        try {
+            if (params != null) {
+                for (String key : params.keySet()) {
+                    if (StringUtils.isNotEmpty(params.get(key))) {
+                        bodyBuilder.add(key, params.get(key));
+                    } else {
+                        bodyBuilder.add(key, "");
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            //System.out.println(e);
+        }
+
+        RequestBody body = bodyBuilder.build();
+        Request.Builder builder = new Request.Builder();
+
+        if (heads != null) {
+            for (String key : heads.keySet()) {
+
+                builder.addHeader(key, heads.get(key));
+            }
+        }
+        builder.url(url);
+        builder.post(body);
+
+        return builder;
+
+    }
+
+
+    static public String requestPostBody(String url, String json, Map<String, String> heads) throws RemoteServiceException {
+
+
+        Request.Builder builder = getBodyBuilder(url,json,heads);
+        Request req = builder.build();
+
+        try {
+            Response response = OkHttpUtil.execute(req);
+            logger.error(response.toString());
+            if (response.isSuccessful()) {
+
+
+                String returnString = response.body().string();
+
+                logger.info("success " + returnString);
+
+                return returnString;
+                //打印服务端返回结果
+
+            } else {
+
+                logger.error("failure: " + response.body().string());
+
+                throw new RemoteServiceException(response.code(), response.message());
+            }
+            //throw   new ApiException(response.code(),response.body().string());
+        } catch (Exception e) {
+            e.printStackTrace();
+            logger.error(HttpUtils.class.getName(), e);
+            throw new RemoteServiceException(505, e.getMessage());
+
+
+        }
+
+
+    }
+
+    static public String requestPostBodyYB(String url, String json, Map<String, String> heads, Callback cb) {
+        Request.Builder builder = getBodyBuilder(url,json,heads);
+        Request req = builder.build();
+        try {
+            OkHttpUtil.enqueue(req, cb);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+    static public String requestPostForm(String url, Map<String, String> params, Map<String, String> heads) throws RemoteServiceException {
+
+
+
+        Request.Builder builder = getFormBuilder(url,params,heads);
+        Request req = builder.build();
+
+        try {
+            Response response = OkHttpUtil.execute(req);
+            if (response.isSuccessful()) {
+                return response.body().string();
+            } else {
+                throw new RemoteServiceException(response.code(), response.message());
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RemoteServiceException(505, e.getMessage());
+        }
+    }
+
+    static public String requestPostFormYB(String url, Map<String, String> params, Map<String, String> heads, Callback cb) {
+
+        Request.Builder builder = getFormBuilder(url,params,heads);
+        Request req = builder.build();
+        try {
+            OkHttpUtil.enqueue(req, cb);
+            //打印服务端返回结果
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return "";
+
+    }
+
+    public String downloadFile(String path, String url, String json, Map<String, String> heads){
+
+        MediaType JSONType = MediaType.parse("application/json; charset=utf-8");
+        RequestBody body = RequestBody.create(JSONType, json);
+        Request.Builder builder = new Request.Builder();
+        builder.url(url);
+        if (heads != null) {
+            for (String key : heads.keySet()) {
+                builder.addHeader(key, heads.get(key));
+            }
+        }
+
+        builder.post(body);
+        Request req = builder.build();
+
+
+        InputStream is = null;
+        FileOutputStream fos = null;
+        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
+        String dateString = df.format(new Date());
+        String newFilePath = path + "/upload/miniqrcode/" + dateString + "/";
+        String newFileName = new Date().getTime() + ".png";
+        File checkFile = new File(newFilePath);
+        if (!checkFile.exists() && !checkFile.isDirectory()) {
+            checkFile.mkdirs();
+        }
+
+        try {
+            Response response = okHttpClient.newCall(req).execute();
+            if (response.isSuccessful()) {
+                byte[] buf = new byte[1024];
+                int len = 0;
+                is = response.body().byteStream();
+                File file = new File(newFilePath, newFileName);
+
+                fos = new FileOutputStream(file);
+                while ((len = is.read(buf)) != -1) {
+                    fos.write(buf, 0, len);
+                }
+                fos.flush();
+                //如果下载文件成功,第一个参数为文件的绝对路径
+            } else {
+                return null;
+            }
+        } catch (IOException e) {
+            System.out.println("数据获取失败");
+        } finally {
+            try {
+                if (is != null)
+                    is.close();
+            } catch (IOException e) {
+            }
+            try {
+                if (fos != null)
+                    fos.close();
+            } catch (IOException e) {
+            }
+        }
+
+        return "/miniqrcode/" + dateString + "/" + newFileName;
+
+    }
+
+//    新创建积分商品二维码路径
+
+
+}

+ 110 - 0
src/main/java/com/gree/mall/contest/utils/http/OkHttpUtil.java

@@ -0,0 +1,110 @@
+package com.gree.mall.contest.utils.http;
+
+import okhttp3.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author: duke
+ * @Date: 2018/4/16 下午2:13
+ */
+public class OkHttpUtil {
+    private final static Logger logger = LoggerFactory.getLogger(OkHttpUtil.class);
+    private static final OkHttpClient mOkHttpClient = new OkHttpClient();
+
+    static {
+        OkHttpClient.Builder builder = mOkHttpClient.newBuilder();
+        builder.connectTimeout(180, TimeUnit.SECONDS)
+                .readTimeout(180, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 该不会开启异步线程。
+     *
+     * @param request
+     * @return
+     * @throws IOException
+     */
+    public static Response execute(Request request) throws IOException {
+        return mOkHttpClient.newCall(request).execute();
+    }
+
+    /**
+     * 开启异步线程访问网络
+     *
+     * @param request
+     * @param responseCallback
+     */
+    public static void enqueue(Request request, Callback responseCallback) {
+        mOkHttpClient.newCall(request).enqueue(responseCallback);
+    }
+
+    /**
+     * 开启异步线程访问网络, 且不在意返回结果(实现空callback)
+     *
+     * @param request
+     */
+    public static void enqueue(Request request) {
+        mOkHttpClient.newCall(request).enqueue(new Callback() {
+            @Override
+            public void onFailure(Call call, IOException e) {
+                logger.error("enqueue", e);
+            }
+
+            @Override
+            public void onResponse(Call call, Response response) throws IOException {
+
+            }
+        });
+    }
+
+    public static String getStringFromServer(String url) throws IOException {
+        Request request = new Request.Builder().url(url).build();
+        Response response = execute(request);
+        if (response.isSuccessful()) {
+            String responseUrl = response.body().string();
+            return responseUrl;
+        } else {
+            logger.error("getStringFromServer", response);
+            throw new IOException("Unexpected code " + response);
+        }
+    }
+
+    private static final String CHARSET_NAME = "UTF-8";
+
+    /**
+     * 这里使用了HttpClinet的API。只是为了方便
+     *
+     * @param params
+     * @return
+     */
+//    public static String formatParams(List<BasicNameValuePair> params) {
+//        return URLEncodedUtils.format(params, CHARSET_NAME);
+//    }
+
+    /**
+     * 为HttpGet 的 url 方便的添加多个name value 参数。
+     *
+     * @param url
+     * @param params
+     * @return
+     */
+//    public static String attachHttpGetParams(String url, List<BasicNameValuePair> params) {
+//        return url + "?" + formatParams(params);
+//    }
+
+    /**
+     * 为HttpGet 的 url 方便的添加1个name value 参数。
+     *
+     * @param url
+     * @param name
+     * @param value
+     * @return
+     */
+    public static String attachHttpGetParam(String url, String name, String value) {
+        return url + "?" + name + "=" + value;
+    }
+}

+ 93 - 0
src/main/resources/mapper/CategoryMapper.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.gree.mall.contest.commonmapper.CategoryMapper">
+    <select id="queryCategory" resultMap="category">
+        select
+            gcl.*,
+            gcr.parent_id as sub_parent_id,
+            gcr.category_id as sub_category_id,
+            gcr.img_url as sub_img_url,
+            gcr.goods_num as sub_goods_num,
+            gcr.sort_num as sub_sort_num,
+            gcr.level as sub_level
+        from goods_category gcl
+        left join
+             goods_category gcr
+        on gcl.category_id=gcr.parent_id
+        where gcl.del=0 and gcl.status = 1 and gcl.parent_id is NULL and gcr.del=0 and gcr.status = 1 and gcr.company_wechat_id=#{companyWechatId}
+        order by gcl.sort_num ASC, gcr.sort_num ASC
+    </select>
+    <resultMap id="category" type="com.gree.mall.contest.bean.goods.CategoryBean">
+        <id property="categoryId" column="category_id"/>
+        <result property="name" column="name"/>
+        <result property="imgUrl" column="img_url"/>
+        <result property="goodsNum" column="goods_num"/>
+        <result property="parentId" column="parent_id"/>
+        <result property="sortNum" column="sort_num"/>
+        <result property="level" column="level"/>
+        <collection property="sub" javaType="ArrayList" column="sub_parent_id" resultMap="category"  columnPrefix="sub_">
+
+        </collection>
+    </resultMap>
+
+    <select id="listGoods" resultType="com.gree.mall.contest.bean.goods.GoodsNewBean">
+        SELECT
+            a.goods_id,
+            a.goods_name,
+            a.describe_text,
+            a.img_url,
+            a.vedio,
+            a.category_id,
+            a.org_goods_price,
+            a.goods_price,
+            a.sold_num,
+            a.has_spec,
+            a.show_detail_stock,
+            a.show_detail_sold,
+            a.freight_type,
+            a.freight_template_id,
+            a.freight,
+            a.content,
+            a.sort_num,
+            a.STATUS,
+            a.promotion_group,
+            a.marketing_id,
+            a.marketing_name,
+            a.del,
+            a.create_time,
+            a.company_wechat_id,
+            a.company_name,
+            a.logo,
+            a.logo_start_time,
+            a.logo_end_time
+        FROM
+        goods a
+        where a.company_wechat_id = #{currentCompanyWechatId} and a.del=0 and a.status=1 and promotion_group =0
+        <if test="categoryId != null and categoryId != ''">
+            and category_id=#{categoryId}
+        </if>
+        <if test="sort != null and sort==0">
+            order by a.sort_num asc
+        </if>
+        <if test="sort != null and sort==1">
+            order by a.sold_num desc
+        </if>
+        <if test="sort != null and sort==2">
+            order by goods_price asc
+        </if>
+        <if test="sort != null and sort==3">
+            order by goods_price desc
+        </if>
+        <if test="sort != null and sort==4">
+            order by a.create_time desc
+        </if>
+    </select>
+
+
+    <select id="queryGoodsCategoryByGoodsId" resultType="com.gree.mall.contest.plus.entity.GoodsCategory">
+        select a.* from goods_category a
+        join goods b on a.category_id = b.category_id
+        where b.goods_id=#{goodsId}
+    </select>
+
+</mapper>

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

@@ -73,5 +73,15 @@
         </if>
         ORDER BY send_time DESC
     </select>
+    <select id="pageApply" resultType="com.gree.mall.contest.bean.merchant.WebsitApplyVO">
+        SELECT
+        ${ex.selected}
+        FROM websit_apply a
+        ${ex.query}
+        <if test="ex.orderBy == null or ex.orderBy ==''">
+            ORDER BY a.create_time DESC
+        </if>
+        ${ex.orderBy}
+    </select>
 
 </mapper>

+ 85 - 0
src/main/resources/mapper/CustomCoupouMapper.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.gree.mall.contest.commonmapper.CustomCoupouMapper">
+    <select id="listCoupou" resultType="com.gree.mall.contest.bean.coupon.CustomCoupouBean">
+        Select t.*
+        from ((SELECT uc.*,
+        (uc.order_amount &lt;= #{orderAmount} and (uc.transfer_type = 0 or (uc.transfer_type = 1 and uc.left_share_times>0))) as useable_flag
+        FROM user_coupon uc
+        WHERE uc.user_id = #{userId}
+        and uc.STATUS = 0
+        and uc.coupon_type = 'SATISFY'
+        AND uc.state = 1
+        and now() BETWEEN uc.active_start_time and uc.active_end_time)
+        union
+        (
+        select uc.*,if((uc.transfer_type = 0 or (uc.transfer_type = 1 and uc.left_share_times>0))
+        <if test="goodsIds!=null and goodsIds.size>0">
+            and count(if(cg.goods_id in
+            <foreach collection="goodsIds" item="item" open="(" separator="," close=")">#{item}</foreach>
+            ,1,null) )>0
+        </if>
+        <if test="goodsIds==null or goodsIds.size&lt;1">
+            and 1=0
+        </if>
+        ,1,0) as useable_flag
+        FROM user_coupon uc
+        left join coupon_goods cg on cg.coupon_id = uc.coupon_id
+        WHERE uc.user_id = #{userId}
+        and uc.STATUS = 0
+        and uc.coupon_type = 'GOODS'
+        AND uc.state = 1
+        and now() BETWEEN uc.active_start_time and uc.active_end_time
+        group by uc.id
+        )) t
+        ORDER BY t.order_amount desc
+    </select>
+
+    <!--0/1/2  未到领取时间/可领/已领取可用(分享券剩余0时不可用)-->
+    <select id="listObtainCoupou" resultType="com.gree.mall.contest.bean.coupon.CouponObtainBean">
+        (SELECT cp.*, 2 as obtainType
+         from coupon cp
+                  inner join user_coupon uc on uc.coupon_id = cp.coupon_id
+         where uc.status = 0
+           and uc.state = 1
+           and uc.user_id = #{userId}
+           and (now() BETWEEN uc.active_start_time AND uc.active_end_time)
+           and now() > cp.display_time
+           and now() &lt; cp.obtain_end_time
+           and if(uc.transfer_type = 1 and uc.left_share_times&lt;1,0,1)
+           and cp.company_wechat_id = #{companyWechatId}
+         group by cp.coupon_id
+         ORDER BY cp.create_time Desc)
+        UNION
+        (SELECT cp.*, 0 as obtainType
+         from coupon cp
+                  LEFT JOIN coupon_user cu ON cp.coupon_id = cu.coupon_id
+                  left JOIN user ur on ur.user_id = cu.user_id
+         WHERE (cp.receive_crowd = 0 or (cu.user_id = #{userId} and cp.receive_crowd &lt;> 0))
+           and cp.left_amount > 0
+           and cp.flag &lt;> 'CANCEL'
+           and (now() &lt; cp.obtain_start_time OR now() > cp.obtain_end_time)
+           and now() > cp.display_time
+           and now() &lt; cp.obtain_end_time
+           and cp.company_wechat_id = #{companyWechatId}
+         group by cp.coupon_id)
+        UNION
+        (SELECT cp.*,
+                1 as obtainType
+         from coupon cp
+                  LEFT JOIN coupon_user cu on cp.coupon_id = cu.coupon_id
+                  left join user_coupon s on s.coupon_id = cp.coupon_id
+         WHERE (cp.receive_crowd = 0 or (cu.user_id = #{userId} and cp.receive_crowd &lt;> 0))
+           and cp.left_amount > 0
+           and cp.flag &lt;> 'CANCEL'
+           and now() BETWEEN cp.obtain_start_time AND cp.obtain_end_time
+           AND now() > cp.display_time
+           and now() &lt; cp.obtain_end_time
+           and cp.company_wechat_id = #{companyWechatId}
+         group by cp.coupon_id
+         having count(if(s.user_id = #{userId}, s.user_id, null)) &lt; cp.receive_limit_count
+             or cp.receive_limit_count = 0)
+    </select>
+
+
+</mapper>

+ 16 - 0
src/main/resources/mapper/CustomGoodsMapper.xml

@@ -222,5 +222,21 @@
         ${ex.orderBy}
     </select>
 
+    <select id="queryGoodsFavorite" resultType="com.gree.mall.contest.bean.goods.GoodsFavoriteBean">
+        select gf.goods_favorite_id, gs.*
+        from goods_favorite gf
+                 left join goods gs
+                           on gs.goods_id = gf.goods_id
+        where gf.user_id = #{userId}
+        order by gf.create_time DESC
+        limit #{pageNum},#{pageSize}
+    </select>
+    <select id="queryGoodsFavoriteCount" resultType="java.lang.Integer">
+        select count(*)
+        from goods_favorite gf
+                 left join goods gs
+                           on gs.goods_id = gf.goods_id
+        where gf.user_id = #{userId}
+    </select>
 
 </mapper>

+ 137 - 0
src/main/resources/mapper/GoodsSpecDetailMapper.xml

@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.gree.mall.contest.commonmapper.GoodsSpecDetailMapper">
+    <select id="querySpecSec" resultType="com.gree.mall.contest.bean.goods.GoodsSpecSecBean">
+        select
+          gs.*,
+		  a.org_goods_price
+        from goods a join goods_spec gs on a.goods_id = gs.goods_id
+        where gs.goods_id = #{goodsId} and gs.del = 0
+        GROUP BY gs.goods_spec_id
+    </select>
+
+
+    <select id="queryCommonTemplate" resultType="com.gree.mall.contest.plus.entity.CommonTemplate">
+        SELECT
+            ct.*
+        FROM
+            goods_template gt
+                RIGHT JOIN common_template ct ON ct.common_template_id = gt.template_id and ct.type=1
+        WHERE
+            gt.goods_id = #{goodsId}
+        order by ct.create_time desc
+        limit 1
+    </select>
+
+
+    <select id="queryPromotionGoods" resultType="com.gree.mall.contest.bean.goods.PromotionGoodsBean">
+        select
+            a.promotion_group_id,
+            a.goods_id,
+            a.goods_name,
+            a.goods_img_src,
+            a.group_price group_price,
+            b.sales_num sale_num,
+            b.stock stock,
+            b.org_goods_price org_goods_price
+        from
+        promotion_group_spec_user a
+        join  promotion_group_spec b on a.promotion_group_spec_id = b.promotion_group_spec_id
+        join promotion_group c on c.promotion_group_id = b.promotion_group_id
+        join goods d on d.goods_id = a.goods_id
+        where a.user_id=#{userId} and c.status=1 and d.del=0 and now() between a.start_time and a.end_time
+        and c.company_wechat_id=#{companyWechatId}
+        <if test="keyword != null and keyword !='' ">
+            and a.goods_name like concat('%',#{keyword},'%')
+        </if>
+        <if test="goodsCategoryId != null and goodsCategoryId !=''">
+            and a.goods_id in (
+                select b.goods_id from promotion_group_spec_user a
+                join goods b on a.goods_id = b.goods_id
+                join goods_category c on c.category_id=b.category_id
+                where c.parent_id = #{goodsCategoryId}
+            )
+        </if>
+        group by a.goods_id
+        order by b.sort_num,sale_num desc
+    </select>
+
+    <select id="goodsCommentList" resultType="com.gree.mall.contest.bean.goods.GoodsComment">
+        select b.user_name,d.avatar,a.* from order_comment a
+            join order_info b on a.order_id=b.order_id
+            join order_detail c on c.order_id=b.order_id
+            join user d on d.user_id = b.user_id
+            where  a.is_show=1 and  a.company_wechat_id=#{companyWechatId} and c.goods_id=#{goodsId}
+            <if test="tag != null and tag!=''">
+            and a.order_id in (
+				  select order_id from order_comment_tag where tag=#{tag} and company_wechat_id=#{companyWechatId}
+			)
+            </if>
+        order by a.create_time desc
+    </select>
+
+    <select id="goodsCommentCount" resultType="com.gree.mall.contest.bean.goods.CommentTagCount">
+        select d.tag,count(1) total from order_comment a
+            join order_info b on a.order_id=b.order_id
+            join order_detail c on c.order_id=b.order_id
+						join order_comment_tag d on d.order_id=a.order_id
+            where c.goods_id=#{goodsId} and a.is_show=1 and a.company_wechat_id=#{companyWechatId}
+						group by d.tag
+    </select>
+
+    <select id="queryGoodsPackage" resultType="com.gree.mall.contest.bean.goods.GoodsPackageBean">
+        select
+            c.*,
+            a.goods_name,
+            b.img_url,
+            b.spec_value,
+            b.org_price
+        from goods a
+        join goods_spec b on a.goods_id=b.goods_id
+        join goods_package_pop c on c.goods_spec_id=b.goods_spec_id
+        left join goods_package_user_rela d on d.goods_id = a.goods_id
+        where a.del=0 and a.status=true and b.del=0 and a.company_wechat_id= #{companyWechatId} and c.goods_package_id=#{goodsId}
+				and (
+						(a.package_user_type=0 and d.user_id=#{userId})
+				        or a.package_user_type=1
+						or (a.package_user_type=2 and #{userType}!='SERVICE')
+						or (a.package_user_type=3 and #{userType}='SERVICE')
+				)
+    </select>
+
+    <select id="queryGoodsList" resultType="com.gree.mall.contest.bean.goods.GoodsNewBean">
+        select
+            a.*
+        from goods a
+        where a.del=0 and a.status=true and a.promotion_group = 0
+                and a.company_wechat_id= #{companyWechatId}
+				and (
+						(a.package_user_type=0 and exists (select 1 from goods_package_user_rela where user_id=#{userId}) )
+				    or a.package_user_type=1
+						or (a.package_user_type=2 and #{userType}!='SERVICE')
+						or (a.package_user_type=3 and #{userType}='SERVICE')
+				)
+				<if test="keyword != null and keyword != ''">
+                    and a.goods_name like concat('%',#{keyword},'%')
+                </if>
+                <if test="categoryId != null and categoryId !=''">
+                    and a.category_id = #{categoryId}
+                </if>
+                <if test="sort != null and sort==0">
+                    order by a.sort_num asc
+                </if>
+                <if test="sort != null and sort==1">
+                    order by a.sold_num desc
+                </if>
+                <if test="sort != null and sort==2">
+                    order by goods_price asc
+                </if>
+                <if test="sort != null and sort==3">
+                    order by goods_price desc
+                </if>
+                <if test="sort != null and sort==4">
+                    order by a.create_time desc
+                </if>
+    </select>
+
+</mapper>

+ 20 - 0
src/main/resources/mapper/SoldNumMapper.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.gree.mall.contest.commonmapper.SoldNumMapper">
+
+
+    <update id="updateGoods">
+        update goods
+        set sold_num = sold_num + ${num}
+        where goods_id = #{goodsId}
+    </update>
+
+
+    <update id="updateGoodsSpec">
+        update goods_spec
+        set sold_num = sold_num + ${orderDetail.num}
+        where goods_spec_id = #{orderDetail.goodsSpecId}
+    </update>
+
+
+</mapper>