Browse Source

Merge branch 'dev_v1.3.4_20220902' into dev_v1.3.4_20220909

Eric 2 years ago
parent
commit
1fdf65f5d7
79 changed files with 6316 additions and 109 deletions
  1. 15 0
      cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/AdminFeignService.java
  2. 25 0
      cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/dto/CouponInfoApi.java
  3. 12 0
      cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/fallback/AdminFeignServiceFallback.java
  4. 4 4
      cooleshow-mall/mall-admin/src/main/java/com/yonge/cooleshow/admin/controller/PmsProductAttributeCategoryController.java
  5. 2 2
      cooleshow-mall/mall-admin/src/main/java/com/yonge/cooleshow/admin/service/PmsProductAttributeCategoryService.java
  6. 7 3
      cooleshow-mall/mall-admin/src/main/java/com/yonge/cooleshow/admin/service/impl/PmsProductAttributeCategoryServiceImpl.java
  7. 2 1
      cooleshow-mall/mall-admin/src/main/resources/config/mybatis/PmsProductAttributeCategoryDao.xml
  8. 3 3
      cooleshow-mall/mall-mbg/src/main/java/com/yonge/cooleshow/mbg/model/OmsOrder.java
  9. 11 0
      cooleshow-mall/mall-mbg/src/main/java/com/yonge/cooleshow/mbg/model/PmsProductAttributeCategory.java
  10. 7 7
      cooleshow-mall/mall-mbg/src/main/resources/config/mybatis/OmsOrderMapper.xml
  11. 16 1
      cooleshow-mall/mall-mbg/src/main/resources/config/mybatis/PmsProductAttributeCategoryMapper.xml
  12. 15 3
      cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/domain/OrderParam.java
  13. 1 1
      cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/service/impl/HomeServiceImpl.java
  14. 55 30
      cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/service/impl/OmsPortalOrderServiceImpl.java
  15. 1 1
      cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/service/impl/PmsPortalProductServiceImpl.java
  16. 1 1
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/AdminApplication.java
  17. 236 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/coupon/CouponInfoController.java
  18. 139 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/coupon/CouponIssueController.java
  19. 69 8
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/AdminClient.java
  20. 430 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/coupon/CouponInfoVO.java
  21. 243 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/coupon/CouponInventoryVO.java
  22. 755 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/coupon/CouponIssueVo.java
  23. 186 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/coupon/CouponOrderVO.java
  24. 11 1
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/task/TaskController.java
  25. 41 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/UserParam.java
  26. 17 7
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/req/OrderReq.java
  27. 5 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ActivityReward.java
  28. 117 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CouponInfo.java
  29. 65 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CouponInventory.java
  30. 98 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CouponIssue.java
  31. 127 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/SysUser.java
  32. 6 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/MK.java
  33. 1 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/MessageTypeEnum.java
  34. 35 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/SendTypeEnum.java
  35. 100 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponCategoryEnum.java
  36. 33 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponInventoryEnum.java
  37. 51 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponTypeEnum.java
  38. 35 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponUseStateEnum.java
  39. 33 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponValidTypeEnum.java
  40. 82 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/CouponInfoMapper.java
  41. 18 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/CouponInventoryMapper.java
  42. 36 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/CouponIssueMapper.java
  43. 34 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/SysUserMapper.java
  44. 182 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/queryInfo/CouponInfoQuery.java
  45. 48 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/queryInfo/CouponInventoryQuery.java
  46. 204 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/queryInfo/CouponIssueQueryInfo.java
  47. 93 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/CouponInfoService.java
  48. 16 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/CouponInventoryService.java
  49. 68 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/CouponIssueService.java
  50. 427 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CouponInfoServiceImp.java
  51. 20 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CouponInventoryServiceImp.java
  52. 298 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CouponIssueServiceImp.java
  53. 16 7
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseGroupServiceImpl.java
  54. 2 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseScheduleServiceImpl.java
  55. 17 8
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/MusicSheetServiceImpl.java
  56. 91 10
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/UserOrderServiceImpl.java
  57. 4 3
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/VideoLessonPurchaseRecordServiceImpl.java
  58. 70 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/CouponIssueUserVo.java
  59. 11 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/UserOrderVo.java
  60. 75 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/coupon/CouponInfoWrapper.java
  61. 297 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/coupon/CouponIssueWrapper.java
  62. 9 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wordfilter/WordContext.java
  63. 24 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/StatGroupWrapper.java
  64. 48 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/coupon/CouponInventoryWrapper.java
  65. 138 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/coupon/CouponOrderWrapper.java
  66. 188 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/CouponInfoMapper.xml
  67. 22 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/CouponInventoryMapper.xml
  68. 159 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/CouponIssueMapper.xml
  69. 1 1
      cooleshow-user/user-biz/src/main/resources/config/mybatis/MusicSheetMapper.xml
  70. 69 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/SysUserMapper.xml
  71. 1 1
      cooleshow-user/user-classroom/src/main/java/com/yonge/cooleshow/classroom/ClassroomApplication.java
  72. 1 1
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/StudentApplication.java
  73. 110 0
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/coupon/CouponInfoController.java
  74. 131 0
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/io/request/CouponInfoVO.java
  75. 1 1
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/TeacherApplication.java
  76. 108 0
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/coupon/CouponInfoController.java
  77. 134 0
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/io/request/CouponInfoVO.java
  78. 1 1
      cooleshow-user/user-website/src/main/java/com/yonge/cooleshow/website/WebsiteApplication.java
  79. 52 0
      toolset/toolset-base/src/main/java/com/yonge/toolset/base/util/ThreadPool.java

+ 15 - 0
cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/AdminFeignService.java

@@ -1,5 +1,6 @@
 package com.yonge.cooleshow.api.feign;
 
+import com.yonge.cooleshow.api.feign.dto.CouponInfoApi;
 import com.yonge.cooleshow.api.feign.dto.EmployeeApi;
 import com.yonge.cooleshow.api.feign.dto.StudentApi;
 import com.yonge.cooleshow.api.feign.dto.TeacherApi;
@@ -16,7 +17,9 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import java.math.BigDecimal;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Description
@@ -113,4 +116,16 @@ public interface AdminFeignService {
 
     @GetMapping(value = "/open/adminClient/getEmployee")
     HttpResponseResult<EmployeeApi> getEmployee(@RequestParam("userId") Long userId);
+
+
+    @PostMapping(value = "/open/adminClient/orderUpdate", consumes="application/json", produces="application/json")
+    HttpResponseResult<Boolean> updateCouponOrderInfo(@RequestParam("couponIssueId") String couponIssueId,
+                                                      @RequestParam("returnCoupon") Boolean returnCoupon,
+                                                      @RequestParam("orderNo") String orderNo);
+
+    @PostMapping(value = "/open/adminClient/orderInfo", consumes="application/json", produces="application/json")
+    HttpResponseResult<CouponInfoApi> queryCouponOrderPageInfo(@RequestParam("userId") Long userId,
+                                                               @RequestParam("issueIds") String issueIds,
+                                                               @RequestParam("orderAmount") BigDecimal orderAmount,
+                                                               @RequestParam("client") String client);
 }

+ 25 - 0
cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/dto/CouponInfoApi.java

@@ -0,0 +1,25 @@
+package com.yonge.cooleshow.api.feign.dto;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2022-09-07
+ */
+public class CouponInfoApi implements Serializable {
+
+
+    private BigDecimal discountedPrices;
+
+    public BigDecimal getDiscountedPrices() {
+        return discountedPrices;
+    }
+
+    public void setDiscountedPrices(BigDecimal discountedPrices) {
+        this.discountedPrices = discountedPrices;
+    }
+}

+ 12 - 0
cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/fallback/AdminFeignServiceFallback.java

@@ -1,6 +1,7 @@
 package com.yonge.cooleshow.api.feign.fallback;
 
 import com.yonge.cooleshow.api.feign.AdminFeignService;
+import com.yonge.cooleshow.api.feign.dto.CouponInfoApi;
 import com.yonge.cooleshow.api.feign.dto.EmployeeApi;
 import com.yonge.cooleshow.api.feign.dto.StudentApi;
 import com.yonge.cooleshow.api.feign.dto.TeacherApi;
@@ -9,6 +10,7 @@ import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.cooleshow.common.entity.MallOrderItemDto;
 import com.yonge.cooleshow.common.enums.UserFirstTimeTypeEnum;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -94,4 +96,14 @@ public class AdminFeignServiceFallback implements AdminFeignService {
     public HttpResponseResult<EmployeeApi> getEmployee(Long userId) {
         return null;
     }
+
+    @Override
+    public HttpResponseResult<Boolean> updateCouponOrderInfo(String couponIssueId, Boolean returnCoupon, String orderNo) {
+        return null;
+    }
+
+    @Override
+    public HttpResponseResult<CouponInfoApi> queryCouponOrderPageInfo(Long userId, String issueIds, BigDecimal orderAmount, String client) {
+        return null;
+    }
 }

+ 4 - 4
cooleshow-mall/mall-admin/src/main/java/com/yonge/cooleshow/admin/controller/PmsProductAttributeCategoryController.java

@@ -27,8 +27,8 @@ public class PmsProductAttributeCategoryController {
     @ApiOperation("添加商品属性分类")
     @RequestMapping(value = "/create", method = RequestMethod.POST)
     @ResponseBody
-    public CommonResult create(@RequestParam String name) {
-        int count = productAttributeCategoryService.create(name);
+    public CommonResult create(@RequestParam String name,@RequestParam Integer sort) {
+        int count = productAttributeCategoryService.create(name,sort);
         if (count > 0) {
             return CommonResult.success(count);
         } else {
@@ -39,8 +39,8 @@ public class PmsProductAttributeCategoryController {
     @ApiOperation("修改商品属性分类")
     @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
     @ResponseBody
-    public CommonResult update(@PathVariable Long id, @RequestParam String name) {
-        int count = productAttributeCategoryService.update(id, name);
+    public CommonResult update(@PathVariable Long id, @RequestParam String name,@RequestParam Integer sort) {
+        int count = productAttributeCategoryService.update(id, name,sort);
         if (count > 0) {
             return CommonResult.success(count);
         } else {

+ 2 - 2
cooleshow-mall/mall-admin/src/main/java/com/yonge/cooleshow/admin/service/PmsProductAttributeCategoryService.java

@@ -15,13 +15,13 @@ public interface PmsProductAttributeCategoryService {
      * 创建属性分类
      */
     @Transactional
-    int create(String name);
+    int create(String name, Integer sort);
 
     /**
      * 修改属性分类
      */
     @Transactional
-    int update(Long id, String name);
+    int update(Long id, String name, Integer sort);
 
     /**
      * 删除属性分类

+ 7 - 3
cooleshow-mall/mall-admin/src/main/java/com/yonge/cooleshow/admin/service/impl/PmsProductAttributeCategoryServiceImpl.java

@@ -24,17 +24,19 @@ public class PmsProductAttributeCategoryServiceImpl implements PmsProductAttribu
     private PmsProductAttributeCategoryDao productAttributeCategoryDao;
 
     @Override
-    public int create(String name) {
+    public int create(String name, Integer sort) {
         PmsProductAttributeCategory productAttributeCategory = new PmsProductAttributeCategory();
         productAttributeCategory.setName(name);
+        productAttributeCategory.setSort(sort);
         return productAttributeCategoryMapper.insertSelective(productAttributeCategory);
     }
 
     @Override
-    public int update(Long id, String name) {
+    public int update(Long id, String name, Integer sort) {
         PmsProductAttributeCategory productAttributeCategory = new PmsProductAttributeCategory();
         productAttributeCategory.setName(name);
         productAttributeCategory.setId(id);
+        productAttributeCategory.setSort(sort);
         return productAttributeCategoryMapper.updateByPrimaryKeySelective(productAttributeCategory);
     }
 
@@ -51,7 +53,9 @@ public class PmsProductAttributeCategoryServiceImpl implements PmsProductAttribu
     @Override
     public List<PmsProductAttributeCategory> getList(Integer pageSize, Integer pageNum) {
         PageHelper.startPage(pageNum,pageSize);
-        return productAttributeCategoryMapper.selectByExample(new PmsProductAttributeCategoryExample());
+        PmsProductAttributeCategoryExample pmsProductAttributeCategoryExample = new PmsProductAttributeCategoryExample();
+        pmsProductAttributeCategoryExample.setOrderByClause("sort desc");
+        return productAttributeCategoryMapper.selectByExample(pmsProductAttributeCategoryExample);
     }
 
     @Override

+ 2 - 1
cooleshow-mall/mall-admin/src/main/resources/config/mybatis/PmsProductAttributeCategoryDao.xml

@@ -10,7 +10,8 @@
             pac.id,
             pac.name,
             pa.id attr_id,
-            pa.name attr_name
+            pa.name attr_name,
+            pac.sort
         FROM
             pms_product_attribute_category pac
             LEFT JOIN pms_product_attribute pa ON pac.id = pa.product_attribute_category_id

+ 3 - 3
cooleshow-mall/mall-mbg/src/main/java/com/yonge/cooleshow/mbg/model/OmsOrder.java

@@ -11,7 +11,7 @@ public class OmsOrder implements Serializable {
 
     private Long memberId;
 
-    private Long couponId;
+    private String couponId;
 
     @ApiModelProperty("下单平台 STUDENT TEACHER")
     private String platformType;
@@ -180,11 +180,11 @@ public class OmsOrder implements Serializable {
         this.memberId = memberId;
     }
 
-    public Long getCouponId() {
+    public String getCouponId() {
         return couponId;
     }
 
-    public void setCouponId(Long couponId) {
+    public void setCouponId(String couponId) {
         this.couponId = couponId;
     }
 

+ 11 - 0
cooleshow-mall/mall-mbg/src/main/java/com/yonge/cooleshow/mbg/model/PmsProductAttributeCategory.java

@@ -16,8 +16,19 @@ public class PmsProductAttributeCategory implements Serializable {
     @ApiModelProperty(value = "参数数量")
     private Integer paramCount;
 
+    @ApiModelProperty("排序")
+    private Integer sort;
+
     private static final long serialVersionUID = 1L;
 
+    public Integer getSort() {
+        return sort;
+    }
+
+    public void setSort(Integer sort) {
+        this.sort = sort;
+    }
+
     public Long getId() {
         return id;
     }

+ 7 - 7
cooleshow-mall/mall-mbg/src/main/resources/config/mybatis/OmsOrderMapper.xml

@@ -4,7 +4,7 @@
   <resultMap id="BaseResultMap" type="com.yonge.cooleshow.mbg.model.OmsOrder">
     <id column="id" jdbcType="BIGINT" property="id" />
     <result column="member_id" jdbcType="BIGINT" property="memberId" />
-    <result column="coupon_id" jdbcType="BIGINT" property="couponId" />
+    <result column="coupon_id" property="couponId" />
     <result column="order_sn" jdbcType="VARCHAR" property="orderSn" />
     <result column="cart_ids" jdbcType="VARCHAR" property="cartIds" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
@@ -166,7 +166,7 @@
       delete_status, use_integration, payment_time, 
       delivery_time, receive_time, comment_time, 
       modify_time,cart_ids,platform_type,after_sale)
-    values (#{memberId,jdbcType=BIGINT}, #{couponId,jdbcType=BIGINT}, #{orderSn,jdbcType=VARCHAR}, 
+    values (#{memberId,jdbcType=BIGINT}, #{couponId}, #{orderSn,jdbcType=VARCHAR},
       #{createTime,jdbcType=TIMESTAMP}, #{memberUsername,jdbcType=VARCHAR}, #{totalAmount,jdbcType=DECIMAL}, 
       #{payAmount,jdbcType=DECIMAL}, #{freightAmount,jdbcType=DECIMAL}, #{promotionAmount,jdbcType=DECIMAL}, 
       #{integrationAmount,jdbcType=DECIMAL}, #{couponAmount,jdbcType=DECIMAL}, #{discountAmount,jdbcType=DECIMAL}, 
@@ -332,7 +332,7 @@
         #{memberId,jdbcType=BIGINT},
       </if>
       <if test="couponId != null">
-        #{couponId,jdbcType=BIGINT},
+        #{couponId},
       </if>
       <if test="orderSn != null">
         #{orderSn,jdbcType=VARCHAR},
@@ -484,7 +484,7 @@
         member_id = #{record.memberId,jdbcType=BIGINT},
       </if>
       <if test="record.couponId != null">
-        coupon_id = #{record.couponId,jdbcType=BIGINT},
+        coupon_id = #{record.couponId},
       </if>
       <if test="record.orderSn != null">
         order_sn = #{record.orderSn,jdbcType=VARCHAR},
@@ -627,7 +627,7 @@
     update oms_order
     set id = #{record.id,jdbcType=BIGINT},
       member_id = #{record.memberId,jdbcType=BIGINT},
-      coupon_id = #{record.couponId,jdbcType=BIGINT},
+      coupon_id = #{record.couponId},
       order_sn = #{record.orderSn,jdbcType=VARCHAR},
       create_time = #{record.createTime,jdbcType=TIMESTAMP},
       member_username = #{record.memberUsername,jdbcType=VARCHAR},
@@ -683,7 +683,7 @@
         member_id = #{memberId,jdbcType=BIGINT},
       </if>
       <if test="couponId != null">
-        coupon_id = #{couponId,jdbcType=BIGINT},
+        coupon_id = #{couponId},
       </if>
       <if test="orderSn != null">
         order_sn = #{orderSn,jdbcType=VARCHAR},
@@ -823,7 +823,7 @@
   <update id="updateByPrimaryKey" parameterType="com.yonge.cooleshow.mbg.model.OmsOrder">
     update oms_order
     set member_id = #{memberId,jdbcType=BIGINT},
-      coupon_id = #{couponId,jdbcType=BIGINT},
+      coupon_id = #{couponId},
       order_sn = #{orderSn,jdbcType=VARCHAR},
       create_time = #{createTime,jdbcType=TIMESTAMP},
       member_username = #{memberUsername,jdbcType=VARCHAR},

+ 16 - 1
cooleshow-mall/mall-mbg/src/main/resources/config/mybatis/PmsProductAttributeCategoryMapper.xml

@@ -6,6 +6,7 @@
     <result column="name" jdbcType="VARCHAR" property="name" />
     <result column="attribute_count" jdbcType="INTEGER" property="attributeCount" />
     <result column="param_count" jdbcType="INTEGER" property="paramCount" />
+    <result column="sort" jdbcType="INTEGER" property="sort" />
   </resultMap>
   <sql id="Example_Where_Clause">
     <where>
@@ -66,7 +67,7 @@
     </where>
   </sql>
   <sql id="Base_Column_List">
-    id, name, attribute_count, param_count
+    id, name, attribute_count, param_count,sort
   </sql>
   <select id="selectByExample" parameterType="com.yonge.cooleshow.mbg.model.PmsProductAttributeCategoryExample" resultMap="BaseResultMap">
     select
@@ -122,6 +123,9 @@
       <if test="paramCount != null">
         param_count,
       </if>
+      <if test="sort != null">
+        sort,
+      </if>
     </trim>
     <trim prefix="values (" suffix=")" suffixOverrides=",">
       <if test="name != null">
@@ -133,6 +137,9 @@
       <if test="paramCount != null">
         #{paramCount,jdbcType=INTEGER},
       </if>
+      <if test="sort != null">
+        #{sort,jdbcType=INTEGER},
+      </if>
     </trim>
   </insert>
   <select id="countByExample" parameterType="com.yonge.cooleshow.mbg.model.PmsProductAttributeCategoryExample" resultType="java.lang.Long">
@@ -156,6 +163,9 @@
       <if test="record.paramCount != null">
         param_count = #{record.paramCount,jdbcType=INTEGER},
       </if>
+      <if test="record.sort != null">
+        sort = #{record.sort,jdbcType=INTEGER},
+      </if>
     </set>
     <if test="_parameter != null">
       <include refid="Update_By_Example_Where_Clause" />
@@ -166,6 +176,7 @@
     set id = #{record.id,jdbcType=BIGINT},
       name = #{record.name,jdbcType=VARCHAR},
       attribute_count = #{record.attributeCount,jdbcType=INTEGER},
+      sort = #{record.sort,jdbcType=INTEGER},
       param_count = #{record.paramCount,jdbcType=INTEGER}
     <if test="_parameter != null">
       <include refid="Update_By_Example_Where_Clause" />
@@ -183,6 +194,9 @@
       <if test="paramCount != null">
         param_count = #{paramCount,jdbcType=INTEGER},
       </if>
+      <if test="sort != null">
+        sort = #{sort,jdbcType=INTEGER},
+      </if>
     </set>
     where id = #{id,jdbcType=BIGINT}
   </update>
@@ -190,6 +204,7 @@
     update pms_product_attribute_category
     set name = #{name,jdbcType=VARCHAR},
       attribute_count = #{attributeCount,jdbcType=INTEGER},
+      sort = #{sort,jdbcType=INTEGER},
       param_count = #{paramCount,jdbcType=INTEGER}
     where id = #{id,jdbcType=BIGINT}
   </update>

+ 15 - 3
cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/domain/OrderParam.java

@@ -2,6 +2,7 @@ package com.yonge.cooleshow.portal.domain;
 
 import io.swagger.annotations.ApiModelProperty;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -13,7 +14,7 @@ public class OrderParam {
     @ApiModelProperty("收货地址ID")
     private Long memberReceiveAddressId;
     @ApiModelProperty("优惠券ID")
-    private Long couponId;
+    private String couponId;
     @ApiModelProperty("使用的积分数")
     private Integer useIntegration;
     @ApiModelProperty("支付方式 :0->未支付;1->支付宝;2->微信")
@@ -24,6 +25,17 @@ public class OrderParam {
     @ApiModelProperty("平台类型 STUDENT:学生端  TEACHER:老师端")
     private String platformType;
 
+    @ApiModelProperty("订单金额")
+    private BigDecimal orderAmount;
+
+    public BigDecimal getOrderAmount() {
+        return orderAmount;
+    }
+
+    public void setOrderAmount(BigDecimal orderAmount) {
+        this.orderAmount = orderAmount;
+    }
+
     public String getPlatformType() {
         return platformType;
     }
@@ -40,11 +52,11 @@ public class OrderParam {
         this.memberReceiveAddressId = memberReceiveAddressId;
     }
 
-    public Long getCouponId() {
+    public String getCouponId() {
         return couponId;
     }
 
-    public void setCouponId(Long couponId) {
+    public void setCouponId(String couponId) {
         this.couponId = couponId;
     }
 

+ 1 - 1
cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/service/impl/HomeServiceImpl.java

@@ -103,7 +103,7 @@ public class HomeServiceImpl implements HomeService {
     private  List<PmsProductAttributeCategory>  getHomeProductAttributeCategory() {
         PageHelper.startPage(1, 10);
         PmsProductAttributeCategoryExample example = new PmsProductAttributeCategoryExample();
-        example.setOrderByClause("id desc");
+        example.setOrderByClause("sort desc,id desc");
         List<PmsProductAttributeCategory> pmsProductCategories = productAttributeCategoryMapper.selectByExample(example);
         if (CollectionUtils.isEmpty(pmsProductCategories)) {
             return new ArrayList<>();

+ 55 - 30
cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/service/impl/OmsPortalOrderServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.collection.CollUtil;
 import com.alibaba.fastjson.JSON;
 import com.github.pagehelper.PageHelper;
 import com.yonge.cooleshow.api.feign.AdminFeignService;
+import com.yonge.cooleshow.api.feign.dto.CouponInfoApi;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.cooleshow.common.entity.MallOrderItemDto;
 import com.yonge.cooleshow.common.enums.PostStatusEnum;
@@ -148,6 +149,9 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
 
     @Override
     public Map<String, Object> generateOrder(OrderParam orderParam) {
+        if (orderParam.getOrderAmount() == null) {
+            throw new BizException("订单金额校验不通过");
+        }
         List<OmsOrderItem> orderItemList = new ArrayList<>();
         //获取购物车及优惠信息
         UmsMember currentMember = memberService.getCurrentMember();
@@ -178,20 +182,31 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
         if (!hasStock(cartPromotionItemList)) {
             Asserts.fail("库存不足,无法下单");
         }
+        BigDecimal couponAmount = BigDecimal.ZERO;
         //判断使用使用了优惠券
-        if (orderParam.getCouponId() == null) {
+        if (StringUtil.isEmpty(orderParam.getCouponId())) {
             //不用优惠券
             for (OmsOrderItem orderItem : orderItemList) {
                 orderItem.setCouponAmount(new BigDecimal(0));
             }
         } else {
-            //使用优惠券
-            SmsCouponHistoryDetail couponHistoryDetail = getUseCoupon(cartPromotionItemList, orderParam.getCouponId());
-            if (couponHistoryDetail == null) {
-                Asserts.fail("该优惠券不可用");
-            }
+            // todo 使用优惠券 远程调用获取优惠券金额
+            // SmsCouponHistoryDetail couponHistoryDetail = getUseCoupon(cartPromotionItemList, orderParam.getCouponId());
+            //     if (couponHistoryDetail == null) {
+            //         Asserts.fail("该优惠券不可用");
+            // }
             //对下单商品的优惠券进行处理
-            handleCouponAmount(orderItemList, couponHistoryDetail);
+            HttpResponseResult<CouponInfoApi> couponInfoApiHttpResponseResult = adminFeignService.queryCouponOrderPageInfo(
+                    currentMember.getId(), orderParam.getCouponId(), orderParam.getOrderAmount(),
+                    orderParam.getPlatformType());
+            CouponInfoApi data = couponInfoApiHttpResponseResult.getData();
+            if(data != null) {
+                couponAmount = data.getDiscountedPrices();
+            }
+            if (couponAmount.compareTo(orderParam.getOrderAmount()) > 0) {
+                couponAmount = orderParam.getOrderAmount();
+            }
+            handleCouponAmount(orderItemList, couponAmount);
         }
         //判断是否使用积分
         if (orderParam.getUseIntegration() == null || orderParam.getUseIntegration().equals(0)) {
@@ -234,11 +249,11 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
         order.setPromotionAmount(calcPromotionAmount(orderItemList));
         order.setPromotionInfo(getOrderPromotionInfo(orderItemList));
         order.setPlatformType(orderParam.getPlatformType());
-        if (orderParam.getCouponId() == null) {
+        if (StringUtil.isEmpty(orderParam.getCouponId())) {
             order.setCouponAmount(new BigDecimal(0));
         } else {
             order.setCouponId(orderParam.getCouponId());
-            order.setCouponAmount(calcCouponAmount(orderItemList));
+            order.setCouponAmount(couponAmount);
         }
         if (orderParam.getUseIntegration() == null) {
             order.setIntegration(0);
@@ -247,7 +262,11 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
             order.setIntegration(orderParam.getUseIntegration());
             order.setIntegrationAmount(calcIntegrationAmount(orderItemList));
         }
-        order.setPayAmount(calcPayAmount(order));
+        LOG.info("payAmount:{},totalAmount:{},couponAmount:{}",order.getTotalAmount().subtract(couponAmount),order.getTotalAmount(),couponAmount);
+        if (orderParam.getOrderAmount().compareTo(order.getTotalAmount().subtract(couponAmount)) != 0) {
+            throw new BizException("订单金额校验不通过");
+        }
+        order.setPayAmount(orderParam.getOrderAmount());
         //转化为订单信息并插入数据库
         order.setMemberId(currentMember.getId());
         order.setCreateTime(new Date());
@@ -292,7 +311,9 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
         orderItemDao.insertList(orderItemList);
         //如使用优惠券更新优惠券使用状态
         if (orderParam.getCouponId() != null) {
-            updateCouponStatus(orderParam.getCouponId(), currentMember.getId(), 1);
+            // updateCouponStatus(orderParam.getCouponId(), currentMember.getId(), 1);
+            // todo 远程调用更新优惠券使用
+            adminFeignService.updateCouponOrderInfo(orderParam.getCouponId(),false,order.getOrderSn());
         }
         //如使用积分需要扣除积分
         if (orderParam.getUseIntegration() != null) {
@@ -474,7 +495,10 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
                 portalOrderDao.releaseProductStockLock(orderItemList);
             }
             //修改优惠券使用状态
-            updateCouponStatus(cancelOrder.getCouponId(), cancelOrder.getMemberId(), 0);
+            // updateCouponStatus(cancelOrder.getCouponId(), cancelOrder.getMemberId(), 0);
+            // todo 退回优惠券
+
+            adminFeignService.updateCouponOrderInfo(cancelOrder.getCouponId(),true,cancelOrder.getOrderSn());
             //返还使用积分
             if (cancelOrder.getUseIntegration() != null) {
                 UmsMember member = memberService.getById(cancelOrder.getMemberId());
@@ -1259,22 +1283,22 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
      * @param orderItemList       order_item列表
      * @param couponHistoryDetail 可用优惠券详情
      */
-    private void handleCouponAmount(List<OmsOrderItem> orderItemList, SmsCouponHistoryDetail couponHistoryDetail) {
-        SmsCoupon coupon = couponHistoryDetail.getCoupon();
-        if (coupon.getUseType().equals(0)) {
+    private void handleCouponAmount(List<OmsOrderItem> orderItemList, BigDecimal couponAmount) {
+        // SmsCoupon coupon = couponHistoryDetail.getCoupon();
+        // if (coupon.getUseType().equals(0)) {
             //全场通用
-            calcPerCouponAmount(orderItemList, coupon);
-        } else if (coupon.getUseType().equals(1)) {
-            //指定分类
-            List<OmsOrderItem> couponOrderItemList = getCouponOrderItemByRelation(couponHistoryDetail, orderItemList,
-                                                                                  0);
-            calcPerCouponAmount(couponOrderItemList, coupon);
-        } else if (coupon.getUseType().equals(2)) {
-            //指定商品
-            List<OmsOrderItem> couponOrderItemList = getCouponOrderItemByRelation(couponHistoryDetail, orderItemList,
-                                                                                  1);
-            calcPerCouponAmount(couponOrderItemList, coupon);
-        }
+            calcPerCouponAmount(orderItemList, couponAmount);
+        // } else if (coupon.getUseType().equals(1)) {
+        //     //指定分类
+        //     List<OmsOrderItem> couponOrderItemList = getCouponOrderItemByRelation(couponHistoryDetail, orderItemList,
+        //                                                                           0);
+        //     calcPerCouponAmount(couponOrderItemList, coupon);
+        // } else if (coupon.getUseType().equals(2)) {
+        //     //指定商品
+        //     List<OmsOrderItem> couponOrderItemList = getCouponOrderItemByRelation(couponHistoryDetail, orderItemList,
+        //                                                                           1);
+        //     calcPerCouponAmount(couponOrderItemList, coupon);
+        // }
     }
 
     /**
@@ -1282,13 +1306,14 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
      *
      * @param orderItemList 可用优惠券的下单商品商品
      */
-    private void calcPerCouponAmount(List<OmsOrderItem> orderItemList, SmsCoupon coupon) {
+    private void calcPerCouponAmount(List<OmsOrderItem> orderItemList, BigDecimal coupon) {
+
         BigDecimal totalAmount = calcTotalAmount(orderItemList);
         for (OmsOrderItem orderItem : orderItemList) {
             //(商品价格/可用商品总价)*优惠券面额
             BigDecimal couponAmount = orderItem.getProductPrice()
-                                               .divide(totalAmount, 3, RoundingMode.HALF_EVEN)
-                                               .multiply(coupon.getAmount());
+                                               .divide(totalAmount, 3, BigDecimal.ROUND_DOWN)
+                                               .multiply(coupon);
             orderItem.setCouponAmount(couponAmount);
         }
     }

+ 1 - 1
cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/service/impl/PmsPortalProductServiceImpl.java

@@ -217,7 +217,7 @@ public class PmsPortalProductServiceImpl implements PmsPortalProductService {
 
     private  List<PmsProductAttributeCategory>  getHomeProductAttributeCategory() {
         PmsProductAttributeCategoryExample example = new PmsProductAttributeCategoryExample();
-        example.setOrderByClause("id desc");
+        example.setOrderByClause("sort desc,id desc");
         List<PmsProductAttributeCategory> pmsProductCategories = productAttributeCategoryMapper.selectByExample(example);
         if (CollectionUtils.isEmpty(pmsProductCategories)) {
             return new ArrayList<>();

+ 1 - 1
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/AdminApplication.java

@@ -16,7 +16,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
 @SpringBootApplication
 @EnableDiscoveryClient
 @EnableFeignClients("com.yonge.cooleshow")
-@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.toolset.payment.core.dao"})
+@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.cooleshow.biz.dal.mapper", "com.yonge.toolset.payment.core.dao"})
 @ComponentScan(basePackages = {"com.yonge.cooleshow", "com.yonge.toolset"})
 @Configuration
 @EnableSwagger2Doc

+ 236 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/coupon/CouponInfoController.java

@@ -0,0 +1,236 @@
+package com.yonge.cooleshow.admin.controller.coupon;
+
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.admin.io.request.coupon.CouponInfoVO;
+import com.yonge.cooleshow.admin.io.request.coupon.CouponInventoryVO;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.CouponInfo;
+import com.yonge.cooleshow.biz.dal.entity.CouponInventory;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInventoryQuery;
+import com.yonge.cooleshow.biz.dal.service.CouponInfoService;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponInventoryWrapper;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import com.yonge.cooleshow.common.enums.EStatus;
+import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.base.page.PageInfo;
+import com.yonge.toolset.mybatis.support.PageUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+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 javax.validation.Valid;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * <p>
+ * 优惠券信息 前端控制器
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@RestController
+@RequestMapping("/couponInfo")
+@Api(value = "优惠券信息", tags = "优惠券信息")
+public class CouponInfoController extends BaseController {
+
+    @Autowired
+    private CouponInfoService couponInfoService;
+    @Autowired
+    private SysUserFeignService sysUserFeignService;
+
+    /**
+     * 优惠券信息
+     * @param request CouponInfoVO.RequestInfo
+     * @return HttpResponseResult<PageInfo<CouponInfoVO.ResponseInfo>>
+     */
+    @PostMapping(value = "/page", consumes="application/json", produces="application/json")
+    @ApiOperation(value = "查询优惠券分页", notes = "CouponInfoVO.PageRequest")
+    public HttpResponseResult<PageInfo<CouponInfoVO.CouponPageInfo>> queryCouponPageInfo(@RequestBody CouponInfoVO.PageRequest request) {
+
+        // 优惠券信息
+        IPage<CouponInfoWrapper> wrapper = couponInfoService.queryCouponPageInfo(PageUtil.getPage(request),
+                CouponInfoQuery.from(request.jsonString()));
+
+        // 数据转换
+        List<CouponInfoVO.CouponPageInfo> pageInfos = JSON.parseArray(JSON.toJSONString(wrapper.getRecords()),
+                CouponInfoVO.CouponPageInfo.class);
+
+        // 分页数据信息
+        return succeed(PageUtil.getPageInfo(wrapper, pageInfos));
+    }
+
+    /**
+     * 优惠券详情信息
+     * @param id 优惠券ID
+     * @return HttpResponseResult<CouponInfoVO.CouponQueryInfo>
+     */
+    @GetMapping("/detail/{id}")
+    @ApiOperation(value = "优惠券详情", notes = "传入优惠券ID")
+    public HttpResponseResult<CouponInfoVO.CouponQueryInfo> findCouponById(@PathVariable("id") Long id) {
+
+        if (Objects.isNull(id)) {
+            return failed("无效的优惠券ID");
+        }
+
+        // 优惠券信息
+        CouponInfo couponInfo = couponInfoService.queryCouponInfoById(id);
+
+        return succeed(CouponInfoVO.CouponQueryInfo.from(JSON.toJSONString(couponInfo)));
+    }
+
+    /**
+     * 新增或者更新优惠券信息
+     * - ID为空新增;否则进行更新
+     * @param info CouponInfoVO.CouponInfo
+     * @return HttpResponseResult<CouponInfoVO.CouponInfo>
+     */
+    @PostMapping(value = "/save", consumes="application/json", produces="application/json")
+    @ApiOperation(value = "新增或修改", notes = "CouponInfoVO.CouponInfo")
+    public HttpResponseResult<CouponInfoVO.CouponQueryInfo> saveOrUpdate(@Valid @RequestBody CouponInfoVO.CouponInfo info) {
+
+        // 获取当前登录用户信息
+        SysUser user = sysUserFeignService.queryUserInfo();
+        if (Objects.isNull(user) || Objects.isNull(user.getId())) {
+            return failed(HttpStatus.FORBIDDEN, "请登录");
+        }
+
+        // 校验参数合法性
+        if (info.invalidRequestParam(user.getId())) {
+            throw new BizException("无效的请求参数");
+        }
+
+        // 新增或更新信息
+        CouponInfo couponInfo = couponInfoService.saveOrUpdateCouponInfo(JSON.parseObject(info.jsonString(), CouponInfo.class), false);
+
+        return succeed(CouponInfoVO.CouponQueryInfo.from(JSON.toJSONString(couponInfo)));
+    }
+
+    /**
+     * 启用/停用 优惠券
+     */
+    @PostMapping("/updateState")
+    @ApiOperation(value = "优惠券状态启/停用")
+    public HttpResponseResult<Boolean> updateCouponStateInfo(@RequestBody CouponInfoVO.CouponInfo request) {
+
+        // 获取当前登录用户信息
+        SysUser user = sysUserFeignService.queryUserInfo();
+        if (Objects.isNull(user) || Objects.isNull(user.getId())) {
+            return failed(HttpStatus.FORBIDDEN, "请登录");
+        }
+
+        if (Objects.isNull(request)
+                || Objects.isNull(request.getId())
+                || EStatus.invalid(request.getStatus())) {
+            throw new BizException("无效请求参数");
+        }
+
+        // 更新数据信息
+        CouponInfoVO.CouponInfo info = CouponInfoVO.CouponInfo.builder()
+                .id(request.getId())
+                .status(request.getStatus())
+                .updatedBy(user.getId())
+                .updateTime(DateTime.now().getMillis())
+                .build();
+
+        CouponInfo couponInfo = couponInfoService.saveOrUpdateCouponInfo(JSON.parseObject(info.jsonString(), CouponInfo.class), true);
+
+        // 校验状态更新结果
+        return status(couponInfo.getStatus().intValue() == request.getStatus());
+    }
+
+    /**
+     * 优惠券库存量信息
+     * @param request CouponInventoryVO.PageRequest
+     * @return HttpResponseResult<PageInfo<CouponInventoryVO.PageInfo>>
+     */
+    @PostMapping(value = "/inventory/page", consumes="application/json", produces="application/json")
+    @ApiOperation(value = "查询库存量调整分页信息", notes = "CouponInventoryVO.PageRequest")
+    public HttpResponseResult<PageInfo<CouponInventoryVO.PageInfo>> queryCouponInventoryPageInfo(@RequestBody CouponInventoryVO.PageRequest request) {
+
+        // 校验请求参数
+        if (request.invalidRequestParam()) {
+            throw new BizException("无效的优惠券ID");
+        }
+
+        // 优惠券信息
+        IPage<CouponInventoryWrapper> wrapper = couponInfoService.queryCouponInventoryPageInfo(PageUtil.getPage(request),
+                CouponInventoryQuery.from(request.jsonString()));
+
+        // 数据转换
+        List<CouponInventoryVO.PageInfo> pageInfos = JSON.parseArray(JSON.toJSONString(wrapper.getRecords()),
+                CouponInventoryVO.PageInfo.class);
+
+        // 分页数据信息
+        return succeed(PageUtil.getPageInfo(wrapper, pageInfos));
+    }
+
+    /**
+     * 调整新优惠券库存量信息
+     * @param info CouponInfoVO.CouponInfo
+     * @return HttpResponseResult<CouponInfoVO.CouponInfo>
+     */
+    @PostMapping(value = "/inventory/update", consumes="application/json", produces="application/json")
+    @ApiOperation(value = "优惠券库存量更新", notes = "CouponInventoryVO.InventoryInfo")
+    public HttpResponseResult<Boolean> saveOrUpdate(@Valid @RequestBody CouponInventoryVO.InventoryInfo info) {
+
+        // 获取当前登录用户信息
+        SysUser user = sysUserFeignService.queryUserInfo();
+        if (Objects.isNull(user) || Objects.isNull(user.getId())) {
+            return failed(HttpStatus.FORBIDDEN, "请登录");
+        }
+
+        // 校验参数合法性
+        if (info.invalidRequestParam(user.getId())) {
+            throw new BizException("无效的请求参数");
+        }
+
+        // 新增或更新信息
+        int rows = couponInfoService.saveOrUpdateCouponInventoryInfo(JSON.parseObject(info.jsonString(),
+                CouponInventory.class));
+
+        return status(rows > 0);
+    }
+
+    /**
+     * 库存调整用户信息
+     * @param request CouponInventoryVO.PageRequest
+     * @return HttpResponseResult<PageInfo<CouponInventoryVO.PageInfo>>
+     */
+    @PostMapping(value = "/inventory/user", consumes="application/json", produces="application/json")
+    @ApiOperation(value = "查询库存调整用户信息分页", notes = "CouponInventoryVO.PageRequest")
+    public HttpResponseResult<PageInfo<CouponInventoryVO.PageUserInfo>> queryInventoryUserPageInfo(@RequestBody CouponInventoryVO.PageRequest request) {
+
+        // 校验请求参数
+        if (Objects.isNull(request) || Objects.isNull(request.getCouponId())) {
+            throw new BizException("无效的优惠券ID");
+        }
+
+        // 优惠券信息
+        IPage<CouponInventoryWrapper> wrapper = couponInfoService.queryCouponInventoryPageInfo(PageUtil.getPage(request),
+                CouponInventoryQuery.from(request.jsonString()).groupByUser(true));
+
+        // 数据转换
+        List<CouponInventoryVO.PageUserInfo> pageInfos = JSON.parseArray(JSON.toJSONString(wrapper.getRecords()),
+                CouponInventoryVO.PageUserInfo.class);
+
+        // 分页数据信息
+        return succeed(PageUtil.getPageInfo(wrapper, pageInfos));
+    }
+
+}

+ 139 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/coupon/CouponIssueController.java

@@ -0,0 +1,139 @@
+package com.yonge.cooleshow.admin.controller.coupon;
+
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.admin.io.request.coupon.CouponInfoVO;
+import com.yonge.cooleshow.admin.io.request.coupon.CouponIssueVo;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.CouponIssue;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponIssueQueryInfo;
+import com.yonge.cooleshow.biz.dal.service.CouponIssueService;
+import com.yonge.cooleshow.biz.dal.vo.CouponIssueUserVo;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponIssueWrapper;
+import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import com.yonge.toolset.base.page.PageInfo;
+import com.yonge.toolset.mybatis.support.PageUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.time.Instant;
+import java.util.List;
+
+import static com.yonge.cooleshow.common.entity.HttpResponseResult.failed;
+import static com.yonge.cooleshow.common.entity.HttpResponseResult.succeed;
+
+/**
+ * <p>
+ * 优惠券发放 前端控制器
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@RestController
+@RequestMapping("/couponIssue")
+@Api(value = "优惠券发放信息", tags = "优惠券发放信息")
+@Validated
+public class CouponIssueController {
+
+    @Autowired
+    private CouponIssueService couponIssueService;
+
+    @Autowired
+    private SysUserFeignService sysUserFeignService;
+
+    /**
+     * 优惠券发放分页信息
+     */
+    @PostMapping("/page")
+    @ApiOperation(value = "查询优惠券发放分页")
+    public HttpResponseResult<PageInfo<CouponIssueVo.CouponIssuePageInfo>> queryCouponIssuePageInfo(@RequestBody @Valid CouponIssueVo.PageRequest request) {
+
+        if (request.getCouponId() == null) {
+            return failed("优惠券id不能为空");
+        }
+        IPage<CouponIssueWrapper> couponIssueWrapperIPage = couponIssueService.queryCouponIssueInfo(
+                PageUtil.getPage(request), CouponIssueQueryInfo.from(request.jsonString()));
+
+
+        // 数据转换
+        List<CouponIssueVo.CouponIssuePageInfo> pageInfos = JSON.parseArray(JSON.toJSONString(couponIssueWrapperIPage.getRecords()),
+                                                                    CouponIssueVo.CouponIssuePageInfo.class);
+
+        return succeed(PageUtil.getPageInfo(couponIssueWrapperIPage,pageInfos));
+    }
+
+
+    /**
+     * 优惠券课发放用户分页信息
+     */
+    @PostMapping("/userPage")
+    @ApiOperation(value = "优惠券发放用户分页信息")
+    public HttpResponseResult<PageInfo<CouponIssueUserVo>> queryCouponIssueUserPageInfo(@RequestBody @Valid CouponIssueVo.CouponIssueUserRequest request) {
+
+        IPage<CouponIssueUserVo> couponIssueUserVoIPage = couponIssueService.queryUser(PageUtil.getPage(request),
+                                                                                       request.getCouponId(),
+                                                                                       request.getClient(),
+                                                                                       request.getKeyword());
+
+
+        return succeed(PageUtil.pageInfo(couponIssueUserVoIPage));
+    }
+
+
+    @PostMapping("/issueUser")
+    @ApiOperation(value = "发送优惠券")
+    public HttpResponseResult<Boolean> issueCoupon(@RequestBody @Valid CouponIssueVo.CouponIssueUserParam param) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        if (sysUser == null  || sysUser.getId() == null) {
+            return failed("用户信息获取失败");
+        }
+        couponIssueService.issueCoupon(param.getCouponId(),param.getUserParam(),sysUser.getId(),param.getRemark());
+
+        return succeed();
+    }
+
+    @PostMapping("/withdraw/{couponIssueId}")
+    @ApiOperation(value = "撤回优惠券")
+    public HttpResponseResult<Boolean> withdrawCoupon(@PathVariable Long couponIssueId) {
+        CouponIssue couponIssue = couponIssueService.getById(couponIssueId);
+        if (couponIssue == null) {
+            return failed("未找到数据");
+        }
+        if (couponIssue.getUseState().equals(CouponUseStateEnum.USED)) {
+            return failed("优惠券已被使用");
+        }  else if(couponIssue.getEndTime().compareTo(DateTime.now().getMillis()) <0) {
+            return failed("优惠券已过期");
+        }
+        return HttpResponseResult.status(couponIssueService.withdrawCoupon(couponIssueId));
+    }
+
+
+    /**
+     * 优惠券发放分页信息
+     */
+    @PostMapping("/user/page")
+    @ApiOperation(value = "查询用户(老师/学生)优惠券发放分页")
+    public HttpResponseResult<PageInfo<CouponIssueVo.UserCouponIssuePageInfo>> queryCouponIssuePageInfo(@RequestBody @Valid CouponIssueVo.UserCouponIssuePageRequest request) {
+
+        IPage<CouponIssueWrapper> couponIssueWrapperIPage = couponIssueService.queryCouponIssueInfo(
+                PageUtil.getPage(request), CouponIssueQueryInfo.from(request.jsonString()));
+
+
+        // 数据转换
+        List<CouponIssueVo.UserCouponIssuePageInfo> pageInfos = JSON.parseArray(JSON.toJSONString(couponIssueWrapperIPage.getRecords()),
+                                                                            CouponIssueVo.UserCouponIssuePageInfo.class);
+
+        return succeed(PageUtil.getPageInfo(couponIssueWrapperIPage,pageInfos));
+    }
+
+}

+ 69 - 8
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/AdminClient.java

@@ -1,5 +1,8 @@
 package com.yonge.cooleshow.admin.controller.open;
 
+import com.alibaba.fastjson.JSON;
+import com.yonge.cooleshow.admin.io.request.coupon.CouponOrderVO;
+import com.yonge.cooleshow.api.feign.dto.CouponInfoApi;
 import com.yonge.cooleshow.api.feign.dto.EmployeeApi;
 import com.yonge.cooleshow.api.feign.dto.StudentApi;
 import com.yonge.cooleshow.api.feign.dto.TeacherApi;
@@ -7,23 +10,28 @@ import com.yonge.cooleshow.biz.dal.entity.Employee;
 import com.yonge.cooleshow.biz.dal.entity.Student;
 import com.yonge.cooleshow.biz.dal.entity.Teacher;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
-import com.yonge.cooleshow.biz.dal.service.EmployeeService;
-import com.yonge.cooleshow.biz.dal.service.StudentService;
-import com.yonge.cooleshow.biz.dal.service.TeacherService;
-import com.yonge.cooleshow.biz.dal.service.UserFirstTimeService;
+import com.yonge.cooleshow.biz.dal.enums.GoodTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.service.*;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponOrderWrapper;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.ContractDto;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.cooleshow.common.enums.UserFirstTimeTypeEnum;
 import io.swagger.annotations.ApiOperation;
+import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
 
 @RestController
 @RequestMapping("/open/adminClient")
@@ -42,6 +50,9 @@ public class AdminClient extends BaseController {
     @Autowired
     private EmployeeService employeeService;
 
+    @Autowired
+    private CouponInfoService couponInfoService;
+
     @GetMapping("/recordTime")
     public HttpResponseResult<Boolean> recordTime(
             @RequestParam("userId") Long userId,
@@ -80,4 +91,54 @@ public class AdminClient extends BaseController {
         return succeed(employeeApi);
     }
 
+    /**
+     * 订单优惠券信息
+     * @return HttpResponseResult<CouponOrderVO.CouponPageInfo>
+     */
+    @PostMapping(value = "/orderInfo", consumes="application/json", produces="application/json")
+    @ApiOperation(value = "查询订单优惠券信息", notes = "CouponOrderVO.PageRequest")
+    public HttpResponseResult<CouponInfoApi> queryCouponOrderPageInfo(@RequestParam Long userId, @RequestParam String issueIds, @RequestParam BigDecimal orderAmount, @RequestParam ClientEnum client) {
+
+        List<Long> collect = Arrays.stream(issueIds.split(","))
+                                   .map(Long::valueOf)
+                                   .collect(Collectors.toList());
+
+        CouponOrderVO.PageRequest request = new CouponOrderVO.PageRequest();
+        request.setClientType(client);
+        request.setCouponTypes(CouponCategoryEnum.getCategory("MALL"));
+        request.setIssueIds(collect);
+        request.setUseState(CouponUseStateEnum.USABLE);
+        request.setTimestamp(DateTime.now().getMillis());
+        request.setAmount(orderAmount.doubleValue());
+
+        // 订单优惠券信息
+        CouponOrderWrapper wrapper = couponInfoService.queryUserOrderCouponInfo(request.getUserId(),
+                                                                                CouponInfoQuery.CouponOrderQuery.from(request.jsonString()));
+        CouponInfoApi couponInfoApi = new CouponInfoApi();
+        couponInfoApi.setDiscountedPrices(BigDecimal.valueOf(wrapper.getDiscountedPrices()));
+
+        return succeed(couponInfoApi);
+    }
+
+    /**
+     * 更新订单优惠券信息
+     * @return HttpResponseResult<Boolean>
+     */
+    @PostMapping(value = "/orderUpdate", consumes="application/json", produces="application/json")
+    @ApiOperation(value = "更新订单优惠券信息")
+    public HttpResponseResult<Boolean> updateCouponOrderInfo(@RequestParam String couponIssueId,@RequestParam Boolean returnCoupon,@RequestParam String orderNo) {
+
+        CouponOrderVO.CouponUpdateRequest  request = new CouponOrderVO.CouponUpdateRequest();
+        request.setOrderNo(orderNo);
+        request.setReset(returnCoupon);
+        request.setUseType(CouponCategoryEnum.MALL);
+        List<Long> collect = Arrays.stream(couponIssueId.split(","))
+                                   .map(Long::valueOf)
+                                   .collect(Collectors.toList());
+        request.setIssueIds(collect);
+        // 更新受影响优惠券数据
+        int ret = couponInfoService.updateCouponOrderInfo(CouponOrderWrapper.from(request.jsonString()));
+
+        return succeed(ret > 0);
+    }
 }

+ 430 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/coupon/CouponInfoVO.java

@@ -0,0 +1,430 @@
+package com.yonge.cooleshow.admin.io.request.coupon;
+
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.MK;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponValidTypeEnum;
+import com.yonge.cooleshow.common.enums.EStatus;
+import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.base.page.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * 优惠券信息
+ * Created by Eric.Shang on 2022/9/2.
+ */
+public class CouponInfoVO {
+
+    /**
+     * 优惠券分页请求信息
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("优惠券信息分页请求信息")
+    public static class PageRequest extends QueryInfo {
+
+        @ApiModelProperty(value = "优惠券ID", hidden = true)
+        private Long couponId;
+
+        @ApiModelProperty("优惠券名称")
+        private String name;
+
+        @ApiModelProperty("客户类型")
+        private ClientEnum clientType;
+
+        @ApiModelProperty("优惠券类型")
+        private CouponTypeEnum couponType;
+
+        @ApiModelProperty("可用品类")
+        private CouponCategoryEnum category;
+
+        @ApiModelProperty("优惠券状态")
+        private Integer status;
+
+        @ApiModelProperty(value = "当前时间", hidden = true)
+        private Long timestamp;
+
+        @ApiModelProperty(value = "优惠券时间类型", hidden = true)
+        private CouponValidTypeEnum validType;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public String getName() {
+
+            return Optional.ofNullable(this.name)
+                    .filter(x -> StringUtils.isNotEmpty(x) && !x.matches(MK.EXP_INT)).orElse(null);
+        }
+
+        public Long getCouponId() {
+            if (StringUtils.isNotEmpty(this.name) && this.name.matches(MK.EXP_INT)) {
+                return Long.parseLong(this.name);
+            }
+            return couponId;
+        }
+
+        public void setStatus(Integer status) {
+
+            if (EStatus.ENABLE.match(status)) {
+                this.timestamp(DateTime.now().getMillis()).setValidType(CouponValidTypeEnum.DAY);
+            }
+            this.status = status;
+        }
+
+        public PageRequest timestamp(Long timestamp) {
+            this.timestamp = timestamp;
+            return this;
+        }
+    }
+
+    /**
+     * 优惠券分页响应数据
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("优惠券信息分页响应信息")
+    public static class CouponPageInfo implements Serializable {
+
+        @ApiModelProperty("优惠券ID")
+        private Long id;
+
+        @ApiModelProperty("优惠券名称")
+        private String name;
+
+        @ApiModelProperty("描述")
+        private String describe;
+
+        @ApiModelProperty("客户端类型: TEACHER(老师端), STUDENT(学生端)")
+        private ClientEnum clientType;
+
+        @ApiModelProperty("可用品类: UNIVERSAL(全场通用) VIP(小酷Ai) PIANO(云酷琴房) MALL(商场购物券) MUSIC(单曲点播券) SPARRING(陪练课购买券) LIVE(直播课购买券) VIDEO(视频课购买券) ")
+        private CouponCategoryEnum category;
+
+        @ApiModelProperty("优惠券类型: FULL_DISCOUNT(满减券) VOUCHER(代金券) ")
+        private CouponTypeEnum couponType;
+
+        @ApiModelProperty("当前库存量")
+        private Integer inventory;
+
+        @ApiModelProperty("领取次数限制")
+        private Integer quantityLimit;
+
+        @ApiModelProperty("状态")
+        private Integer status;
+
+        @ApiModelProperty("发放领取数")
+        private Integer issueNum;
+
+        @ApiModelProperty("关联奖品数")
+        private Integer rewardNum;
+
+        @ApiModelProperty("最后更新人")
+        private String updatedUser;
+
+        @ApiModelProperty(value = "最后更新时间", hidden = true)
+        private Long updateTime;
+
+        @ApiModelProperty("最后更新时间")
+        @JsonFormat(pattern = MK.TIME_PATTERN, timezone = MK.TIME_ZONE)
+        private Date updatedTime;
+
+        @ApiModelProperty("创建时间")
+        @JsonFormat(pattern = MK.TIME_PATTERN, timezone = MK.TIME_ZONE)
+        private Date createdTime;
+
+        public Date getUpdatedTime() {
+
+            if (Optional.ofNullable(getUpdateTime()).orElse(0L) > 0) {
+                return new DateTime(getUpdateTime()).toDate();
+            }
+            return updatedTime;
+        }
+    }
+
+    /**
+     * 优惠券详情信息
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponQueryInfo implements Serializable {
+
+        @ApiModelProperty("优惠券ID")
+        private Long id;
+
+        @ApiModelProperty("优惠券名称")
+        private String name;
+
+        @ApiModelProperty("描述")
+        private String describe;
+
+        @ApiModelProperty("客户端类型: TEACHER(老师端), STUDENT(学生端)")
+        private ClientEnum clientType;
+
+        @ApiModelProperty("可用品类: UNIVERSAL(全场通用) VIP(小酷Ai) PIANO(云酷琴房) MALL(商场购物券) MUSIC(单曲点播券) SPARRING(陪练课购买券) LIVE(直播课购买券) VIDEO(视频课购买券) ")
+        private CouponCategoryEnum category;
+
+        @ApiModelProperty("使用门槛")
+        private BigDecimal useLimit;
+
+        @ApiModelProperty("优惠金额")
+        private BigDecimal discountPrice;
+
+        @ApiModelProperty("优惠券类型")
+        private CouponTypeEnum couponType;
+
+        @ApiModelProperty("有效期类型: DAY(固定有效天数) TIME_PERIOD(固定时间段)")
+        private CouponValidTypeEnum validType;
+
+        @ApiModelProperty("有效天数")
+        private Integer validDay;
+
+        @ApiModelProperty("生效时间")
+        private Long startTime;
+
+        @ApiModelProperty("失效时间")
+        private Long endTime;
+
+        @ApiModelProperty("库存量")
+        private Integer inventory;
+
+        @ApiModelProperty("领取次数限制")
+        private Integer quantityLimit;
+
+        @ApiModelProperty("启禁状态")
+        private Integer status;
+
+        @ApiModelProperty("更新用户")
+        private Long updatedBy;
+
+        @ApiModelProperty("更新时间")
+        private Long updateTime;
+
+        @ApiModelProperty("创建用户")
+        private Long createdBy;
+
+        @ApiModelProperty("创建时间")
+        @JsonFormat(pattern = MK.TIME_PATTERN, timezone = MK.TIME_ZONE)
+        private Date createdTime;
+
+        public static CouponQueryInfo from(String recv) {
+            return JSON.parseObject(recv, CouponQueryInfo.class);
+        }
+    }
+
+    /**
+     * 新增或更新优惠券信息
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponInfo implements Serializable {
+
+        @ApiModelProperty("优惠券ID")
+        private Long id;
+
+        @ApiModelProperty("优惠券名称")
+        private String name;
+
+        @ApiModelProperty("描述")
+        private String describe;
+
+        @ApiModelProperty("客户端类型: TEACHER(老师端), STUDENT(学生端)")
+        private ClientEnum clientType;
+
+        @ApiModelProperty("可用品类: UNIVERSAL(全场通用) VIP(小酷Ai) PIANO(云酷琴房) MALL(商场购物券) MUSIC(单曲点播券) SPARRING(陪练课购买券) LIVE(直播课购买券) VIDEO(视频课购买券) ")
+        private CouponCategoryEnum category;
+
+        @ApiModelProperty("使用门槛")
+        private BigDecimal useLimit;
+
+        @ApiModelProperty("优惠金额")
+        private BigDecimal discountPrice;
+
+        @ApiModelProperty("优惠券类型: FULL_DISCOUNT(满减券) VOUCHER(代金券) ")
+        private CouponTypeEnum couponType;
+
+        @ApiModelProperty("有效期类型: DAY(固定有效天数) TIME_PERIOD(固定时间段)")
+        private CouponValidTypeEnum validType;
+
+        @ApiModelProperty("有效天数")
+        private Integer validDay;
+
+        @ApiModelProperty("生效时间")
+        private Long startTime;
+
+        @ApiModelProperty("失效时间")
+        private Long endTime;
+
+        @ApiModelProperty("库存量")
+        private Integer inventory;
+
+        @ApiModelProperty("领取次数限制")
+        private Integer quantityLimit;
+
+        @ApiModelProperty(value = "启禁状态, 0 停用 1 启用")
+        private Integer status;
+
+        @ApiModelProperty(value = "更新用户", hidden = true)
+        private Long updatedBy;
+
+        @ApiModelProperty(value = "更新时间", hidden = true)
+        private Long updateTime;
+
+        @ApiModelProperty(value = "创建用户", hidden = true)
+        private Long createdBy;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public boolean invalidRequestParam(Long userId) {
+
+            if (Objects.isNull(getId())) {
+
+                // 新增参数
+                if (StringUtils.isEmpty(getName())) {
+                    throw new BizException("优惠券名称不能为空");
+                }
+
+                if (Objects.isNull(getClientType())) {
+                    throw new BizException("客户端不能为空");
+                }
+
+                if (Objects.isNull(getCategory())) {
+                    throw new BizException("可用品类不能为空");
+                }
+
+                if (Objects.isNull(getCouponType())) {
+                    throw new BizException("优惠券类型不能为空");
+                }
+
+                /*if (CouponTypeEnum.FULL_DISCOUNT == getCouponType()
+                        && Optional.ofNullable(getUseLimit()).map(BigDecimal::intValue).orElse(0) <= 0) {
+                    throw new BizException("满减金额未设置");
+                }*/
+
+                if (Objects.isNull(getValidType())) {
+                    throw new BizException("有效期类型不能为空");
+                }
+
+                if (Optional.ofNullable(getValidDay()).orElse(0) <= 0
+                        && (Optional.ofNullable(getStartTime()).orElse(0L) == 0
+                            || Optional.ofNullable(getEndTime()).orElse(0L) == 0)) {
+                    throw new BizException("优惠券时间有效期不合法");
+                }
+
+                this.createdBy(userId).initSaveParam();
+            }
+            // 更新用户参数
+            this.updatedBy(userId).setUpdateTime(DateTime.now().getMillis());
+
+            return false;
+        }
+
+        public CouponInfo initSaveParam() {
+
+            return this.describe("")
+                    .useLimit(BigDecimal.ZERO)
+                    .discountPrice(BigDecimal.ZERO)
+                    .validDay(0)
+                    .startTime(0L)
+                    .endTime(0L)
+                    .inventory(0)
+                    .quantityLimit(0)
+                    .status(EStatus.DISABLE.getValue());
+        }
+
+        public CouponInfo describe(String describe) {
+            if (StringUtils.isEmpty(this.describe)) {
+                this.describe = describe;
+            }
+            return this;
+        }
+
+        public CouponInfo useLimit(BigDecimal useLimit) {
+            if (Objects.isNull(this.useLimit)) {
+                this.useLimit = useLimit;
+            }
+            return this;
+        }
+
+        public CouponInfo discountPrice(BigDecimal discountPrice) {
+            if (Objects.isNull(this.discountPrice)) {
+                this.discountPrice = discountPrice;
+            }
+            return this;
+        }
+
+        public CouponInfo validDay(Integer validDay) {
+            if (Objects.isNull(this.validDay)) {
+                this.validDay = validDay;
+            }
+            return this;
+        }
+
+        public CouponInfo startTime(Long startTime) {
+            if (Objects.isNull(this.startTime)) {
+                this.startTime = startTime;
+            }
+            return this;
+        }
+
+        public CouponInfo endTime(Long endTime) {
+            if (Objects.isNull(this.endTime)) {
+                this.endTime = endTime;
+            }
+            return this;
+        }
+
+        public CouponInfo inventory(Integer inventory) {
+            if (Objects.isNull(this.inventory)) {
+                this.inventory = inventory;
+            }
+            return this;
+        }
+
+        public CouponInfo quantityLimit(Integer quantityLimit) {
+            if (Objects.isNull(this.quantityLimit)) {
+                this.quantityLimit = quantityLimit;
+            }
+            return this;
+        }
+
+        public CouponInfo status(Integer status) {
+            this.status = status;
+            return this;
+        }
+
+        public CouponInfo createdBy(Long createdBy) {
+            this.createdBy = createdBy;
+            return this;
+        }
+
+        public CouponInfo updatedBy(Long updatedBy) {
+            this.updatedBy = updatedBy;
+            return this;
+        }
+
+    }
+}

+ 243 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/coupon/CouponInventoryVO.java

@@ -0,0 +1,243 @@
+package com.yonge.cooleshow.admin.io.request.coupon;
+
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.yonge.cooleshow.biz.dal.enums.MK;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponInventoryEnum;
+import com.yonge.cooleshow.common.enums.EStatus;
+import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.base.page.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * 优惠券库存量
+ * Created by Eric.Shang on 2022/9/5.
+ */
+public class CouponInventoryVO {
+
+
+    /**
+     * 优惠券库存调整分页请求信息
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(value = "优惠券库存调整分页请求", description = "优惠券库存调整分页")
+    public static class PageRequest extends QueryInfo {
+
+        @ApiModelProperty("优惠券ID")
+        private Long couponId;
+
+        @ApiModelProperty("操作人")
+        private Long userId;
+
+        @ApiModelProperty("用户昵称或名称匹配")
+        private String keyword;
+
+        @ApiModelProperty("数据类型: ADDITION(添加) REDUCE(减少)")
+        private CouponInventoryEnum dataType;
+
+        @ApiModelProperty("开始时间, yyyy-MM-dd HH:mm:ss")
+        @DateTimeFormat(pattern = MK.TIME_PATTERN)
+        @JsonFormat(pattern = MK.TIME_PATTERN, timezone = MK.TIME_ZONE)
+        private Date startQueryTime;
+
+        @ApiModelProperty("结束时间, yyyy-MM-dd HH:mm:ss")
+        @DateTimeFormat(pattern = MK.TIME_PATTERN)
+        @JsonFormat(pattern = MK.TIME_PATTERN, timezone = MK.TIME_ZONE)
+        private Date endQueryTime;
+
+        @ApiModelProperty(value = "结束时间", hidden = true)
+        private Long startTime;
+        @ApiModelProperty(value = "结束时间", hidden = true)
+        private Long endTime;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public boolean invalidRequestParam() {
+
+            // 优惠券ID不能为空
+            return Optional.ofNullable(getCouponId()).orElse(0L) <= 0;
+        }
+
+        public Long getUserId() {
+
+            if (Optional.ofNullable(this.userId).orElse(0L) > 0) {
+                return this.userId;
+            }
+
+            return null;
+        }
+
+        public Long getStartTime() {
+            if (Objects.nonNull(getStartQueryTime())) {
+                return new DateTime(getStartQueryTime()).getMillis() / 1000;
+            }
+            return startTime;
+        }
+
+        public Long getEndTime() {
+            if (Objects.nonNull(getEndQueryTime())) {
+                return new DateTime(getEndQueryTime()).getMillis() / 1000;
+            }
+            return endTime;
+        }
+
+        public String getKeyword() {
+            if (StringUtils.isNotEmpty(this.keyword)) {
+                return keyword;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * 库存量调整分页数据
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class PageInfo implements Serializable {
+
+        @ApiModelProperty("发放ID")
+        private Long id;
+
+        @ApiModelProperty("用户ID")
+        private Long userId;
+
+        @ApiModelProperty("操作人")
+        private String username;
+
+        @ApiModelProperty("优惠券ID")
+        private Long couponId;
+
+        @ApiModelProperty("数据类型: ADDITION(添加) REDUCE(减少)")
+        private CouponInventoryEnum dataType;
+
+        @ApiModelProperty("变化量")
+        private Integer number;
+
+        @ApiModelProperty("备注")
+        private String remark;
+
+        @ApiModelProperty("创建时间")
+        @JsonFormat(pattern = MK.TIME_PATTERN, timezone = MK.TIME_ZONE)
+        private Date createdTime;
+
+    }
+
+    /**
+     * 库存量调整记录
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class InventoryInfo implements Serializable {
+
+        @ApiModelProperty(value = "发放ID", hidden = true)
+        private Long id;
+
+        @ApiModelProperty(value = "用户ID", hidden = true)
+        private Long userId;
+
+        @ApiModelProperty("优惠券ID")
+        private Long couponId;
+
+        @ApiModelProperty("数据类型: ADDITION(添加) REDUCE(减少)")
+        private CouponInventoryEnum dataType;
+
+        @ApiModelProperty("变化量")
+        private Integer number;
+
+        @ApiModelProperty(value = "备注", hidden = true)
+        private String remark;
+
+        @ApiModelProperty(value = "启/禁状态", hidden = true)
+        private Integer status;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public boolean invalidRequestParam(Long userId) {
+
+            if (Optional.ofNullable(getCouponId()).orElse(0L) <= 0) {
+                throw new BizException("优惠券ID不能为空");
+            }
+
+            if (Objects.isNull(getDataType())) {
+                throw new BizException("类型不能为空");
+            }
+
+            if (Optional.ofNullable(getNumber()).orElse(0) <= 0) {
+                throw new BizException("变化量不能为空");
+            }
+
+            // 设置保存参数
+            this.userId(userId).initSaveParam();
+
+            return false;
+        }
+
+        public InventoryInfo initSaveParam() {
+
+            return this.remark("").status(EStatus.ENABLE.getValue());
+        }
+
+
+        public InventoryInfo id(Long id) {
+            this.id = id;
+            return this;
+        }
+
+        public InventoryInfo userId(Long userId) {
+            this.userId = userId;
+            return this;
+        }
+
+        public InventoryInfo remark(String remark) {
+            this.remark = remark;
+            return this;
+        }
+
+        public InventoryInfo status(Integer status) {
+            this.status = status;
+            return this;
+        }
+    }
+
+    /**
+     * 库存量调整用户分页数据
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class PageUserInfo implements Serializable {
+
+        @ApiModelProperty("用户ID")
+        private Long userId;
+
+        @ApiModelProperty("操作人")
+        private String username;
+
+        @ApiModelProperty("优惠券ID")
+        private Long couponId;
+
+    }
+}

+ 755 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/coupon/CouponIssueVo.java

@@ -0,0 +1,755 @@
+package com.yonge.cooleshow.admin.io.request.coupon;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.yonge.cooleshow.biz.dal.dto.UserParam;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.SendTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.toolset.base.page.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.joda.time.DateTime;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2022-09-02
+ */
+public class CouponIssueVo {
+
+    @ApiModel("优惠券发放信息分页请求")
+    public static class PageRequest extends QueryInfo {
+
+        @ApiModelProperty(value = "优惠券id",required = true)
+        private Long couponId;
+
+        // 真实姓名/昵称/手机号/编号
+        @ApiModelProperty("真实姓名/昵称/手机号/编号")
+        private String keyword;
+
+        @ApiModelProperty("用户id")
+        private Long userId;
+
+        // 客户端 TEACHER  STUDENT
+        @ApiModelProperty("客户端 TEACHER  STUDENT")
+        private ClientEnum clientType;
+
+        // 发放方式 PLATFORM AVTIVITY
+        @ApiModelProperty("发放方式 PLATFORM:后台发放 ACTIVITY:活动领取")
+        private SendTypeEnum issueWay;
+
+        //使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期
+        @ApiModelProperty("使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期 WITHDRAW 撤回")
+        private CouponUseStateEnum useState;
+
+        //  发放开始时间
+        @ApiModelProperty("发放开始时间")
+        private Long issueStartTime;
+
+        // 发放结束时间
+        @ApiModelProperty("发放结束时间")
+        private Long issueEndTime;
+
+        //  使用开始时间
+        @ApiModelProperty("使用开始时间")
+        private Long usedStartTime;
+
+        // 使用结束时间
+        @ApiModelProperty("使用结束时间")
+        private Long usedEndTime;
+
+        public Long getUserId() {
+            return userId;
+        }
+
+        public void setUserId(Long userId) {
+            this.userId = userId;
+        }
+
+        public Long getCouponId() {
+            return couponId;
+        }
+
+        public void setCouponId(Long couponId) {
+            this.couponId = couponId;
+        }
+
+        public String getKeyword() {
+            return keyword;
+        }
+
+        public void setKeyword(String keyword) {
+            this.keyword = keyword;
+        }
+
+        public ClientEnum getClientType() {
+            return clientType;
+        }
+
+        public void setClientType(ClientEnum clientType) {
+            this.clientType = clientType;
+        }
+
+        public SendTypeEnum getIssueWay() {
+            return issueWay;
+        }
+
+        public void setIssueWay(SendTypeEnum issueWay) {
+            this.issueWay = issueWay;
+        }
+
+        public CouponUseStateEnum getUseState() {
+            return useState;
+        }
+
+        public void setUseState(CouponUseStateEnum useState) {
+            this.useState = useState;
+        }
+
+        public Long getIssueStartTime() {
+            return issueStartTime;
+        }
+
+        public void setIssueStartTime(Long issueStartTime) {
+            this.issueStartTime = issueStartTime;
+        }
+
+        public Long getIssueEndTime() {
+            return issueEndTime;
+        }
+
+        public void setIssueEndTime(Long issueEndTime) {
+            this.issueEndTime = issueEndTime;
+        }
+
+        public Long getUsedStartTime() {
+            return usedStartTime;
+        }
+
+        public void setUsedStartTime(Long usedStartTime) {
+            this.usedStartTime = usedStartTime;
+        }
+
+        public Long getUsedEndTime() {
+            return usedEndTime;
+        }
+
+        public void setUsedEndTime(Long usedEndTime) {
+            this.usedEndTime = usedEndTime;
+        }
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+    }
+
+
+
+
+    @ApiModel("优惠券发放信息分页信息")
+    public static class CouponIssuePageInfo implements Serializable {
+
+        @ApiModelProperty("用户id")
+        private Long userId;
+
+        @ApiModelProperty("优惠券发放id")
+        private Long couponIssueId;
+
+        @ApiModelProperty("用户实名")
+        private String realName;
+
+        @ApiModelProperty("用户昵称")
+        private String username;
+
+        @ApiModelProperty("客户端类型 TEACHER :老师端 STUDENT:学生端")
+        private ClientEnum clientType;
+
+        @ApiModelProperty("使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期  WITHDRAW 撤回")
+        private CouponUseStateEnum useState;
+
+        @ApiModelProperty("订单号")
+        private String orderNo;
+
+        @ApiModelProperty("有效期开始时间")
+        @JSONField(format = "yyyy-MM-dd")
+        private Date startTime;
+
+        @ApiModelProperty("有效期结束时间")
+        @JSONField(format = "yyyy-MM-dd")
+        private Date endTime;
+
+        @ApiModelProperty("发放时间")
+        private Date issueTime;
+
+        @ApiModelProperty("使用时间")
+        private Date useTime;
+
+        @ApiModelProperty("发放途径PLATFORM:后台发放 ACTIVITY:活动领取")
+        private SendTypeEnum issueWay;
+
+        @ApiModelProperty("发放人昵称")
+        private String issueUsername;
+
+        @ApiModelProperty("发放人实名")
+        private String issueRealName;
+
+        @ApiModelProperty("备注")
+        private String remark;
+
+
+        @ApiModelProperty("用户手机号")
+        private String phone;
+
+        @ApiModelProperty("使用类型")
+        private CouponCategoryEnum useType;
+
+        public CouponCategoryEnum getUseType() {
+            return useType;
+        }
+
+        public void setUseType(CouponCategoryEnum useType) {
+            this.useType = useType;
+        }
+
+        public String getPhone() {
+            return phone;
+        }
+
+        public void setPhone(String phone) {
+            this.phone = phone;
+        }
+
+        public Long getUserId() {
+            return userId;
+        }
+
+        public void setUserId(Long userId) {
+            this.userId = userId;
+        }
+
+        public Long getCouponIssueId() {
+            return couponIssueId;
+        }
+
+        public void setCouponIssueId(Long couponIssueId) {
+            this.couponIssueId = couponIssueId;
+        }
+
+        public String getRealName() {
+            return realName;
+        }
+
+        public void setRealName(String realName) {
+            this.realName = realName;
+        }
+
+        public String getUsername() {
+            return username;
+        }
+
+        public void setUsername(String username) {
+            this.username = username;
+        }
+
+        public ClientEnum getClientType() {
+            return clientType;
+        }
+
+        public void setClientType(ClientEnum clientType) {
+            this.clientType = clientType;
+        }
+
+        public CouponUseStateEnum getUseState() {
+            return useState;
+        }
+
+        public void setUseState(CouponUseStateEnum useState) {
+            this.useState = useState;
+        }
+
+        public String getOrderNo() {
+            return orderNo;
+        }
+
+        public void setOrderNo(String orderNo) {
+            this.orderNo = orderNo;
+        }
+
+        public Date getStartTime() {
+            return startTime;
+        }
+
+        public void setStartTime(Date startTime) {
+            this.startTime = startTime;
+        }
+
+        public Date getEndTime() {
+            return endTime;
+        }
+
+        public void setEndTime(Date endTime) {
+            this.endTime = endTime;
+        }
+
+        public Date getIssueTime() {
+            return issueTime;
+        }
+
+        public void setIssueTime(Date issueTime) {
+            this.issueTime = issueTime;
+        }
+
+        public Date getUseTime() {
+            return useTime;
+        }
+
+        public void setUseTime(Date useTime) {
+            this.useTime = useTime;
+        }
+
+        public SendTypeEnum getIssueWay() {
+            return issueWay;
+        }
+
+        public void setIssueWay(SendTypeEnum issueWay) {
+            this.issueWay = issueWay;
+        }
+
+        public String getIssueUsername() {
+            return issueUsername;
+        }
+
+        public void setIssueUsername(String issueUsername) {
+            this.issueUsername = issueUsername;
+        }
+
+        public String getIssueRealName() {
+            return issueRealName;
+        }
+
+        public void setIssueRealName(String issueRealName) {
+            this.issueRealName = issueRealName;
+        }
+
+        public String getRemark() {
+            return remark;
+        }
+
+        public void setRemark(String remark) {
+            this.remark = remark;
+        }
+    }
+
+    @ApiModel("优惠券发放用户分页查询")
+    public static class CouponIssueUserRequest extends QueryInfo {
+
+        @ApiModelProperty(value = "优惠券id",required = true)
+        @NotNull(message = "优惠券id不能为空")
+        private Long couponId;
+
+        // 真实姓名/昵称/手机号/编号
+        @ApiModelProperty("真实姓名/昵称/手机号/编号")
+        private String keyword;
+
+        // 客户端 TEACHER  STUDENT
+        @ApiModelProperty("客户端 TEACHER  STUDENT")
+        private ClientEnum client;
+
+        public Long getCouponId() {
+            return couponId;
+        }
+
+        public void setCouponId(Long couponId) {
+            this.couponId = couponId;
+        }
+
+        public String getKeyword() {
+            return keyword;
+        }
+
+        public void setKeyword(String keyword) {
+            this.keyword = keyword;
+        }
+
+        public ClientEnum getClient() {
+            return client;
+        }
+
+        public void setClient(ClientEnum client) {
+            this.client = client;
+        }
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+    }
+
+    @ApiModel("发送优惠券")
+    public static class CouponIssueUserParam {
+
+        @ApiModelProperty(value = "优惠券id",required = true)
+        @NotNull(message = "优惠券id不能为空")
+        private Long couponId;
+
+        @ApiModelProperty("用户信息")
+        @Size(min = 1,message = "请选择发送优惠券用户")
+        private List<UserParam> userParam;
+
+
+        @ApiModelProperty(value = "发放原因",required = true)
+        private String remark;
+
+        public String getRemark() {
+            return remark;
+        }
+
+        public void setRemark(String remark) {
+            this.remark = remark;
+        }
+
+        public Long getCouponId() {
+            return couponId;
+        }
+
+        public void setCouponId(Long couponId) {
+            this.couponId = couponId;
+        }
+
+        public List<UserParam> getUserParam() {
+            return userParam;
+        }
+
+        public void setUserParam(List<UserParam> userParam) {
+            this.userParam = userParam;
+        }
+
+
+    }
+
+    @ApiModel("用户查询优惠券分页请求")
+    public static class UserCouponIssuePageRequest extends QueryInfo{
+
+
+        @ApiModelProperty("用户id")
+        @NotNull(message = "用户id不能为空")
+        private Long userId;
+
+
+        @ApiModelProperty("用户类型")
+        @NotNull(message = "用户类型不能为空")
+        private ClientEnum clientType;
+
+        @ApiModelProperty("优惠券编号/名称")
+        private String couponKeyword;
+
+        @ApiModelProperty("优惠券类型 ")
+        private CouponTypeEnum couponType;
+
+        @ApiModelProperty("可用品类 UNIVERSAL:全场通用 VIP:小酷Ai PIANO:云酷琴房 MALL:商场购物券 MUSIC:单曲点播券 SPARRING:陪练课购买券 LIVE:直播课购买券  VIDEO:视频课购买券")
+        private CouponCategoryEnum couponCategory;
+
+        @ApiModelProperty("获取途径 PLATFORM:平台,ACTIVITY:活动")
+        private SendTypeEnum issueWay;
+
+        @ApiModelProperty("使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期  WITHDRAW 撤回")
+        private CouponUseStateEnum useState;
+
+        @ApiModelProperty("领取开始时间")
+        private Date issueStartTime;
+
+        @ApiModelProperty("领取结束时间")
+        private Date issueEndTime;
+
+
+        public SendTypeEnum getIssueWay() {
+            return issueWay;
+        }
+
+        public void setIssueWay(SendTypeEnum issueWay) {
+            this.issueWay = issueWay;
+        }
+
+        public CouponUseStateEnum getUseState() {
+            return useState;
+        }
+
+        public void setUseState(CouponUseStateEnum useState) {
+            this.useState = useState;
+        }
+
+        public Long getUserId() {
+            return userId;
+        }
+
+        public void setUserId(Long userId) {
+            this.userId = userId;
+        }
+
+        public ClientEnum getClientType() {
+            return clientType;
+        }
+
+        public void setClientType(ClientEnum clientType) {
+            this.clientType = clientType;
+        }
+
+        public String getCouponKeyword() {
+            return couponKeyword;
+        }
+
+        public void setCouponKeyword(String couponKeyword) {
+            this.couponKeyword = couponKeyword;
+        }
+
+        public CouponTypeEnum getCouponType() {
+            return couponType;
+        }
+
+        public void setCouponType(CouponTypeEnum couponType) {
+            this.couponType = couponType;
+        }
+
+        public CouponCategoryEnum getCouponCategory() {
+            return couponCategory;
+        }
+
+        public void setCouponCategory(CouponCategoryEnum couponCategory) {
+            this.couponCategory = couponCategory;
+        }
+
+        public Date getIssueStartTime() {
+            return issueStartTime;
+        }
+
+        public void setIssueStartTime(Date issueStartTime) {
+            this.issueStartTime = issueStartTime;
+        }
+
+        public Date getIssueEndTime() {
+            return issueEndTime;
+        }
+
+        public void setIssueEndTime(Date issueEndTime) {
+            this.issueEndTime = issueEndTime;
+        }
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+    }
+
+    @ApiModel("老师学生查看领取过的优惠券信息分页数据")
+    public static class UserCouponIssuePageInfo {
+
+        @ApiModelProperty("用户id")
+        private Long userId;
+
+        @ApiModelProperty("优惠券id")
+        private Long couponId;
+
+        @ApiModelProperty("优惠券名称")
+        private String couponName;
+
+        @ApiModelProperty("优惠券类型 ")
+        private CouponTypeEnum couponType;
+
+        @ApiModelProperty("可用品类 UNIVERSAL:全场通用 VIP:小酷Ai PIANO:云酷琴房 MALL:商场购物券 MUSIC:单曲点播券 SPARRING:陪练课购买券 LIVE:直播课购买券  VIDEO:视频课购买券")
+        private CouponCategoryEnum couponCategory;
+
+        @ApiModelProperty("使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期")
+        private CouponUseStateEnum useState;
+
+        @ApiModelProperty("订单号")
+        private String orderNo;
+
+        @ApiModelProperty("有效期开始时间")
+        @JSONField(format = "yyyy-MM-dd")
+        private Date startTime;
+
+        @ApiModelProperty("有效期结束时间")
+        @JSONField(format = "yyyy-MM-dd")
+        private Date endTime;
+
+        @ApiModelProperty("发放时间")
+        private Date issueTime;
+
+        @ApiModelProperty("使用时间")
+        private Date useTime;
+
+        @ApiModelProperty("发放途径PLATFORM:后台发放 ACTIVITY:活动领取")
+        private SendTypeEnum issueWay;
+
+        @ApiModelProperty("发放人id")
+        private Long issuer;
+
+        @ApiModelProperty("发放人昵称")
+        private String issueUsername;
+
+        @ApiModelProperty("发放人实名")
+        private String issueRealName;
+
+        @ApiModelProperty("备注")
+        private String remark;
+
+        public BigDecimal couponAmount;
+
+        public BigDecimal actualPrice;
+
+        public BigDecimal expectPrice;
+
+        @ApiModelProperty("使用类型")
+        private CouponCategoryEnum useType;
+
+        public CouponCategoryEnum getUseType() {
+            return useType;
+        }
+
+        public void setUseType(CouponCategoryEnum useType) {
+            this.useType = useType;
+        }
+
+        public Long getUserId() {
+            return userId;
+        }
+
+        public void setUserId(Long userId) {
+            this.userId = userId;
+        }
+
+        public Long getCouponId() {
+            return couponId;
+        }
+
+        public void setCouponId(Long couponId) {
+            this.couponId = couponId;
+        }
+
+        public String getCouponName() {
+            return couponName;
+        }
+
+        public void setCouponName(String couponName) {
+            this.couponName = couponName;
+        }
+
+        public CouponTypeEnum getCouponType() {
+            return couponType;
+        }
+
+        public void setCouponType(CouponTypeEnum couponType) {
+            this.couponType = couponType;
+        }
+
+        public CouponCategoryEnum getCouponCategory() {
+            return couponCategory;
+        }
+
+        public void setCouponCategory(CouponCategoryEnum couponCategory) {
+            this.couponCategory = couponCategory;
+        }
+
+        public CouponUseStateEnum getUseState() {
+            return useState;
+        }
+
+        public void setUseState(CouponUseStateEnum useState) {
+            this.useState = useState;
+        }
+
+        public String getOrderNo() {
+            return orderNo;
+        }
+
+        public void setOrderNo(String orderNo) {
+            this.orderNo = orderNo;
+        }
+
+        public Date getStartTime() {
+            return startTime;
+        }
+
+        public void setStartTime(Date startTime) {
+            this.startTime = startTime;
+        }
+
+        public Date getEndTime() {
+            return endTime;
+        }
+
+        public void setEndTime(Date endTime) {
+            this.endTime = endTime;
+        }
+
+        public Date getIssueTime() {
+            return issueTime;
+        }
+
+        public void setIssueTime(Date issueTime) {
+            this.issueTime = issueTime;
+        }
+
+        public Date getUseTime() {
+            return useTime;
+        }
+
+        public void setUseTime(Date useTime) {
+            this.useTime = useTime;
+        }
+
+        public SendTypeEnum getIssueWay() {
+            return issueWay;
+        }
+
+        public void setIssueWay(SendTypeEnum issueWay) {
+            this.issueWay = issueWay;
+        }
+
+        public Long getIssuer() {
+            return issuer;
+        }
+
+        public void setIssuer(Long issuer) {
+            this.issuer = issuer;
+        }
+
+        public String getIssueUsername() {
+            return issueUsername;
+        }
+
+        public void setIssueUsername(String issueUsername) {
+            this.issueUsername = issueUsername;
+        }
+
+        public String getIssueRealName() {
+            return issueRealName;
+        }
+
+        public void setIssueRealName(String issueRealName) {
+            this.issueRealName = issueRealName;
+        }
+
+        public String getRemark() {
+            return remark;
+        }
+
+        public void setRemark(String remark) {
+            this.remark = remark;
+        }
+    }
+}

+ 186 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/coupon/CouponOrderVO.java

@@ -0,0 +1,186 @@
+package com.yonge.cooleshow.admin.io.request.coupon;
+
+import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Lists;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * 优惠券订单信息
+ * Created by Eric.Shang on 2022/9/6.
+ */
+public class CouponOrderVO {
+
+    /**
+     * 订单优惠券信息
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("订单优惠券查询条件")
+    public static class PageRequest implements Serializable {
+
+        @ApiModelProperty("用户ID")
+        private Long userId;
+
+        @ApiModelProperty("客户端类型")
+        private ClientEnum clientType;
+
+        @ApiModelProperty("优惠券类型")
+        private List<CouponCategoryEnum> couponTypes;
+
+        @ApiModelProperty("优惠券发放ID")
+        private List<Long> issueIds;
+
+        @ApiModelProperty("可用状态")
+        private CouponUseStateEnum useState;
+
+        @ApiModelProperty("当前时间")
+        private Long timestamp;
+
+        @ApiModelProperty("订单支付金额")
+        private Double amount;
+
+        public boolean invalidRequestParam() {
+
+            return Objects.isNull(getUserId())
+                    || CollectionUtils.isEmpty(getIssueIds())
+                    || Objects.isNull(getAmount());
+        }
+
+        public String jsonString() {
+
+            return JSON.toJSONString(this);
+        }
+    }
+
+    /**
+     * 订单优惠券信息
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponPageInfo implements Serializable {
+
+        @ApiModelProperty("优惠券抵扣金额")
+        private Double discountedPrices;
+
+        @ApiModelProperty("优惠券信息")
+        private List<CouponInfo> couponInfos;
+
+        @ApiModelProperty("优惠券状态重置标识")
+        private Boolean reset;
+
+        @ApiModelProperty("订单编号")
+        private String orderNo;
+    }
+
+    /**
+     * 优惠券详情信息
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponInfo implements Serializable {
+
+        @ApiModelProperty("优惠券ID")
+        private Long id;
+
+        @ApiModelProperty("优惠券名称")
+        private String name;
+
+        @ApiModelProperty("优惠券使用门槛")
+        private Double useLimit;
+
+        @ApiModelProperty("优惠券抵扣金额")
+        private Double discountPrice;
+
+        @ApiModelProperty("使用状态")
+        private Boolean selected;
+
+        @ApiModelProperty("优惠券发放ID")
+        private Long issueId;
+    }
+
+    /**
+     * 订单优惠券更新请求
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponUpdateRequest implements Serializable {
+
+        @ApiModelProperty("用户ID")
+        private Long userId;
+
+        @ApiModelProperty(value = "优惠券发放ID")
+        private List<Long> issueIds;
+
+        @ApiModelProperty(value = "优惠券信息", hidden = true)
+        private List<CouponInfo> couponInfos;
+
+        @ApiModelProperty("优惠券状态重置标识")
+        private Boolean reset;
+
+        @ApiModelProperty("订单编号")
+        private String orderNo;
+
+        @ApiModelProperty("使用类型")
+        private CouponCategoryEnum useType;
+
+        public boolean invalidRequestParam() {
+
+            if (Optional.ofNullable(getReset()).orElse(false)) {
+
+                // 订单取消,优惠券重置
+                if (StringUtils.isEmpty(getOrderNo())) {
+                    return  true;
+                }
+
+            } else {
+
+                // 更新优惠券使用状态
+                if (CollectionUtils.isEmpty(getIssueIds())) {
+                    return true;
+                }
+            }
+
+            return Objects.isNull(getUserId());
+        }
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+
+        public List<CouponInfo> getCouponInfos() {
+
+            List<CouponInfo> retlist = Lists.newArrayList();
+
+            if (CollectionUtils.isNotEmpty(getIssueIds())) {
+
+                for (Long item : getIssueIds()) {
+
+                    retlist.add(CouponInfo.builder().issueId(item).selected(true).build());
+                }
+            }
+
+            return retlist;
+        }
+    }
+}

+ 11 - 1
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/task/TaskController.java

@@ -4,9 +4,11 @@ import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.common.constant.SysConfigConstant;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import org.apache.commons.lang3.StringUtils;
 import org.redisson.api.RBucket;
 import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -38,6 +40,9 @@ public class TaskController extends BaseController {
     @Autowired
     private ActivityPlanService activityPlanService;
 
+    @Value("${app.blacklist:}")
+    private String blacklistFilePath;
+
     /***
      * 轮询用户订单
      * @author liweifan
@@ -82,8 +87,13 @@ public class TaskController extends BaseController {
     //刷新黑名单
     @PostMapping(value = "/refreshBlackList")
     public HttpResponseResult refreshBlackList() {
+
+        if (StringUtils.isEmpty(blacklistFilePath)) {
+            blacklistFilePath = SysConfigConstant.BLACK_LIST_FILE_PATH;
+        }
+
         RBucket<Long> bucket = redissonClient.getBucket(SysConfigConstant.BLACK_LIST_UPDATE_TIME);
-        File file = new File(SysConfigConstant.BLACK_LIST_FILE_PATH);
+        File file = new File(blacklistFilePath);
         bucket.set(file.lastModified());
         return HttpResponseResult.succeed();
     }

+ 41 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/UserParam.java

@@ -0,0 +1,41 @@
+package com.yonge.cooleshow.biz.dal.dto;
+
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2022-09-02
+ */
+@ApiModel("用户信息")
+public class UserParam{
+
+    @ApiModelProperty("用户id")
+    @NotNull(message = "用户id不能为空")
+    private Long userId;
+
+    @ApiModelProperty("客户端类型 TEACHER :老师 STUDENT:学生")
+    @NotNull(message = "客户端类型不能为空")
+    private ClientEnum clientType;
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public ClientEnum getClientType() {
+        return clientType;
+    }
+
+    public void setClientType(ClientEnum clientType) {
+        this.clientType = clientType;
+    }
+}

+ 17 - 7
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/req/OrderReq.java

@@ -13,6 +13,7 @@ import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import java.math.BigDecimal;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * 下单请求
@@ -38,15 +39,15 @@ public class OrderReq {
     private String orderDesc;
     @ApiModelProperty("用户备注 ")
     private String userNote;
-    @ApiModelProperty(value = "优惠券id")
-    private Long couponId;
+    @ApiModelProperty(value = "优惠券id;多个使用,隔开")
+    private String couponId;
     @ApiModelProperty(value = "推荐用户id(有推荐人的情况)")
     private Long recomUserId;
 
     @ApiModelProperty(value = "活动id")
     private Long activityId;
 
-    @ApiModelProperty(value = "活动id",hidden = true)
+    @ApiModelProperty(value = "活动奖品id",hidden = true)
     private Long rewardId;
     @NotNull(message = "订单金额不能为空")
     @ApiModelProperty(value = "订单金额", required = true)
@@ -77,10 +78,19 @@ public class OrderReq {
         private Object bizContent;
         @ApiModelProperty(value = "调用业务创建方法返回", hidden = true)
         private OrderCreateRes createRes;
-
+        @ApiModelProperty(value = "订单金额", required = true)
+        private BigDecimal actualPrice;
         @ApiModelProperty(value = "活动id")
         private Long activityId;
 
+        public BigDecimal getActualPrice() {
+            return actualPrice;
+        }
+
+        public void setActualPrice(BigDecimal actualPrice) {
+            this.actualPrice = actualPrice;
+        }
+
         public Long getActivityId() {
             return activityId;
         }
@@ -218,11 +228,11 @@ public class OrderReq {
         this.userNote = userNote;
     }
 
-    public Long getCouponId() {
-        return couponId;
+    public String getCouponId() {
+        return Optional.ofNullable(couponId).orElse("");
     }
 
-    public void setCouponId(Long couponId) {
+    public void setCouponId(String couponId) {
         this.couponId = couponId;
     }
 

+ 5 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ActivityReward.java

@@ -45,6 +45,11 @@ public class ActivityReward implements Serializable {
     @TableField(value = "reward_type_")
     @NotNull(message = "奖品类型不能为空")
     private RewardTypeEnum rewardType;
+
+    // @ApiModelProperty("关联优惠券ID ")
+    // @TableField(value = "coupon_id_")
+    // private Long couponId;
+
     @ApiModelProperty("奖品描述 ")
     @TableField(value = "reward_describe_")
     private String rewardDescribe;

+ 117 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CouponInfo.java

@@ -0,0 +1,117 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponValidTypeEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 优惠券信息
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@Getter
+@Setter
+@TableName("coupon_info")
+@ApiModel(value = "CouponInfo对象", description = "优惠券信息")
+public class CouponInfo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("优惠券ID")
+    @TableId(value = "id_", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("优惠券名称")
+    @TableField("name_")
+    private String name;
+
+    @ApiModelProperty("优惠券编号")
+    @TableField("serial_num_")
+    private String serialNum;
+
+    @ApiModelProperty("描述")
+    @TableField("describe_")
+    private String describe;
+
+    @ApiModelProperty("客户端类型")
+    @TableField("client_type_")
+    private ClientEnum clientType;
+
+    @ApiModelProperty("可用品类")
+    @TableField("category_")
+    private CouponCategoryEnum category;
+
+    @ApiModelProperty("使用门槛")
+    @TableField("use_limit_")
+    private BigDecimal useLimit;
+
+    @ApiModelProperty("优惠金额")
+    @TableField("discount_price_")
+    private BigDecimal discountPrice;
+
+    @ApiModelProperty("优惠券类型")
+    @TableField("coupon_type_")
+    private CouponTypeEnum couponType;
+
+    @ApiModelProperty("有效期类型")
+    @TableField("valid_type_")
+    private CouponValidTypeEnum validType;
+
+    @ApiModelProperty("有效天数")
+    @TableField("valid_day_")
+    private Integer validDay;
+
+    @ApiModelProperty("生效时间")
+    @TableField("start_time_")
+    private Long startTime;
+
+    @ApiModelProperty("失效时间")
+    @TableField("end_time_")
+    private Long endTime;
+
+    @ApiModelProperty("库存量")
+    @TableField("inventory_")
+    private Integer inventory;
+
+    @ApiModelProperty("领取次数限制")
+    @TableField("quantity_limit_")
+    private Integer quantityLimit;
+
+    @ApiModelProperty("启禁状态")
+    @TableField("status_")
+    private Integer status;
+
+    @ApiModelProperty("更新用户")
+    @TableField("updated_by_")
+    private Long updatedBy;
+
+    @ApiModelProperty("更新时间")
+    @TableField("update_time_")
+    private Long updateTime;
+
+    @ApiModelProperty("创建用户")
+    @TableField("created_by_")
+    private Long createdBy;
+
+    @ApiModelProperty("创建时间")
+    @TableField("created_time_")
+    private Date createdTime;
+
+
+}

+ 65 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CouponInventory.java

@@ -0,0 +1,65 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.util.Date;
+
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponInventoryEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 优惠券库存变化
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-05
+ */
+@Getter
+@Setter
+@TableName("coupon_inventory")
+@ApiModel(value = "CouponInventory对象", description = "优惠券库存变化")
+public class CouponInventory implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("发放ID")
+    @TableId(value = "id_", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("用户ID")
+    @TableField("user_id_")
+    private Long userId;
+
+    @ApiModelProperty("优惠券ID")
+    @TableField("coupon_id_")
+    private Long couponId;
+
+    @ApiModelProperty("数据类型")
+    @TableField("data_type_")
+    private CouponInventoryEnum dataType;
+
+    @ApiModelProperty("变化量")
+    @TableField("number_")
+    private Integer number;
+
+    @ApiModelProperty("备注")
+    @TableField("remark_")
+    private String remark;
+
+    @ApiModelProperty("启禁状态")
+    @TableField("status_")
+    private Integer status;
+
+    @ApiModelProperty("创建时间")
+    @TableField("created_time_")
+    private Date createdTime;
+
+
+}

+ 98 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CouponIssue.java

@@ -0,0 +1,98 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.util.Date;
+
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 优惠券发放
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@Getter
+@Setter
+@TableName("coupon_issue")
+@ApiModel(value = "CouponIssue对象", description = "优惠券发放")
+public class CouponIssue implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("发放ID")
+    @TableId(value = "id_", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("用户ID")
+    @TableField("user_id_")
+    private Long userId;
+
+    @ApiModelProperty("优惠券ID")
+    @TableField("coupon_id_")
+    private Long couponId;
+
+    @ApiModelProperty("客户端类型")
+    @TableField("client_type_")
+    private String clientType;
+
+    @ApiModelProperty("使用状态")
+    @TableField("use_state_")
+    private CouponUseStateEnum useState;
+
+    @ApiModelProperty("使用时间")
+    @TableField("use_time_")
+    private Long useTime;
+
+    @ApiModelProperty("使用类型")
+    @TableField("use_type_")
+    private CouponCategoryEnum useType;
+
+    @ApiModelProperty("订单编号")
+    @TableField("order_no_")
+    private String orderNo;
+
+    @ApiModelProperty("生效时间")
+    @TableField("start_time_")
+    private Long startTime;
+
+    @ApiModelProperty("失效时间")
+    @TableField("end_time_")
+    private Long endTime;
+
+    @ApiModelProperty("发放方式")
+    @TableField("issue_way_")
+    private String issueWay;
+
+    @ApiModelProperty("发放人")
+    @TableField("issuer_")
+    private Long issuer;
+
+    @ApiModelProperty("发放时间")
+    @TableField("issue_time_")
+    private Long issueTime;
+
+    @ApiModelProperty("备注")
+    @TableField("remark_")
+    private String remark;
+
+    @ApiModelProperty("启禁状态")
+    @TableField("status_")
+    private Integer status;
+
+    @ApiModelProperty("创建时间")
+    @TableField("created_time_")
+    private Date createdTime;
+
+
+}

+ 127 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/SysUser.java

@@ -0,0 +1,127 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 用户表
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@Getter
+@Setter
+@TableName("sys_user")
+@ApiModel(value = "SysUser对象", description = "用户表")
+public class SysUser implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("主键ID")
+    @TableId(value = "id_", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("用户名")
+    @TableField("username_")
+    private String username;
+
+    @TableField("password_")
+    private String password;
+
+    @ApiModelProperty("随机盐")
+    @TableField("salt_")
+    private String salt;
+
+    @ApiModelProperty("手机号")
+    @TableField("phone_")
+    private String phone;
+
+    @ApiModelProperty("头像")
+    @TableField("avatar_")
+    private String avatar;
+
+    @ApiModelProperty("创建时间")
+    @TableField("create_time_")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("修改时间")
+    @TableField("update_time_")
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("0-正常,1-锁定")
+    @TableField("lock_flag_")
+    private Integer lockFlag;
+
+    @ApiModelProperty("微信openid")
+    @TableField("wx_openid_")
+    private String wxOpenid;
+
+    @ApiModelProperty("QQ openid")
+    @TableField("qq_openid_")
+    private String qqOpenid;
+
+    @ApiModelProperty("用户类型(STUDENT/TEACHER/SYSTEM)")
+    @TableField("user_type_")
+    private String userType;
+
+    @ApiModelProperty("性别(0,女  1,男)")
+    @TableField("gender_")
+    private Integer gender;
+
+    @ApiModelProperty("民族")
+    @TableField("nation_")
+    private String nation;
+
+    @ApiModelProperty("出生日期")
+    @TableField("birthdate_")
+    private LocalDate birthdate;
+
+    @ApiModelProperty("邮箱")
+    @TableField("email_")
+    private String email;
+
+    @ApiModelProperty("融云token")
+    @TableField("im_token_")
+    private String imToken;
+
+    @ApiModelProperty("真实姓名")
+    @TableField("real_name_")
+    private String realName;
+
+    @ApiModelProperty("身份证号码")
+    @TableField("id_card_no_")
+    private String idCardNo;
+
+    @ApiModelProperty("证件类型")
+    @TableField("certificate_type_")
+    private String certificateType;
+
+    @ApiModelProperty("是否是超管")
+    @TableField("is_super_admin_")
+    private Boolean superAdmin;
+
+    @ApiModelProperty("微信号")
+    @TableField("wechat_id_")
+    private String wechatId;
+
+    @ApiModelProperty("0-正常,1-删除")
+    @TableField("del_flag_")
+    private Integer delFlag;
+
+    @ApiModelProperty("最近一次修改用户名称时间")
+    @TableField("last_username_time_")
+    private LocalDateTime lastUsernameTime;
+
+
+}

+ 6 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/MK.java

@@ -10,4 +10,10 @@ public interface MK {
     String EXP_MOBILE_NUMBER = "^1(3\\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$";
     // 正型
     String EXP_INT = "^\\d+$";
+    // 时间格式
+    String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+    // 时区设置
+    String TIME_ZONE = "GMT+8";
+    // 时间戳
+    String YYYYMMDD = "yyyyMMdd";
 }

+ 1 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/MessageTypeEnum.java

@@ -106,6 +106,7 @@ public enum MessageTypeEnum implements BaseEnum<String, MessageTypeEnum> {
     PLATFORM_ADD_VIP("会员赠送"),
     SMS_STUDENT_LIVE_COMPLETION_FAIL("直播课成课失败"),
     STUDENT_LIVE_COMPLETION_FAIL("直播课成课失败"),
+    COUPON_ISSUE("优惠券发放"),
 
     ;
 

+ 35 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/SendTypeEnum.java

@@ -0,0 +1,35 @@
+package com.yonge.cooleshow.biz.dal.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.yonge.toolset.base.enums.BaseEnum;
+
+/**
+ * 状态,-1,发送失败;0,待发送;1,发送中;2,发送完成
+ */
+public enum SendTypeEnum implements BaseEnum<String, SendTypeEnum> {
+
+    PLATFORM("PLATFORM"),
+    ACTIVITY("ACTIVITY"),
+
+
+    ;
+
+    @EnumValue
+    private String code;
+    private String msg;
+
+    SendTypeEnum(String msg) {
+        this.code = this.name();
+        this.msg = msg;
+    }
+
+    @Override
+    public String getCode() {
+        return this.code;
+    }
+
+    public String getMsg() {
+        return this.msg;
+    }
+
+}

+ 100 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponCategoryEnum.java

@@ -0,0 +1,100 @@
+package com.yonge.cooleshow.biz.dal.enums.coupon;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.google.common.collect.Lists;
+import com.yonge.toolset.base.enums.BaseEnum;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+
+/**
+ * 优惠券可用品类
+ * @author: Eric
+ * @date: 2022-03-30
+ */
+public enum CouponCategoryEnum implements BaseEnum<String, CouponCategoryEnum> {
+
+    UNIVERSAL("全场通用", "UNIVERSAL"),
+    VIP("小酷Ai", "VIP"),
+    PIANO("云酷琴房", "PINAO_ROOM"),
+    MALL("商场购物券", "MALL"),
+    MUSIC("单曲点播券", "MUSIC"),
+    SPARRING("陪练课购买券", "PRACTICE"),
+    LIVE("直播课购买券", "LIVE"),
+    VIDEO("视频课购买券", "VIDEO"),
+    ;
+
+    @EnumValue
+    private String code;
+    private String msg;
+    private String goodsType;
+
+    CouponCategoryEnum(String msg, String goodsType) {
+        this.code = this.name();
+        this.msg = msg;
+        this.goodsType = goodsType;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return this.msg;
+    }
+
+    public String getGoodsType() {
+        return goodsType;
+    }
+
+    /**
+     * 商品对应优惠券类型
+     * @param goodsType 商品类型
+     * @return List<CouponCategoryEnum>
+     */
+    public static List<CouponCategoryEnum> getCategory(String goodsType) {
+
+        List<CouponCategoryEnum> retlist = Lists.newArrayList(UNIVERSAL);
+
+        if (StringUtils.isNotEmpty(goodsType)) {
+
+            CouponCategoryEnum[] values = CouponCategoryEnum.values();
+
+            for (CouponCategoryEnum value : values) {
+
+                if (value.getGoodsType().equals(goodsType)) {
+
+                    retlist.add(value);
+                }
+            }
+
+        }
+
+        return retlist;
+    }
+
+    /**
+     * 商品对应优惠券类型
+     * @param goodsType 商品类型
+     * @return List<CouponCategoryEnum>
+     */
+    public static CouponCategoryEnum goodTypeTo(String goodsType) {
+
+
+        if (StringUtils.isNotEmpty(goodsType)) {
+
+            CouponCategoryEnum[] values = CouponCategoryEnum.values();
+
+            for (CouponCategoryEnum value : values) {
+
+                if (value.getGoodsType().equals(goodsType)) {
+
+                    return value;
+                }
+            }
+
+        }
+
+        return null;
+    }
+}

+ 33 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponInventoryEnum.java

@@ -0,0 +1,33 @@
+package com.yonge.cooleshow.biz.dal.enums.coupon;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.yonge.toolset.base.enums.BaseEnum;
+
+/**
+ * 优惠券库存调整类型
+ * @author: Eric
+ * @date: 2022-03-30
+ */
+public enum CouponInventoryEnum implements BaseEnum<String, CouponInventoryEnum> {
+
+    ADDITION("添加"),
+    REDUCE("减少"),
+    ;
+
+    @EnumValue
+    private String code;
+    private String msg;
+
+    CouponInventoryEnum(String msg) {
+        this.code = this.name();
+        this.msg = msg;
+    }
+
+    public String getMsg() {
+        return this.msg;
+    }
+
+    public String getCode() {
+        return code;
+    }
+}

+ 51 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponTypeEnum.java

@@ -0,0 +1,51 @@
+package com.yonge.cooleshow.biz.dal.enums.coupon;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.yonge.toolset.base.enums.BaseEnum;
+
+/**
+ * 优惠券类型
+ * @author: Eric
+ * @date: 2022-03-30
+ */
+public enum CouponTypeEnum implements BaseEnum<String, CouponTypeEnum> {
+
+    FULL_DISCOUNT("满减券"),
+    VOUCHER("代金券"),
+    ;
+
+    @EnumValue
+    private String code;
+    private String msg;
+
+    CouponTypeEnum(String msg) {
+        this.code = this.name();
+        this.msg = msg;
+    }
+
+    public String getMsg() {
+        return this.msg;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    /**
+     * 类型匹配
+     * @param name 类型
+     * @return boolean
+     */
+    public static boolean match(String name) {
+
+        CouponTypeEnum[] values = CouponTypeEnum.values();
+        for (CouponTypeEnum item : values) {
+
+            if (item.name().equals(name)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 35 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponUseStateEnum.java

@@ -0,0 +1,35 @@
+package com.yonge.cooleshow.biz.dal.enums.coupon;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.yonge.toolset.base.enums.BaseEnum;
+
+/**
+ * 优惠券使用状态
+ * @author: Eric
+ * @date: 2022-03-30
+ */
+public enum CouponUseStateEnum implements BaseEnum<String, CouponUseStateEnum> {
+
+
+    EXPIRED("已失效"),
+    USABLE("可使用"),
+    USED("已使用"),
+    WITHDRAW("撤回"),
+    ;
+    @EnumValue
+    private String code;
+    private String msg;
+
+    CouponUseStateEnum(String msg) {
+        this.code = this.name();
+        this.msg = msg;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return this.msg;
+    }
+}

+ 33 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/coupon/CouponValidTypeEnum.java

@@ -0,0 +1,33 @@
+package com.yonge.cooleshow.biz.dal.enums.coupon;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.yonge.toolset.base.enums.BaseEnum;
+
+/**
+ * 优惠券有效期类型
+ * @author: Eric
+ * @date: 2022-03-30
+ */
+public enum CouponValidTypeEnum implements BaseEnum<String, CouponValidTypeEnum> {
+
+    DAY("固定有效天数"),
+    TIME_PERIOD("固定时间段"),
+    ;
+
+    @EnumValue
+    private String code;
+    private String msg;
+
+    CouponValidTypeEnum(String msg) {
+        this.code = this.name();
+        this.msg = msg;
+    }
+
+    public String getMsg() {
+        return this.msg;
+    }
+
+    public String getCode() {
+        return code;
+    }
+}

+ 82 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/CouponInfoMapper.java

@@ -0,0 +1,82 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.biz.dal.entity.CouponInfo;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInventoryQuery;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponInventoryWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponOrderWrapper;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * 优惠券信息 Mapper 接口
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@Repository
+public interface CouponInfoMapper extends BaseMapper<CouponInfo> {
+
+    /**
+     * 分页查询优惠券信息
+     *
+     * @param page  IPage<CouponInfoWrapper>
+     * @param query CouponInfoQuery
+     * @return List<CouponInfoWrapper>
+     */
+    List<CouponInfoWrapper> selectCouponPageInfo(@Param("page") IPage<CouponInfoWrapper> page, @Param("record") CouponInfoQuery query);
+
+    /**
+     * 优惠券发放未使用统计
+     *
+     * @param couponIds 优惠券ID
+     * @param query     统计查询条件
+     * @return List<StatGroupWrapper>
+     */
+    List<StatGroupWrapper> selectCouponIssueStatInfo(@Param("couponIds") List<Long> couponIds, @Param("record") CouponInfoQuery.IssueStatQuery query);
+
+    /**
+     * 优惠券库存量调整信息
+     *
+     * @param page  IPage<CouponInventoryWrapper>
+     * @param query CouponInventoryQuery
+     * @return List<CouponInventoryWrapper>
+     */
+    List<CouponInventoryWrapper> selectCouponInventoryPageInfo(@Param("page") IPage<CouponInventoryWrapper> page, @Param("record") CouponInventoryQuery query);
+
+    /**
+     * 活动优惠券统计
+     *
+     * @param couponIds 优惠券ID
+     * @return List<StatGroupWrapper>
+     */
+    List<StatGroupWrapper> selectActivityCouponStatInfo(@Param("couponIds") List<Long> couponIds);
+
+    /**
+     * 优惠券库存变更
+     *
+     * @param couponId 优惠券id
+     * @param size     变更数量
+     * @param inOrOut  增/减状态
+     * @return
+     */
+    int updateStock(@Param("couponId") Long couponId, @Param("size") int size, @Param("inOrOut") InOrOutEnum inOrOut);
+
+    /**
+     * 用户订单优惠券信息
+     * @param userid 用户ID
+     * @param query CouponInfoQuery.CouponOrderQuery
+     * @return List<CouponOrderWrapper.CouponInfo>
+     */
+    List<CouponOrderWrapper.CouponInfo> selectUserOrderCouponInfo(@Param("userId") Long userid, @Param("record") CouponInfoQuery.CouponOrderQuery query);
+}

+ 18 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/CouponInventoryMapper.java

@@ -0,0 +1,18 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import com.yonge.cooleshow.biz.dal.entity.CouponInventory;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * <p>
+ * 优惠券库存变化 Mapper 接口
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-05
+ */
+@Repository
+public interface CouponInventoryMapper extends BaseMapper<CouponInventory> {
+
+}

+ 36 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/CouponIssueMapper.java

@@ -0,0 +1,36 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.biz.dal.entity.CouponIssue;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponIssueQueryInfo;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponIssueWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 优惠券发放 Mapper 接口
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@Repository
+public interface CouponIssueMapper extends BaseMapper<CouponIssue> {
+
+    List<CouponIssueWrapper> queryCouponIssueInfo(IPage<CouponIssueWrapper> page, @Param("query") CouponIssueQueryInfo query);
+
+    /**
+     * 用户优惠券状态统计
+     * @param id 用户ID
+     * @param query CouponInfoQuery.CouponStateStatQuery
+     * @return List<StatGroupWrapper>
+     */
+    List<StatGroupWrapper> selectCouponStateStatInfo(@Param("userId") Long id, @Param("record") CouponInfoQuery.CouponStateStatQuery query);
+}

+ 34 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/SysUserMapper.java

@@ -0,0 +1,34 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.biz.dal.entity.SysUser;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.vo.CouponIssueUserVo;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 用户表 Mapper 接口
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@Repository
+public interface SysUserMapper extends BaseMapper<SysUser> {
+
+    /**
+     * 获取能发放优惠券的用户信息
+     *
+     * @param iPage
+     * @param client  客户端类型
+     * @param keyword 查询条件
+     * @return
+     */
+    List<CouponIssueUserVo> queryCouponIssueUser(@Param("iPage") IPage<CouponIssueUserVo> iPage, @Param(
+            "client") ClientEnum client, @Param("keyword") String keyword);
+}

+ 182 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/queryInfo/CouponInfoQuery.java

@@ -0,0 +1,182 @@
+package com.yonge.cooleshow.biz.dal.queryInfo;
+
+import com.alibaba.fastjson.JSON;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponValidTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * 优惠券查询条件
+ * Created by Eric.Shang on 2022/9/2.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CouponInfoQuery implements Serializable {
+
+    // 优惠券ID
+    private Long couponId;
+    // 优惠券名称
+    private String name;
+    // 客户端类型
+    private ClientEnum clientType;
+    // 优惠券类型
+    private CouponTypeEnum couponType;
+    // 可用品类
+    private CouponCategoryEnum category;
+    // 优惠券状态
+    private Integer status;
+    // 当前时间
+    private Long timestamp;
+    // 优惠券时间类型
+    private CouponValidTypeEnum validType;
+
+    public static CouponInfoQuery from(String recv) {
+
+        return JSON.parseObject(recv, CouponInfoQuery.class);
+    }
+
+    /**
+     * 优惠券发放数统计
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class IssueStatQuery implements Serializable {
+
+        // 当前时间
+        private Long timestamp;
+
+        // 使用状态
+        private CouponUseStateEnum useState;
+
+        // 状态排除
+        private List<CouponUseStateEnum> excludeUseState;
+    }
+
+    /**
+     * 优惠券状态统计
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponStateStatQuery implements Serializable {
+
+        // 客户端类型
+        private ClientEnum clientType;
+
+        // 优惠券类型
+        private CouponTypeEnum couponType;
+
+        // 类型分组
+        private Boolean groupByState;
+
+        // 当前时间
+        private Long timestamp;
+
+        // 订单类型
+        private CouponUseStateEnum userState;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static CouponStateStatQuery from(String recv) {
+
+            return JSON.parseObject(recv, CouponStateStatQuery.class);
+        }
+
+
+        public CouponStateStatQuery couponType(String couponType) {
+            if (CouponTypeEnum.match(couponType)) {
+                this.couponType = CouponTypeEnum.valueOf(couponType);
+            }
+            return this;
+        }
+
+
+        public CouponStateStatQuery groupByState(Boolean groupByState) {
+            this.groupByState = groupByState;
+            return this;
+        }
+
+        public CouponStateStatQuery timestamp(Long timestamp) {
+            this.timestamp = timestamp;
+            return this;
+        }
+
+        public CouponStateStatQuery userState(CouponUseStateEnum userState) {
+            this.userState = userState;
+            return this;
+        }
+    }
+
+    /**
+     * 用户发放优惠券信息
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponOrderQuery implements Serializable {
+
+        // 客户端类型
+        private ClientEnum clientType;
+
+        // 优惠券类型
+        private List<CouponCategoryEnum> couponTypes;
+
+        // 优惠券发放ID
+        private List<Long> issueIds;
+
+        // 可用状态
+        private CouponUseStateEnum useState;
+
+        // 当前时间
+        private Long timestamp;
+
+        // 订单编号
+        private String orderNo;
+
+        // 订单支付金额
+        private Double amount;
+
+        public static CouponOrderQuery from(String recv) {
+            return JSON.parseObject(recv, CouponOrderQuery.class);
+        }
+
+        public Double getAmount() {
+            return Optional.ofNullable(amount).orElse(0D);
+        }
+
+        public List<CouponCategoryEnum> getCouponTypes() {
+
+            return Optional.ofNullable(couponTypes).filter(CollectionUtils::isNotEmpty).orElse(null);
+        }
+
+
+        public CouponOrderQuery issueIds(String issueIds) {
+            if (StringUtils.isNotEmpty(issueIds)) {
+                this.issueIds = Arrays.stream(issueIds.split(",")).map(Long::parseLong).collect(Collectors.toList());
+            }
+            return this;
+        }
+    }
+}

+ 48 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/queryInfo/CouponInventoryQuery.java

@@ -0,0 +1,48 @@
+package com.yonge.cooleshow.biz.dal.queryInfo;
+
+import com.alibaba.fastjson.JSON;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponInventoryEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 优惠券查询条件
+ * Created by Eric.Shang on 2022/9/2.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CouponInventoryQuery implements Serializable {
+
+    // 优惠券ID
+    private Long couponId;
+    // 操作人ID
+    private Long userId;
+    // 用户名称匹配
+    private String keyword;
+    // 调整类型
+    private CouponInventoryEnum dataType;
+    // 按用户分组
+    private Boolean groupByUser;
+
+    // 开始时间
+    private Long startTime;
+    // 结束时间
+    private Long endTime;
+
+    public static CouponInventoryQuery from(String recv) {
+
+        return JSON.parseObject(recv, CouponInventoryQuery.class);
+    }
+
+
+    public CouponInventoryQuery groupByUser(Boolean groupByUser) {
+        this.groupByUser = groupByUser;
+        return this;
+    }
+}

+ 204 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/queryInfo/CouponIssueQueryInfo.java

@@ -0,0 +1,204 @@
+package com.yonge.cooleshow.biz.dal.queryInfo;
+
+import com.alibaba.fastjson.JSON;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.SendTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import io.swagger.annotations.ApiModelProperty;
+import org.joda.time.DateTime;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2022-09-02
+ */
+public class CouponIssueQueryInfo implements Serializable {
+
+    // 优惠券id
+    private Long couponId;
+    // 用户id
+    private Long userId;
+
+    // 真实姓名/昵称/手机号/编号
+    private String keyword;
+
+    // 客户端 TEACHER  STUDENT
+    private ClientEnum clientType;
+
+    // 发放方式 PLATFORM ACTIVITY
+    private SendTypeEnum issueWay;
+
+    // 使用状态 USED USABLE EXPIRED
+    private CouponUseStateEnum useState;
+
+    //  发放开始时间
+    private Long issueStartTime;
+
+    // 发放结束时间
+    private Long issueEndTime;
+
+    //  使用开始时间
+    private Long usedStartTime;
+
+    // 使用结束时间
+    private Long usedEndTime;
+
+
+    // 优惠券编号/名称
+    private String couponKeyword;
+
+    // 优惠券类型
+    private CouponTypeEnum couponType;
+
+    // 订单金额
+    private BigDecimal amount;
+
+    // 可用品类 UNIVERSAL:全场通用 VIP:小酷Ai PIANO:云酷琴房 MALL:商场购物券 MUSIC:单曲点播券 SPARRING:陪练课购买券 LIVE:直播课购买券  VIDEO:视频课购买券
+    private CouponCategoryEnum couponCategory;
+
+    // 可用品类
+    private List<CouponCategoryEnum> couponCategoryList;
+
+    // 过期时间
+    private Long expiredTime = DateTime.now().getMillis();
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public void setAmount(BigDecimal amount) {
+        this.amount = amount;
+    }
+
+    public List<CouponCategoryEnum> getCouponCategoryList() {
+        return couponCategoryList;
+    }
+
+    public void setCouponCategoryList(List<CouponCategoryEnum> couponCategoryList) {
+        this.couponCategoryList = couponCategoryList;
+    }
+
+    public Long getExpiredTime() {
+        return expiredTime;
+    }
+
+    public void setExpiredTime(Long expiredTime) {
+        this.expiredTime = expiredTime;
+    }
+
+    public static CouponIssueQueryInfo from(String recv) {
+
+        return JSON.parseObject(recv, CouponIssueQueryInfo.class);
+    }
+
+    public String getCouponKeyword() {
+        return couponKeyword;
+    }
+
+    public void setCouponKeyword(String couponKeyword) {
+        this.couponKeyword = couponKeyword;
+    }
+
+    public CouponTypeEnum getCouponType() {
+        return couponType;
+    }
+
+    public void setCouponType(CouponTypeEnum couponType) {
+        this.couponType = couponType;
+    }
+
+    public CouponCategoryEnum getCouponCategory() {
+        return couponCategory;
+    }
+
+    public void setCouponCategory(CouponCategoryEnum couponCategory) {
+        this.couponCategory = couponCategory;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getKeyword() {
+        return keyword;
+    }
+
+    public void setKeyword(String keyword) {
+        this.keyword = keyword;
+    }
+
+    public ClientEnum getClientType() {
+        return clientType;
+    }
+
+    public void setClientType(ClientEnum clientType) {
+        this.clientType = clientType;
+    }
+
+    public SendTypeEnum getIssueWay() {
+        return issueWay;
+    }
+
+    public void setIssueWay(SendTypeEnum issueWay) {
+        this.issueWay = issueWay;
+    }
+
+    public Long getCouponId() {
+        return couponId;
+    }
+
+    public void setCouponId(Long couponId) {
+        this.couponId = couponId;
+    }
+
+    public CouponUseStateEnum getUseState() {
+        return useState;
+    }
+
+    public void setUseState(CouponUseStateEnum useState) {
+        this.useState = useState;
+    }
+
+    public Long getIssueStartTime() {
+        return issueStartTime;
+    }
+
+    public void setIssueStartTime(Long issueStartTime) {
+        this.issueStartTime = issueStartTime;
+    }
+
+    public Long getIssueEndTime() {
+        return issueEndTime;
+    }
+
+    public void setIssueEndTime(Long issueEndTime) {
+        this.issueEndTime = issueEndTime;
+    }
+
+    public Long getUsedStartTime() {
+        return usedStartTime;
+    }
+
+    public void setUsedStartTime(Long usedStartTime) {
+        this.usedStartTime = usedStartTime;
+    }
+
+    public Long getUsedEndTime() {
+        return usedEndTime;
+    }
+
+    public void setUsedEndTime(Long usedEndTime) {
+        this.usedEndTime = usedEndTime;
+    }
+}

+ 93 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/CouponInfoService.java

@@ -0,0 +1,93 @@
+package com.yonge.cooleshow.biz.dal.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.yonge.cooleshow.biz.dal.entity.CouponInfo;
+import com.yonge.cooleshow.biz.dal.entity.CouponInventory;
+import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInventoryQuery;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponInventoryWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponOrderWrapper;
+
+/**
+ * <p>
+ * 优惠券信息 服务类
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+public interface CouponInfoService extends IService<CouponInfo> {
+
+    /**
+     * 分页查询优惠券信息
+     * @param page IPage<CouponInfoWrapper>
+     * @param query CouponInfoQuery
+     * @return IPage<CouponInfoWrapper>
+     */
+    IPage<CouponInfoWrapper> queryCouponPageInfo(IPage<CouponInfoWrapper> page, CouponInfoQuery query);
+
+    /**
+     * 优惠券信息
+     * @param id 优惠券ID
+     * @return CouponInfo
+     */
+    CouponInfo queryCouponInfoById(Long id);
+
+    /**
+     * 新增或更新优惠券信息
+     * @param couponInfo CouponInfo
+     * @param stateUpdate 状态更新标识
+     * @return CouponInfo
+     */
+    CouponInfo saveOrUpdateCouponInfo(CouponInfo couponInfo, Boolean stateUpdate);
+
+    /**
+     * 优惠券库存量调整信息
+     * @param page IPage<CouponInventoryWrapper>
+     * @param query CouponInventoryQuery
+     * @return IPage<CouponInventoryWrapper>
+     */
+    IPage<CouponInventoryWrapper> queryCouponInventoryPageInfo(IPage<CouponInventoryWrapper> page, CouponInventoryQuery query);
+
+    /**
+     * 查询订单优惠券信息
+     * @param userId 用户ID
+     * @param query CouponInfoQuery.CouponOrderQuery
+     * @return CouponOrderWrapper
+     */
+    CouponOrderWrapper queryUserOrderCouponInfo(Long userId, CouponInfoQuery.CouponOrderQuery query);
+
+    /**
+     * 更新用户订单优惠券状态
+     * @param wrapper CouponOrderWrapper
+     * @return int 受影响的行数
+     */
+    int updateUserOrderCouponInfo(CouponOrderWrapper wrapper);
+
+    /**
+     * 更新用户订单优惠券状态
+     * @param wrapper CouponOrderWrapper
+     * @return int 受影响的行数
+     */
+    int updateCouponOrderInfo( CouponOrderWrapper wrapper);
+
+    /**
+     * 新增优惠券库存量调整信息
+     * @param inventory CouponInventory
+     * @return int 入库行数
+     */
+    int saveOrUpdateCouponInventoryInfo(CouponInventory inventory);
+
+    /**
+     * 优惠券库存变更
+     *
+     * @param couponId 优惠券id
+     * @param size 变更数量
+     * @param inOrOut 增/减状态
+     * @return
+     */
+    int updateStock(Long couponId, int size, InOrOutEnum inOrOut);
+}

+ 16 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/CouponInventoryService.java

@@ -0,0 +1,16 @@
+package com.yonge.cooleshow.biz.dal.service;
+
+import com.yonge.cooleshow.biz.dal.entity.CouponInventory;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 优惠券库存变化 服务类
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-05
+ */
+public interface CouponInventoryService extends IService<CouponInventory> {
+
+}

+ 68 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/CouponIssueService.java

@@ -0,0 +1,68 @@
+package com.yonge.cooleshow.biz.dal.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.yonge.cooleshow.biz.dal.dto.UserParam;
+import com.yonge.cooleshow.biz.dal.entity.CouponIssue;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponIssueQueryInfo;
+import com.yonge.cooleshow.biz.dal.vo.CouponIssueUserVo;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponIssueWrapper;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 优惠券发放 服务类
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+public interface CouponIssueService extends IService<CouponIssue> {
+
+    /**
+     * 优惠券发放记录分页
+     *
+     * @param page
+     * @param query
+     * @return
+     */
+    IPage<CouponIssueWrapper> queryCouponIssueInfo(IPage<CouponIssueWrapper> page, CouponIssueQueryInfo query);
+
+    /**
+     * 撤销优惠券
+     *
+     * @param couponIssueId 优惠券发放记录id
+     * @return
+     */
+    Boolean withdrawCoupon(Long couponIssueId);
+
+    /**
+     * 发放优惠券
+     * @param couponId 优惠券id
+     * @param userParam 用户信息
+     * @param issuer
+     * @param reason
+     */
+    void issueCoupon(Long couponId, List<UserParam> userParam, Long issuer, String reason);
+
+    /**
+     * 查询能发放优惠券的用户列表
+     *
+     * @param couponId 优惠券id
+     * @param client 客户端
+     * @param keyword 真实姓名/昵称/手机号/编号
+     */
+    IPage<CouponIssueUserVo> queryUser(IPage<CouponIssueUserVo> iPage, Long couponId, ClientEnum client, String keyword);
+
+    /**
+     * 统计用户优惠券状态
+     * @param id 用户ID
+     * @param query CouponInfoQuery.CouponStateStatQuery
+     * @return List<CouponInfoWrapper.CouponStat>
+     */
+    List<CouponInfoWrapper.CouponStat> queryCouponStateStatInfo(Long id, CouponInfoQuery.CouponStateStatQuery query);
+}

+ 427 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CouponInfoServiceImp.java

@@ -0,0 +1,427 @@
+package com.yonge.cooleshow.biz.dal.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
+import com.yonge.cooleshow.biz.dal.entity.CouponInfo;
+import com.yonge.cooleshow.biz.dal.entity.CouponInventory;
+import com.yonge.cooleshow.biz.dal.entity.CouponIssue;
+import com.yonge.cooleshow.biz.dal.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponInventoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponValidTypeEnum;
+import com.yonge.cooleshow.biz.dal.mapper.CouponInfoMapper;
+import com.yonge.cooleshow.biz.dal.mapper.CouponInventoryMapper;
+import com.yonge.cooleshow.biz.dal.mapper.CouponIssueMapper;
+import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInventoryQuery;
+import com.yonge.cooleshow.biz.dal.service.CouponInfoService;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponInventoryWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponOrderWrapper;
+import com.yonge.cooleshow.common.enums.EStatus;
+import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.base.util.ThreadPool;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 优惠券信息 服务实现类
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@Service
+public class CouponInfoServiceImp extends ServiceImpl<CouponInfoMapper, CouponInfo> implements CouponInfoService {
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+    @Autowired
+    private CouponInventoryMapper couponInventoryMapper;
+    @Autowired
+    private CouponIssueMapper couponIssueMapper;
+
+    /**
+     * 分页查询优惠券信息
+     *
+     * @param page  IPage<CouponInfoWrapper>
+     * @param query CouponInfoQuery
+     * @return IPage<CouponInfoWrapper>
+     */
+    @Override
+    public IPage<CouponInfoWrapper> queryCouponPageInfo(IPage<CouponInfoWrapper> page, CouponInfoQuery query) {
+
+        // 优惠券信息
+        List<CouponInfoWrapper> wrappers = getBaseMapper().selectCouponPageInfo(page, query);
+
+        if (CollectionUtils.isEmpty(wrappers)) {
+            // 没有数据,直接返回
+            return page.setRecords(Lists.newArrayList());
+        }
+
+        List<Long> couponIds = wrappers.stream()
+                .map(CouponInfoWrapper::getId).distinct().collect(Collectors.toList());
+
+        // 查询优惠券发放人数
+        Map<Long, Integer> issueNumMap = getBaseMapper().selectCouponIssueStatInfo(couponIds,
+                        CouponInfoQuery.IssueStatQuery.builder()
+                                //.timestamp(DateTime.now().getMillis())
+                                .excludeUseState(Lists.newArrayList(CouponUseStateEnum.WITHDRAW)).build()).stream()
+                .collect(Collectors.toMap(StatGroupWrapper::getId, StatGroupWrapper::getTotal, (o, n) -> n));
+
+        // 关联奖品数
+        Map<Long, Integer> rewardNumMap = getBaseMapper().selectActivityCouponStatInfo(couponIds).stream()
+                .collect(Collectors.toMap(StatGroupWrapper::getId, StatGroupWrapper::getTotal, (o, n) -> n));
+
+        // 更新用户信息
+        List<Long> userIds = wrappers.stream().map(CouponInfoWrapper::getUpdatedBy).distinct().collect(Collectors.toList());
+
+        Map<Long, String> userNameMap = sysUserMapper.selectBatchIds(userIds).stream()
+                .collect(Collectors.toMap(SysUser::getId, x -> {
+
+                    if (StringUtils.isNotEmpty(x.getUsername())) {
+                        return x.getUsername();
+                    }
+                    return x.getRealName();
+                }, (o, n) -> n));
+
+        List<Long> updateIds = Lists.newArrayList();
+        for (CouponInfoWrapper item : wrappers) {
+
+            if (CouponValidTypeEnum.TIME_PERIOD == item.getValidType()
+                    && DateTime.now().getMillis() > item.getEndTime()) {
+
+                updateIds.add(item.getId());
+                // 更新显示状态为禁用
+                item.setStatus(EStatus.DISABLE.getValue());
+            }
+
+            item.rewardNum(rewardNumMap.getOrDefault(item.getId(), 0))
+                    .updatedUser(userNameMap.getOrDefault(item.getUpdatedBy(), ""))
+                    .setIssueNum(issueNumMap.getOrDefault(item.getId(), 0));
+        }
+
+        // 增加优惠券时间过期处理流程
+        if (CollectionUtils.isNotEmpty(updateIds)) {
+
+            ThreadPool.getExecutor().submit(() -> {
+
+                CouponInfoWrapper wrapper = CouponInfoWrapper.builder()
+                        .status(EStatus.DISABLE.getValue())
+                        .build();
+
+                getBaseMapper().update(JSON.parseObject(wrapper.jsonString(), CouponInfo.class),
+                        Wrappers.<CouponInfo>lambdaQuery().in(CouponInfo::getId, updateIds));
+
+            });
+
+        }
+
+        return page.setRecords(wrappers);
+    }
+
+    /**
+     * 优惠券信息
+     *
+     * @param id 优惠券ID
+     * @return CouponInfo
+     */
+    @Override
+    public CouponInfo queryCouponInfoById(Long id) {
+
+        CouponInfo couponInfo = getBaseMapper().selectById(id);
+        if (Objects.isNull(couponInfo)) {
+            throw new BizException("无效的优惠券ID");
+        }
+
+        return couponInfo;
+    }
+
+    /**
+     * 新增或更新优惠券信息
+     *
+     * @param couponInfo CouponInfo
+     * @param stateUpdate 更新标识
+     * @return CouponInfo
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public CouponInfo saveOrUpdateCouponInfo(CouponInfo couponInfo, Boolean stateUpdate) {
+
+        if (Objects.isNull(couponInfo.getId())) {
+
+            // 新增优惠券
+            save(couponInfo);
+
+            // 添加库存变更记录
+            if (couponInfo.getInventory() > 0) {
+
+                CouponInventoryWrapper wrapper = CouponInventoryWrapper.builder()
+                        .userId(couponInfo.getCreatedBy())
+                        .couponId(couponInfo.getId())
+                        .dataType(CouponInventoryEnum.ADDITION)
+                        .number(couponInfo.getInventory())
+                        .remark("")
+                        .status(EStatus.ENABLE.getValue())
+                        .build();
+
+                couponInventoryMapper.insert(JSON.parseObject(wrapper.jsonString(), CouponInventory.class));
+            }
+        } else {
+
+            CouponInfo info = getById(couponInfo.getId());
+            if (Objects.isNull(info)) {
+                throw new BizException("无效的优惠券ID");
+            }
+
+            if (stateUpdate) {
+
+                // 优惠券状态更新
+                if (EStatus.ENABLE.match(couponInfo.getStatus())
+                        && CouponValidTypeEnum.TIME_PERIOD == info.getValidType()
+                        && DateTime.now().getMillis() > info.getEndTime()) {
+                    throw new BizException("已过期优惠券不允许修改");
+                }
+
+            } else {
+
+                // 优惠券已过期
+                if (CouponValidTypeEnum.TIME_PERIOD == info.getValidType()
+                        && DateTime.now().getMillis() > info.getEndTime()) {
+
+                    throw new BizException("已过期优惠券不允许修改");
+                }
+
+                // 优惠券未过期,已发放不允许修复
+                List<Long> couponIds = Lists.newArrayList(info.getId());
+
+                int ret = 0;
+                // 查询优惠券发放人数
+                ret += getBaseMapper().selectCouponIssueStatInfo(couponIds,
+                                CouponInfoQuery.IssueStatQuery.builder()
+                                        .excludeUseState(Lists.newArrayList(CouponUseStateEnum.WITHDRAW)).build()).stream()
+                        .mapToInt(StatGroupWrapper::getTotal).sum();
+                if (ret > 0) {
+                    throw new BizException("已发放优惠券不允许修改");
+                }
+
+                // 关联奖品数
+                ret += getBaseMapper().selectActivityCouponStatInfo(couponIds).stream()
+                        .mapToInt(StatGroupWrapper::getTotal).sum();
+
+                if (ret > 0) {
+                    throw new BizException("已关联奖品优惠券不允许修改");
+                }
+
+                // 启用状态不允许修改
+                if (EStatus.ENABLE.match(info.getStatus())) {
+
+                    throw new BizException("已启用优惠券不允许修改");
+                }
+
+            }
+
+            // 更新优惠券
+            updateById(couponInfo);
+        }
+
+        return getById(couponInfo.getId());
+    }
+
+    /**
+     * 优惠券库存量调整信息
+     *
+     * @param page  IPage<CouponInventoryWrapper>
+     * @param query CouponInventoryQuery
+     * @return IPage<CouponInventoryWrapper>
+     */
+    @Override
+    public IPage<CouponInventoryWrapper> queryCouponInventoryPageInfo(IPage<CouponInventoryWrapper> page, CouponInventoryQuery query) {
+
+        // 优惠券库存调整信息
+        List<CouponInventoryWrapper> wrappers = getBaseMapper().selectCouponInventoryPageInfo(page, query);
+
+        if (CollectionUtils.isEmpty(wrappers)) {
+            // 没有数据,直接返回
+            return page.setRecords(Lists.newArrayList());
+        }
+
+        return page.setRecords(wrappers);
+    }
+
+    /**
+     * 查询订单优惠券信息
+     *
+     * @param userId 用户ID
+     * @param query  CouponInfoQuery.CouponOrderQuery
+     * @return CouponOrderWrapper
+     */
+    @Override
+    public CouponOrderWrapper queryUserOrderCouponInfo(Long userId, CouponInfoQuery.CouponOrderQuery query) {
+
+        CouponOrderWrapper wrapper = CouponOrderWrapper.builder()
+                .discountedPrices(0D)
+                .couponInfos(Lists.newArrayList())
+                .build();
+
+        // 查询用户订单优惠券信息
+        List<CouponOrderWrapper.CouponInfo> couponInfos = getBaseMapper().selectUserOrderCouponInfo(userId, query);
+
+        // 计算抵扣金额
+        double discountPrices = 0;
+        // 订单金额
+        double amount = query.getAmount();
+
+        for (CouponOrderWrapper.CouponInfo item : couponInfos) {
+
+            if (amount >= item.getUseLimit()) {
+
+                // 重置数据选中状态
+                item.setSelected(true);
+                // 重新计算优惠券门槛金额
+                amount += item.getUseLimit();
+                // 叠加优惠券优惠金额
+                discountPrices += item.getDiscountPrice();
+            }
+        }
+
+        return wrapper.discountedPrices(discountPrices).couponInfos(couponInfos);
+    }
+
+    /**
+     * 更新用户订单优惠券状态
+     *
+     * @param wrapper CouponOrderWrapper
+     * @return int 受影响的行数
+     */
+    @Override
+    public int updateUserOrderCouponInfo( CouponOrderWrapper wrapper) {
+
+        // 更新数据信息
+        List<CouponOrderWrapper.CouponIssue> couponIssues = Lists.newArrayList();
+
+        if (wrapper.getReset()) {
+
+            // 重置订单优惠券使用状态
+            List<CouponIssue> issues = couponIssueMapper.selectList(Wrappers.<CouponIssue>lambdaQuery()
+                    .eq(CouponIssue::getOrderNo, wrapper.getOrderNo()));
+
+            for (CouponIssue item : issues) {
+
+                couponIssues.add(CouponOrderWrapper.CouponIssue.builder()
+                        .id(item.getId())
+                        .useState(CouponUseStateEnum.USABLE)
+                        .useTime(0L)
+                        .orderNo("")
+                        .useType(null)
+                        .build());
+            }
+        } else {
+
+            // 更新优惠券为已使用
+            List<CouponOrderWrapper.CouponInfo> couponInfos = wrapper.getCouponInfos().stream()
+                    .filter(CouponOrderWrapper.CouponInfo::getSelected).collect(Collectors.toList());
+
+            for (CouponOrderWrapper.CouponInfo item : couponInfos) {
+
+                couponIssues.add(CouponOrderWrapper.CouponIssue.builder()
+                                .id(item.getIssueId())
+                                .useState(CouponUseStateEnum.USED)
+                                .useTime(DateTime.now().getMillis())
+                                .useType(wrapper.getUseType())
+                                .orderNo(wrapper.getOrderNo())
+                        .build());
+            }
+        }
+
+        for (CouponOrderWrapper.CouponIssue item : couponIssues) {
+
+            couponIssueMapper.updateById(JSON.parseObject(item.jsonString(), CouponIssue.class));
+        }
+
+        return CollectionUtils.size(couponIssues);
+    }
+
+    /**
+     * 更新用户订单优惠券状态
+     * 1、增加批量更新事务边界
+     * @param wrapper CouponOrderWrapper
+     * @return int 受影响的行数
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public int updateCouponOrderInfo(CouponOrderWrapper wrapper) {
+
+        return updateUserOrderCouponInfo(wrapper);
+    }
+
+    /**
+     * 新增优惠券库存量调整信息
+     *
+     * @param inventory CouponInventory
+     * @return int 入库行数
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public int saveOrUpdateCouponInventoryInfo(CouponInventory inventory) {
+
+        // 优惠券信息
+        CouponInfo couponInfo = getBaseMapper().selectById(inventory.getCouponId());
+
+        if (Objects.isNull(couponInfo)) {
+            throw new BizException("无效的优惠券ID");
+        }
+
+        // 比较当前库存量是否有效
+        if (CouponInventoryEnum.REDUCE == inventory.getDataType()
+                && couponInfo.getInventory() < inventory.getNumber()) {
+
+            throw new BizException("当前优惠券库存为: " + couponInfo.getInventory());
+        }
+
+        int ret = couponInfo.getInventory();
+        switch (inventory.getDataType()) {
+            case ADDITION:
+                ret += inventory.getNumber();
+                break;
+            case REDUCE:
+                 ret -= inventory.getNumber();
+                break;
+            default:
+                break;
+        }
+
+        // 调整优惠券库存量信息
+        CouponInfoWrapper wrapper = CouponInfoWrapper.builder()
+                .id(couponInfo.getId())
+                .inventory(ret)
+                .updatedBy(inventory.getUserId())
+                .updateTime(DateTime.now().getMillis())
+                .build();
+        getBaseMapper().updateById(JSON.parseObject(wrapper.jsonString(), CouponInfo.class));
+
+        // 新增优惠券库存量记录
+        return couponInventoryMapper.insert(inventory);
+    }
+
+    @Override
+    public int updateStock(Long couponId, int size, InOrOutEnum inOrOut) {
+        return baseMapper.updateStock(couponId,size,inOrOut);
+    }
+}

+ 20 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CouponInventoryServiceImp.java

@@ -0,0 +1,20 @@
+package com.yonge.cooleshow.biz.dal.service.impl;
+
+import com.yonge.cooleshow.biz.dal.entity.CouponInventory;
+import com.yonge.cooleshow.biz.dal.mapper.CouponInventoryMapper;
+import com.yonge.cooleshow.biz.dal.service.CouponInventoryService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 优惠券库存变化 服务实现类
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-05
+ */
+@Service
+public class CouponInventoryServiceImp extends ServiceImpl<CouponInventoryMapper, CouponInventory> implements CouponInventoryService {
+
+}

+ 298 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CouponIssueServiceImp.java

@@ -0,0 +1,298 @@
+package com.yonge.cooleshow.biz.dal.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.biz.dal.dto.UserParam;
+import com.yonge.cooleshow.biz.dal.entity.CouponInfo;
+import com.yonge.cooleshow.biz.dal.entity.CouponIssue;
+import com.yonge.cooleshow.biz.dal.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
+import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.SendTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.cooleshow.biz.dal.mapper.CouponIssueMapper;
+import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponIssueQueryInfo;
+import com.yonge.cooleshow.biz.dal.service.CouponInfoService;
+import com.yonge.cooleshow.biz.dal.service.CouponIssueService;
+import com.yonge.cooleshow.biz.dal.service.SysMessageService;
+import com.yonge.cooleshow.biz.dal.vo.CouponIssueUserVo;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponIssueWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
+import com.yonge.cooleshow.common.enums.EStatus;
+import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.thirdparty.message.MessageSenderPluginContext;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 优惠券发放 服务实现类
+ * </p>
+ *
+ * @author Eric
+ * @since 2022-09-02
+ */
+@Service
+public class CouponIssueServiceImp extends ServiceImpl<CouponIssueMapper, CouponIssue> implements CouponIssueService {
+
+    private static final Logger log = LoggerFactory.getLogger(CouponIssueServiceImp.class);
+    @Autowired
+    private CouponInfoService couponInfoService;
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    @Autowired
+    private SysUserFeignService sysUserFeignService;
+
+    @Autowired
+    private SysMessageService sysMessageService;
+
+
+    @Override
+    public IPage<CouponIssueWrapper> queryCouponIssueInfo(IPage<CouponIssueWrapper> page, CouponIssueQueryInfo query) {
+        List<CouponIssueWrapper> couponIssueWrappers = baseMapper.queryCouponIssueInfo(page, query);
+
+
+        // 优惠券过期状态
+        couponIssueWrappers = couponIssueWrappers.stream()
+                .peek(x -> {
+                    if (StringUtils.isEmpty(x.getOrderNo())
+                            && CouponUseStateEnum.WITHDRAW != x.getUseState()
+                            && x.getEndTime().compareTo(query.getExpiredTime()) <0) {
+                        x.setUseState(CouponUseStateEnum.EXPIRED);
+                    }
+                }).collect(Collectors.toList());
+
+        // 查看人员信息
+        List<Long> userIdList = couponIssueWrappers.stream()
+                                                     .map(CouponIssueWrapper::getUserId)
+                                                     .collect(Collectors.toList());
+        List<SysUser> userList = new ArrayList<>();
+        if (!CollectionUtils.isEmpty(userIdList)) {
+            userList = sysUserMapper.selectBatchIds(userIdList);
+        }
+        Map<Long, List<SysUser>> userMap = userList.stream().collect(Collectors.groupingBy(SysUser::getId));
+
+
+        // 获取发放人信息
+        List<Long> issuerIdList = couponIssueWrappers.stream()
+                                                .map(CouponIssueWrapper::getIssuer)
+                                                .collect(Collectors.toList());
+        List<SysUser> issuerList = new ArrayList<>();
+        if (!CollectionUtils.isEmpty(issuerIdList)) {
+            issuerList = sysUserMapper.selectBatchIds(issuerIdList);
+        }
+
+        Map<Long, List<SysUser>> issuerMap = issuerList.stream().collect(Collectors.groupingBy(SysUser::getId));
+
+
+
+        // 可用优惠券数量
+        long count  = 0 ;
+        if (query.getAmount() != null) {
+
+             count= couponIssueWrappers.stream()
+                                            .filter(couponIssueWrapper -> couponIssueWrapper.getUseLimit()
+                                                                                            .compareTo(query.getAmount()) <= 0)
+                                            .count();
+        }
+        for (CouponIssueWrapper couponIssueWrapper : couponIssueWrappers) {
+            List<SysUser> issuer = issuerMap.get(couponIssueWrapper.getIssuer());
+            if (!CollectionUtils.isEmpty(issuer)) {
+                couponIssueWrapper.setIssueRealName(issuer.get(0).getRealName());
+                couponIssueWrapper.setIssueUsername(issuer.get(0).getUsername());
+            }
+
+            List<SysUser> users = userMap.get(couponIssueWrapper.getUserId());
+            if (!CollectionUtils.isEmpty(issuer)) {
+                couponIssueWrapper.setUsername(users.get(0).getUsername());
+                couponIssueWrapper.setRealName(users.get(0).getRealName());
+                couponIssueWrapper.setPhone(users.get(0).getPhone());
+                couponIssueWrapper.setUsable(count);
+            }
+        }
+
+        return page.setRecords(couponIssueWrappers);
+
+
+    }
+
+    @Override
+    @Transactional
+    public Boolean withdrawCoupon(Long couponIssueId) {
+
+        // 测回退库存
+        CouponIssue couponIssue = getById(couponIssueId);
+        couponInfoService.updateStock(couponIssue.getCouponId(),1,InOrOutEnum.IN);
+
+        return this.lambdaUpdate()
+                .eq(CouponIssue::getId,couponIssueId)
+                .set(CouponIssue::getStatus, EStatus.DISABLE.getValue())
+                .set(CouponIssue::getUseState,CouponUseStateEnum.WITHDRAW)
+                .update();
+    }
+
+    @Override
+    @Transactional
+    public void issueCoupon(Long couponId, List<UserParam> userParam, Long issuer, String reason) {
+        CouponInfo couponInfo = couponInfoService.queryCouponInfoById(couponId);
+        if (couponInfo == null) {
+            throw new BizException("未找到优惠券信息");
+        }
+        if (couponInfo.getInventory() < userParam.size()) {
+            throw new BizException("库存不足");
+        }
+        if (couponInfo.getStatus() == EStatus.DISABLE.getValue()) {
+            throw new BizException("优惠券已被禁用");
+        }
+        // 判断优惠券类型 设置优惠券时间
+        Long startTime = null;
+        Long endTime = null;
+        if (couponInfo.getValidDay() != null && couponInfo.getValidDay() >0) {
+            startTime =LocalDateTime.of(LocalDate.now(), LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+            endTime = LocalDateTime.of(LocalDate.now().plusDays(couponInfo.getValidDay()-1), LocalTime.MAX)
+                                   .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+        } else {
+            startTime = couponInfo.getStartTime();
+            endTime = couponInfo.getEndTime();
+        }
+        List<CouponIssue> couponIssueList = new ArrayList<>();
+        for (UserParam param : userParam) {
+            if (param.getUserId() == null) {
+                throw new BizException("发放用户id不能为空");
+            }
+            if (param.getClientType() == null) {
+                throw new BizException("发放客户端不能为空");
+            }
+            CouponIssue couponIssue = new CouponIssue();
+            couponIssue.setCouponId(couponId);
+            couponIssue.setIssuer(issuer);
+            couponIssue.setIssueTime(DateTime.now().getMillis());
+            couponIssue.setIssueWay(SendTypeEnum.PLATFORM.getCode());
+            couponIssue.setRemark(reason);
+            couponIssue.setStartTime(startTime);
+            couponIssue.setEndTime(endTime);
+            couponIssue.setUseState(CouponUseStateEnum.USABLE);
+            couponIssue.setClientType(param.getClientType().getCode());
+            couponIssue.setUserId(param.getUserId());
+            couponIssue.setStatus(1);
+            couponIssueList.add(couponIssue);
+        }
+        this.saveBatch(couponIssueList);
+
+        //  优惠券扣减库存
+        int i = couponInfoService.updateStock(couponId, userParam.size(), InOrOutEnum.OUT);
+        if (i == 0) {
+            throw new BizException("库存不足");
+        }
+
+        // 发送消息
+        sendMessage(couponInfo.getName(),userParam);
+    }
+
+    private void sendMessage(String couponName,List<UserParam> userParams) {
+        for (UserParam userParam : userParams) {
+
+            try {
+                com.yonge.cooleshow.auth.api.entity.SysUser sysUser = getSysUser(userParam.getUserId());
+                // 推送老师
+                Map<Long, String> teacherReceivers = new HashMap<>();
+                teacherReceivers.put(sysUser.getId(), sysUser.getPhone());
+
+                sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.COUPON_ISSUE, teacherReceivers, null,
+                                                   0, null, userParam.getClientType().getCode(),couponName);
+
+            } catch (Exception e) {
+                log.error("发放优惠券消息发送失败 -->{}",e.fillInStackTrace());
+            }
+
+        }
+    }
+
+    private com.yonge.cooleshow.auth.api.entity.SysUser getSysUser(Long userId) {
+        return Optional.ofNullable(userId)
+                       .map(sysUserFeignService::queryUserById)
+                       .orElseThrow(() -> new BizException("用户不存在"));
+    }
+
+    @Override
+    public IPage<CouponIssueUserVo> queryUser(IPage<CouponIssueUserVo> iPage, Long couponId, ClientEnum client, String keyword) {
+        CouponInfo couponInfo = couponInfoService.getById(couponId);
+        if (couponInfo == null) {
+            throw new BizException("未找到优惠券信息");
+        }
+        if (client == null) {
+            client = couponInfo.getClientType();
+        }
+
+        List<CouponIssueUserVo> list = sysUserMapper.queryCouponIssueUser(iPage,client, keyword);
+
+        return iPage.setRecords(list);
+
+
+    }
+
+    /**
+     * 统计用户优惠券状态
+     *
+     * @param id         用户ID
+     * @param query     CouponInfoQuery.CouponStateStatQuery
+     * @return List<CouponInfoWrapper.CouponStat>
+     */
+    @Override
+    public List<CouponInfoWrapper.CouponStat> queryCouponStateStatInfo(Long id, CouponInfoQuery.CouponStateStatQuery query) {
+
+        List<CouponInfoWrapper.CouponStat> couponStats = Lists.newCopyOnWriteArrayList();
+
+        // 优惠券统计状态
+        /*Map<String, Integer> collect = getBaseMapper().selectCouponStateStatInfo(id, query).stream()
+                .collect(Collectors.toMap(StatGroupWrapper::getGid, StatGroupWrapper::getTotal, (o, n) -> n));
+
+        CouponUseStateEnum[] values = CouponUseStateEnum.values();
+        for (CouponUseStateEnum item : values) {
+
+            couponStats.add(CouponInfoWrapper.CouponStat.builder()
+                    .useState(item)
+                    .total(collect.getOrDefault(item.getCode(), 0))
+                    .build());
+        }*/
+
+        // 分享统计优惠状态数
+        Lists.newArrayList(CouponUseStateEnum.values()).parallelStream().forEach(item -> {
+
+            CouponInfoQuery.CouponStateStatQuery queryParam = CouponInfoQuery.CouponStateStatQuery.from(query.jsonString())
+                    .timestamp(DateTime.now().getMillis())
+                    .userState(item);
+
+            int sum = getBaseMapper().selectCouponStateStatInfo(id, queryParam).stream()
+                    .mapToInt(StatGroupWrapper::getTotal).sum();
+
+            couponStats.add(CouponInfoWrapper.CouponStat.builder().useState(item).total(sum).build());
+        });
+
+        return couponStats;
+    }
+}

+ 16 - 7
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseGroupServiceImpl.java

@@ -22,6 +22,7 @@ import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.biz.dal.support.WrapperUtil;
 import com.yonge.cooleshow.biz.dal.vo.*;
 import com.yonge.cooleshow.biz.dal.vo.res.OrderCreateRes;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponOrderWrapper;
 import com.yonge.cooleshow.common.constant.SysConfigConstant;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.cooleshow.common.enums.PostStatusEnum;
@@ -100,6 +101,9 @@ public class CourseGroupServiceImpl extends ServiceImpl<CourseGroupDao, CourseGr
     @Autowired
     private TeacherService teacherService;
 
+    @Autowired
+    private CouponInfoService couponInfoService;
+
     @Override
     public CourseGroupDao getDao() {
         return this.baseMapper;
@@ -722,7 +726,7 @@ public class CourseGroupServiceImpl extends ServiceImpl<CourseGroupDao, CourseGr
         //校验购买的课程组每节课时间是否和自己的课时冲突
         batchCheckStudentCourseTime(studentId, courseList, CourseSchedule::getStartTime, CourseSchedule::getEndTime);
         //写入学生购买课程记录
-        buyLiveCourseAfter(orderReqInfo.getOrderNo(), studentId, courseList, courseGroup);
+        buyLiveCourseAfter(orderReqInfo.getOrderNo(), studentId, courseList, courseGroup,orderReqInfo.getActualPrice());
 
         OrderCreateRes orderCreateRes = new OrderCreateRes();
         orderCreateRes.setRes(true);
@@ -741,16 +745,17 @@ public class CourseGroupServiceImpl extends ServiceImpl<CourseGroupDao, CourseGr
 
     /**
      * 待订单创建完毕后继续后续操作
-     *
-     * @param orderNo     订单号
-     * @param courseList  课程列表
+     *  @param orderNo     订单号
      * @param studentId   学员id
+     * @param courseList  课程列表
      * @param courseGroup 直播课程组信息
+     * @param actualPrice
      */
-    private void buyLiveCourseAfter(String orderNo, Long studentId, List<CourseSchedule> courseList, CourseGroup courseGroup) {
+    private void buyLiveCourseAfter(String orderNo, Long studentId, List<CourseSchedule> courseList, CourseGroup courseGroup, BigDecimal actualPrice) {
         log.info("buyLiveCourse buyLiveCourseAfter -> order {} studentId {} courseList {} courseGroup {}", orderNo, studentId, JSON.toJSONString(courseList), JSON.toJSONString(courseGroup));
         //获取每节课购买的价格
         Map<Integer, BigDecimal> courseAveragePrice = WrapperUtil.getAveragePrice(courseGroup.getCourseNum(), courseGroup.getCoursePrice());
+        Map<Integer, BigDecimal> courseAverageActualPrice = WrapperUtil.getAveragePrice(courseGroup.getCourseNum(),actualPrice);
         //写course_schedule_student_payment表 作为记录锁定时间用,防止重复购买,如果支付失败则删除该数据
         List<CourseScheduleStudentPayment> studentPaymentList = new ArrayList<>();
         Date now = new Date();
@@ -761,10 +766,11 @@ public class CourseGroupServiceImpl extends ServiceImpl<CourseGroupDao, CourseGr
             studentPayment.setCourseGroupId(courseGroup.getId());
             studentPayment.setCourseId(course.getId());
             studentPayment.setOrderNo(orderNo);
-            BigDecimal amount = courseAveragePrice.get(i.getAndIncrement());
+            BigDecimal amount = courseAveragePrice.get(i.get());
+            BigDecimal actualAmount = courseAverageActualPrice.get(i.getAndIncrement());
             studentPayment.setOriginalPrice(amount);
             studentPayment.setExpectPrice(amount);
-            studentPayment.setActualPrice(amount);
+            studentPayment.setActualPrice(actualAmount);
             studentPayment.setCreatedTime(now);
             studentPayment.setUpdatedTime(now);
             studentPayment.setCourseType(courseGroup.getType());
@@ -1206,6 +1212,9 @@ public class CourseGroupServiceImpl extends ServiceImpl<CourseGroupDao, CourseGr
         for (String orderNo : orderNoList) {
             try {
                 userOrderRefundService.orderRefund(orderNo, "直播课成课失败退款");
+
+                //退还优惠券
+                couponInfoService.updateUserOrderCouponInfo(CouponOrderWrapper.builder().orderNo(orderNo).reset(true).build());
             } catch (Exception e) {
                 log.warn("直播课成课失败退款 退款失败,退款订单号 {}", orderNo);
                 log.error("直播课成课失败退款 退款失败", e.getCause());

+ 2 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseScheduleServiceImpl.java

@@ -965,7 +965,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
         BigDecimal decimal = new BigDecimal(courseNum);//选购课程节数
         BigDecimal multiply = price.multiply(decimal);//预计总价
         if (multiply.compareTo(scheduleDto.getCoursePrice()) != 0) {
-            throw new BizException("价格异常。预计价格:{},实际价格:{}", multiply, scheduleDto.getCoursePrice());
+            throw new BizException("价格异常。预计价格:{},实际价格:{}", multiply, orderReqInfo.getActualPrice());
         }
 
         //获取老师锁课缓存
@@ -1001,7 +1001,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
         BigDecimal subjectPrice = teacherSubjectPrice.getSubjectPrice();
 
         //每课实际价格
-        Map<Integer, BigDecimal> courseAveragePrice = WrapperUtil.getAveragePrice(scheduleDto.getCourseNum(), scheduleDto.getCoursePrice());
+        Map<Integer, BigDecimal> courseAveragePrice = WrapperUtil.getAveragePrice(scheduleDto.getCourseNum(), orderReqInfo.getActualPrice());
 
         List<CourseScheduleDate> classTime = scheduleDto.getClassTime();
         for (int i = 0; i < classTime.size(); i++) {

+ 17 - 8
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/MusicSheetServiceImpl.java

@@ -532,13 +532,16 @@ public class MusicSheetServiceImpl extends ServiceImpl<MusicSheetDao, MusicSheet
 
         }
 
-        if (musicSheetDto.getId() != null && musicSheetDto.getSourceType().equals(SourceTypeEnum.PLATFORM)) {
+        if (musicSheetDto.getId() != null) {
             musicSheetAccompanimentService.delByMusicSheetId(musicSheetDto.getId());
         }
 
         if (musicSheetDto.getAuditVersion() == null) {
             musicSheetDto.setAuditVersion(YesOrNoEnum.NO);
         }
+        if (musicSheetDto.getChargeType().equals(ChargeTypeEnum.VIP) || musicSheetDto.getChargeType().equals(ChargeTypeEnum.FREE)) {
+            musicSheetDto.setMusicPrice(BigDecimal.ZERO);
+        }
 
         // 删除曲目专辑关联
         if (oldMusicSheet != null && !oldMusicSheet.getMusicSubject().equals(musicSheetDto.getMusicSubject())) {
@@ -647,16 +650,24 @@ public class MusicSheetServiceImpl extends ServiceImpl<MusicSheetDao, MusicSheet
         BigDecimal serviceFee = new BigDecimal(musicSheetServiceFee).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
         //支付金额
         BigDecimal actualPrice = userOrderDetailVo.getActualPrice();
+        BigDecimal expectPrice = userOrderDetailVo.getExpectPrice();
 
-        //服务费
-        BigDecimal serviceFeeAmount = actualPrice.multiply(serviceFee).setScale(2, RoundingMode.HALF_UP);
+        //服务费  原价的平台服务费 ,减去优惠券金额
+        BigDecimal serviceFeeAmount = expectPrice.multiply(serviceFee).subtract(userOrderDetailVo.getCouponAmount()).setScale(2, RoundingMode.HALF_UP);
+        if (serviceFeeAmount.compareTo(BigDecimal.ZERO) <0) {
+            serviceFeeAmount = BigDecimal.ZERO;
+        }
 
         // 保存购买记录
         addMusicSheetPurchaseRecord(userOrderDetailVo, actualPrice, serviceFeeAmount);
 
         if (!musicSheet.getSourceType().getCode().equals(SourceTypeEnum.PLATFORM.getCode())) {
+
+            // 老师收入
+            BigDecimal teacherAmount = BigDecimal.ONE.subtract(serviceFee).multiply(expectPrice);
+
             // 加入账户
-            addTeacherAccount(userOrderDetailVo, actualPrice, serviceFeeAmount, userOrderDetailVo.getBizId());
+            addTeacherAccount(userOrderDetailVo, teacherAmount, userOrderDetailVo.getBizId());
         }
 
         // 消息通知
@@ -751,16 +762,14 @@ public class MusicSheetServiceImpl extends ServiceImpl<MusicSheetDao, MusicSheet
      * 添加金额到老师账户
      *
      * @param userOrderDetailVo 订单信息
-     * @param actualPrice       付款价格
-     * @param serviceFeeAmount  服务费
+     * @param transAmount       老师收入
      * @param musicSheetId      曲目id
      */
-    private void addTeacherAccount(UserOrderDetailVo userOrderDetailVo, BigDecimal actualPrice, BigDecimal serviceFeeAmount,
+    private void addTeacherAccount(UserOrderDetailVo userOrderDetailVo, BigDecimal transAmount,
                                    Long musicSheetId) {
         //获取账期时间
         Date accountPeriodTime = userOrderService.getAccountPeriodTime(userOrderDetailVo);
 
-        BigDecimal transAmount = actualPrice.subtract(serviceFeeAmount).setScale(2, RoundingMode.HALF_UP);
         //插入老师账户变更记录-老师预收
         userAccountService.accountRecord(
                 new UserAccountRecordDto(userOrderDetailVo.getMerchId(), PostStatusEnum.WAIT, transAmount, InOrOutEnum.IN,

+ 91 - 10
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/UserOrderServiceImpl.java

@@ -3,18 +3,49 @@ package com.yonge.cooleshow.biz.dal.service.impl;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
 import com.yonge.cooleshow.biz.dal.dao.UserOrderDao;
 import com.yonge.cooleshow.biz.dal.dto.UserAccountRecordDto;
 import com.yonge.cooleshow.biz.dal.dto.req.OrderPayReq;
 import com.yonge.cooleshow.biz.dal.dto.req.OrderReq;
 import com.yonge.cooleshow.biz.dal.dto.search.OrderSearch;
-import com.yonge.cooleshow.biz.dal.entity.*;
-import com.yonge.cooleshow.biz.dal.enums.*;
-import com.yonge.cooleshow.biz.dal.service.*;
+import com.yonge.cooleshow.biz.dal.entity.ActivityPlan;
+import com.yonge.cooleshow.biz.dal.entity.ActivityRegistration;
+import com.yonge.cooleshow.biz.dal.entity.CourseGroup;
+import com.yonge.cooleshow.biz.dal.entity.PlatformCashAccountRecord;
+import com.yonge.cooleshow.biz.dal.entity.UserAccountRecord;
+import com.yonge.cooleshow.biz.dal.entity.UserOrder;
+import com.yonge.cooleshow.biz.dal.entity.UserOrderPayment;
+import com.yonge.cooleshow.biz.dal.enums.AccountBizTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.GoodTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
+import com.yonge.cooleshow.biz.dal.enums.OrderStatusEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.service.ActivityEvaluationRecordService;
+import com.yonge.cooleshow.biz.dal.service.ActivityPlanService;
+import com.yonge.cooleshow.biz.dal.service.ActivityRegistrationService;
+import com.yonge.cooleshow.biz.dal.service.ActivityUserRewardService;
+import com.yonge.cooleshow.biz.dal.service.CouponInfoService;
+import com.yonge.cooleshow.biz.dal.service.CourseGroupService;
+import com.yonge.cooleshow.biz.dal.service.CourseScheduleService;
+import com.yonge.cooleshow.biz.dal.service.MemberPriceSettingsService;
+import com.yonge.cooleshow.biz.dal.service.MusicSheetService;
+import com.yonge.cooleshow.biz.dal.service.PianoRoomBuyRecordService;
+import com.yonge.cooleshow.biz.dal.service.PlatformCashAccountRecordService;
+import com.yonge.cooleshow.biz.dal.service.SysConfigService;
+import com.yonge.cooleshow.biz.dal.service.UserAccountService;
+import com.yonge.cooleshow.biz.dal.service.UserOrderDetailService;
+import com.yonge.cooleshow.biz.dal.service.UserOrderPaymentService;
+import com.yonge.cooleshow.biz.dal.service.UserOrderService;
+import com.yonge.cooleshow.biz.dal.service.VideoLessonPurchaseRecordService;
 import com.yonge.cooleshow.biz.dal.vo.UserOrderDetailVo;
 import com.yonge.cooleshow.biz.dal.vo.UserOrderVo;
 import com.yonge.cooleshow.biz.dal.vo.res.OrderCreateRes;
 import com.yonge.cooleshow.biz.dal.vo.res.OrderPayRes;
+import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponOrderWrapper;
 import com.yonge.cooleshow.common.constant.SysConfigConstant;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.cooleshow.common.enums.ActivityResourceEnum;
@@ -33,9 +64,10 @@ import com.yonge.toolset.payment.base.model.callback.PaymentCallBack;
 import com.yonge.toolset.payment.core.props.PaymentProperties;
 import com.yonge.toolset.payment.core.service.PaymentClient;
 import com.yonge.toolset.payment.util.DistributedLock;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.joda.time.DateTime;
 import org.redisson.api.RedissonClient;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -45,14 +77,20 @@ import javax.annotation.PostConstruct;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.LocalDateTime;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
+@Slf4j
 @Service
 public class UserOrderServiceImpl extends ServiceImpl<UserOrderDao, UserOrder> implements UserOrderService {
-    private final static Logger log = LoggerFactory.getLogger(UserOrderServiceImpl.class);
 
     @Autowired
     private MemberPriceSettingsService memberPriceSettingsService;
@@ -96,6 +134,8 @@ public class UserOrderServiceImpl extends ServiceImpl<UserOrderDao, UserOrder> i
 
     @Autowired
     private ActivityUserRewardService activityUserRewardService;
+    @Autowired
+    private CouponInfoService couponInfoService;
 
     //验证订单是否可以下单,获取订单金额信息
     private static final Map<GoodTypeEnum, Function<OrderReq.OrderReqInfo, HttpResponseResult<OrderCreateRes>>> orderCreate = new HashMap<>();
@@ -188,6 +228,15 @@ public class UserOrderServiceImpl extends ServiceImpl<UserOrderDao, UserOrder> i
                 o.setPlantformFeeRate(null);
             });
             userOrderVo.setOrderDetailList(orderDetilList);
+
+            // 计算优惠券金额
+            CouponOrderWrapper couponOrderWrapper  = couponInfoService.queryUserOrderCouponInfo(param.getUserId(),
+                    CouponInfoQuery.CouponOrderQuery.builder()
+                            .clientType(userOrderVo.getOrderClient())
+                            .orderNo(param.getOrderNo())
+                            .amount(userOrderVo.getExpectPrice().doubleValue())
+                            .build());
+            userOrderVo.setDiscountPrice(BigDecimal.valueOf(couponOrderWrapper.getDiscountedPrices()));
         }
         return userOrderVo;
     }
@@ -287,6 +336,9 @@ public class UserOrderServiceImpl extends ServiceImpl<UserOrderDao, UserOrder> i
 
         //验证参数,必须验证参数
         BigDecimal actualPrice = BigDecimal.ZERO;
+        // 订单优惠券信息
+        CouponOrderWrapper couponOrderWrapper = CouponOrderWrapper.builder().couponInfos(Lists.newArrayList()).build();
+
         for (OrderReq.OrderReqInfo info : orderReq.getOrderInfos()) {
             Function<OrderReq.OrderReqInfo, HttpResponseResult<OrderCreateRes>> createFunction = orderCreate.get(info.getGoodType());
             if (Objects.isNull(createFunction)) {
@@ -298,7 +350,7 @@ public class UserOrderServiceImpl extends ServiceImpl<UserOrderDao, UserOrder> i
             info.setUserId(orderReq.getUserId());
             info.setRecomUserId(orderReq.getRecomUserId());
             info.setActivityId(orderReq.getActivityId());
-
+            info.setActualPrice(orderReq.getActualPrice());
             HttpResponseResult<OrderCreateRes> createResult = createFunction.apply(info);
             OrderCreateRes createRes = createResult.getData();
             if (!createResult.getStatus() || null == createRes || !createRes.getRes()) {
@@ -306,11 +358,27 @@ public class UserOrderServiceImpl extends ServiceImpl<UserOrderDao, UserOrder> i
                 return HttpResponseResult.failed(StringUtil.isEmpty(createResult.getMsg()) ? "订单商品信息获取失败" : createResult.getMsg());
             }
             createRes.setGoodType(info.getGoodType());
-            //todo 优惠券优惠金额,暂时为0
             BigDecimal couponAmount = BigDecimal.ZERO;
             if (createRes.getCouponAmount() != null) {
                 couponAmount = createRes.getCouponAmount();
             }
+
+            // 计算优惠券金额
+            couponOrderWrapper = couponInfoService.queryUserOrderCouponInfo(orderReq.getUserId(),
+                    CouponInfoQuery.CouponOrderQuery.builder()
+                            .clientType(orderReq.getOrderClient())
+                            .couponTypes(CouponCategoryEnum.getCategory(orderReq.getOrderType().getCode()))
+                            .useState(CouponUseStateEnum.USABLE)
+                            .timestamp(DateTime.now().getMillis())
+                            .amount(createRes.getExpectPrice().doubleValue())
+                            .build()
+                            .issueIds(orderReq.getCouponId()));
+
+            //log.info("executeOrder couponId={}, wrapper={}", orderReq.getCouponId(), couponOrderWrapper.jsonString());
+
+            // 优惠券优惠金额,暂时为0
+            couponAmount =couponAmount.add(BigDecimal.valueOf(couponOrderWrapper.getDiscountedPrices()));
+
             if (couponAmount.compareTo(createRes.getExpectPrice()) > 0 ){
                 couponAmount = createRes.getExpectPrice();
             }
@@ -327,12 +395,19 @@ public class UserOrderServiceImpl extends ServiceImpl<UserOrderDao, UserOrder> i
         //验证金额
         if (actualPrice.setScale(2, RoundingMode.HALF_UP)
                 .compareTo(orderReq.getActualPrice().setScale(2, RoundingMode.HALF_UP)) != 0) {
-            return HttpResponseResult.failed("订单金额校验不通过");
+            throw new BizException("订单金额校验不通过");
         }
 
         //验证成功后,订单入库
         UserOrderVo orderVo = insertOrder(orderReq);
 
+        // 更新用户订单优惠券使用状态
+        if (CollectionUtils.isNotEmpty(couponOrderWrapper.getCouponInfos())) {
+
+            couponOrderWrapper.useType(CouponCategoryEnum.goodTypeTo(orderReq.getOrderType().getCode()));
+            couponInfoService.updateUserOrderCouponInfo(couponOrderWrapper.orderNo(orderReq.getOrderNo()));
+        }
+
         List<UserOrderDetailVo> orderDetailList = orderVo.getOrderDetailList();
         for (UserOrderDetailVo orderDetailVo : orderDetailList) {
             orderDetailVo.setUserId(orderReq.getUserId());
@@ -732,6 +807,12 @@ public class UserOrderServiceImpl extends ServiceImpl<UserOrderDao, UserOrder> i
         userOrder.setStatus(orderStatus);
         userOrder.setUpdateTime(new Date());
         baseMapper.updateById(userOrder);
+
+        // 重置优惠券状态
+        couponInfoService.updateUserOrderCouponInfo(CouponOrderWrapper.builder()
+                        .orderNo(userOrder.getOrderNo())
+                        .reset(true)
+                .build());
     }
 
     private void orderSuccess(UserOrderVo detail) {

+ 4 - 3
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/VideoLessonPurchaseRecordServiceImpl.java

@@ -102,6 +102,7 @@ public class VideoLessonPurchaseRecordServiceImpl extends ServiceImpl<VideoLesso
         purchaseRecord.setOrderStatus(OrderStatusEnum.WAIT_PAY.getCode());
         purchaseRecord.setOriginalPrice(lessonGroup.getLessonPrice());//原价
         purchaseRecord.setExpectPrice(lessonGroup.getLessonPrice());//预计价格
+        purchaseRecord.setPayMoney(orderReqInfo.getActualPrice());
         videoLessonPurchaseRecordDao.insert(purchaseRecord);
 
         OrderCreateRes orderCreateRes = new OrderCreateRes();
@@ -163,11 +164,11 @@ public class VideoLessonPurchaseRecordServiceImpl extends ServiceImpl<VideoLesso
         BigDecimal liveServiceRate = new BigDecimal(sysConfigService.findConfigValue(SysConfigConstant.VIDEO_LESSON_SERVICE_FEE))
                 .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
         //支付金额
-        BigDecimal actualPrice = userOrderDetailVo.getActualPrice();
+        BigDecimal expectPrice = userOrderDetailVo.getExpectPrice();
         //服务费
-        BigDecimal serviceFeeAmount = actualPrice.multiply(liveServiceRate).setScale(2, RoundingMode.HALF_UP);
+        BigDecimal serviceFeeAmount = expectPrice.multiply(liveServiceRate).setScale(2, RoundingMode.HALF_UP);
         //老师入账金额
-        BigDecimal transAmount = actualPrice.subtract(serviceFeeAmount).setScale(2, RoundingMode.HALF_UP);
+        BigDecimal transAmount = expectPrice.subtract(serviceFeeAmount).setScale(2, RoundingMode.HALF_UP);
         //插入老师账户变更记录-老师预收
         userAccountService.accountRecord(
                 new UserAccountRecordDto(userOrderDetailVo.getMerchId(), PostStatusEnum.WAIT, transAmount, InOrOutEnum.IN,

+ 70 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/CouponIssueUserVo.java

@@ -0,0 +1,70 @@
+package com.yonge.cooleshow.biz.dal.vo;
+
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2022-09-05
+ */
+@ApiModel("发放优惠券用户分页数据")
+public class CouponIssueUserVo {
+
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+    @ApiModelProperty("实名")
+    private String realName;
+
+    @ApiModelProperty("昵称")
+    private String username;
+
+    @ApiModelProperty("客户端")
+    private ClientEnum clientType;
+
+    @ApiModelProperty("手机号")
+    private String phone;
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getRealName() {
+        return realName;
+    }
+
+    public void setRealName(String realName) {
+        this.realName = realName;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public ClientEnum getClientType() {
+        return clientType;
+    }
+
+    public void setClientType(ClientEnum clientType) {
+        this.clientType = clientType;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+}

+ 11 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/UserOrderVo.java

@@ -37,6 +37,9 @@ public class UserOrderVo extends UserOrder {
 	@ApiModelProperty(value = "手机号")
 	private String phone;
 
+	@ApiModelProperty("优惠券抵扣金额 ")
+	private BigDecimal discountPrice;
+
 	public List<UserOrderDetailVo> getOrderDetailList() {
 		return orderDetailList;
 	}
@@ -108,4 +111,12 @@ public class UserOrderVo extends UserOrder {
 	public void setPaymentClient(String paymentClient) {
 		this.paymentClient = paymentClient;
 	}
+
+	public BigDecimal getDiscountPrice() {
+		return discountPrice;
+	}
+
+	public void setDiscountPrice(BigDecimal discountPrice) {
+		this.discountPrice = discountPrice;
+	}
 }

+ 75 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/coupon/CouponInfoWrapper.java

@@ -0,0 +1,75 @@
+package com.yonge.cooleshow.biz.dal.vo.coupon;
+
+import com.alibaba.fastjson.JSON;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponValidTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ *  优惠券信息
+ * Created by Eric.Shang on 2022/9/2.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CouponInfoWrapper implements Serializable {
+
+    private Long id;
+    private String name;
+    private String clientType;
+    private String category;
+    private String couponType;
+    private Integer inventory;
+    private Integer status;
+    private Long updatedBy;
+    private Long updateTime;
+    private Long createdBy;
+    private Date createdTime;
+
+    private CouponValidTypeEnum validType;
+    private Long endTime;
+
+    // 发放领取数
+    private Integer issueNum;
+    // 关联奖品数
+    private Integer rewardNum;
+    // 更新用户
+    private String updatedUser;
+
+    public String jsonString() {
+        return JSON.toJSONString(this);
+    }
+
+    public CouponInfoWrapper rewardNum(Integer rewardNum) {
+        this.rewardNum = rewardNum;
+        return this;
+    }
+
+    public CouponInfoWrapper updatedUser(String updateUser) {
+        this.updatedUser = updateUser;
+        return this;
+    }
+
+    /**
+     * 优惠券状态统计
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponStat implements Serializable {
+
+        // 优惠券统计状态
+        private CouponUseStateEnum useState;
+        // 统计数据
+        private Integer total;
+
+    }
+}

+ 297 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/coupon/CouponIssueWrapper.java

@@ -0,0 +1,297 @@
+package com.yonge.cooleshow.biz.dal.vo.coupon;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.SendTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.math.BigDecimal;
+import java.util.Optional;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2022-09-05
+ */
+public class CouponIssueWrapper {
+
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+    @ApiModelProperty("优惠券发放id")
+    private Long couponIssueId;
+
+    @ApiModelProperty("优惠券id")
+    private Long couponId;
+
+    @ApiModelProperty("使用类型")
+    private CouponCategoryEnum useType;
+
+    @ApiModelProperty("优惠券名称")
+    private String couponName;
+
+    @ApiModelProperty("优惠券类型 ")
+    private CouponTypeEnum couponType;
+
+    @ApiModelProperty("可用品类 UNIVERSAL:全场通用 VIP:小酷Ai PIANO:云酷琴房 MALL:商场购物券 MUSIC:单曲点播券 SPARRING:陪练课购买券 LIVE:直播课购买券  VIDEO:视频课购买券")
+    private CouponCategoryEnum couponCategory;
+
+
+    @ApiModelProperty("用户实名")
+    private String realName;
+
+    @ApiModelProperty("用户昵称")
+    private String username;
+
+    @ApiModelProperty("用户手机号")
+    private String phone;
+
+    @ApiModelProperty("客户端类型 TEACHER :老师端 STUDENT:学生端")
+    private ClientEnum clientType;
+
+    @ApiModelProperty("使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期")
+    private CouponUseStateEnum useState;
+
+    @ApiModelProperty("订单号")
+    private String orderNo;
+
+    @ApiModelProperty("有效期开始时间")
+    private Long startTime;
+
+    @ApiModelProperty("有效期结束时间")
+    private Long endTime;
+
+    @ApiModelProperty("发放时间")
+    private Long issueTime;
+
+    @ApiModelProperty("使用时间")
+    private Long useTime;
+
+    @ApiModelProperty("发放途径PLATFORM:后台发放 ACTIVITY:活动领取")
+    private SendTypeEnum issueWay;
+
+    @ApiModelProperty("发放人id")
+    private Long issuer;
+
+    @ApiModelProperty("发放人昵称")
+    private String issueUsername;
+
+    @ApiModelProperty("发放人实名")
+    private String issueRealName;
+
+    @ApiModelProperty("使用门槛")
+    private BigDecimal useLimit;
+
+    @ApiModelProperty("优惠金额")
+    private BigDecimal discountPrice;
+
+    @ApiModelProperty("备注")
+    private String remark;
+
+    @ApiModelProperty("可用数量")
+    private Long usable;
+
+    public CouponCategoryEnum getUseType() {
+        return useType;
+    }
+
+    public void setUseType(CouponCategoryEnum useType) {
+        this.useType = useType;
+    }
+
+    public Long getUsable() {
+        return usable;
+    }
+
+    public void setUsable(Long usable) {
+        this.usable = usable;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public BigDecimal getUseLimit() {
+        return useLimit;
+    }
+
+    public void setUseLimit(BigDecimal useLimit) {
+        this.useLimit = useLimit;
+    }
+
+    public BigDecimal getDiscountPrice() {
+        return discountPrice;
+    }
+
+    public void setDiscountPrice(BigDecimal discountPrice) {
+        this.discountPrice = discountPrice;
+    }
+
+    public CouponTypeEnum getCouponType() {
+        return couponType;
+    }
+
+    public void setCouponType(CouponTypeEnum couponType) {
+        this.couponType = couponType;
+    }
+
+    public CouponCategoryEnum getCouponCategory() {
+        return couponCategory;
+    }
+
+    public void setCouponCategory(CouponCategoryEnum couponCategory) {
+        this.couponCategory = couponCategory;
+    }
+
+    public Long getCouponId() {
+        return couponId;
+    }
+
+    public void setCouponId(Long couponId) {
+        this.couponId = couponId;
+    }
+
+    public String getCouponName() {
+        return couponName;
+    }
+
+    public void setCouponName(String couponName) {
+        this.couponName = couponName;
+    }
+
+    public Long getIssuer() {
+        return issuer;
+    }
+
+    public void setIssuer(Long issuer) {
+        this.issuer = issuer;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public Long getCouponIssueId() {
+        return couponIssueId;
+    }
+
+    public void setCouponIssueId(Long couponIssueId) {
+        this.couponIssueId = couponIssueId;
+    }
+
+    public String getRealName() {
+        return realName;
+    }
+
+    public void setRealName(String realName) {
+        this.realName = realName;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public ClientEnum getClientType() {
+        return clientType;
+    }
+
+    public void setClientType(ClientEnum clientType) {
+        this.clientType = clientType;
+    }
+
+    public CouponUseStateEnum getUseState() {
+        return useState;
+    }
+
+    public void setUseState(CouponUseStateEnum useState) {
+        this.useState = useState;
+    }
+
+    public String getOrderNo() {
+        return orderNo;
+    }
+
+    public void setOrderNo(String orderNo) {
+        this.orderNo = orderNo;
+    }
+
+    public Long getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Long startTime) {
+        this.startTime = startTime;
+    }
+
+    public Long getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Long endTime) {
+        this.endTime = endTime;
+    }
+
+    public Long getIssueTime() {
+        return issueTime;
+    }
+
+    public void setIssueTime(Long issueTime) {
+        this.issueTime = issueTime;
+    }
+
+    public Long getUseTime() {
+        return Optional.ofNullable(useTime).filter(x -> x > 0).orElse(null);
+    }
+
+    public void setUseTime(Long useTime) {
+        this.useTime = useTime;
+    }
+
+    public SendTypeEnum getIssueWay() {
+        return issueWay;
+    }
+
+    public void setIssueWay(SendTypeEnum issueWay) {
+        this.issueWay = issueWay;
+    }
+
+    public String getIssueUsername() {
+        return issueUsername;
+    }
+
+    public void setIssueUsername(String issueUsername) {
+        this.issueUsername = issueUsername;
+    }
+
+    public String getIssueRealName() {
+        return issueRealName;
+    }
+
+    public void setIssueRealName(String issueRealName) {
+        this.issueRealName = issueRealName;
+    }
+
+    public String getRemark() {
+        return remark;
+    }
+
+    public void setRemark(String remark) {
+        this.remark = remark;
+    }
+}

+ 9 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wordfilter/WordContext.java

@@ -10,6 +10,7 @@ import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 
 import javax.annotation.PostConstruct;
@@ -34,6 +35,8 @@ public class WordContext {
     private SysConfigService sysConfigService;
     @Autowired
     private RedissonClient redissonClient;
+    @Value("${app.blacklist:}")
+    private String blacklistFilePath;
 
     /**
      * 敏感词字典
@@ -78,8 +81,13 @@ public class WordContext {
 
     private void initKeyWord1() {
         try {
+
+            if (StringUtils.isEmpty(blacklistFilePath)) {
+                blacklistFilePath = SysConfigConstant.BLACK_LIST_FILE_PATH;
+            }
+
             RBucket<Long> bucket = redissonClient.getBucket(SysConfigConstant.BLACK_LIST_UPDATE_TIME);
-            File file = new File(SysConfigConstant.BLACK_LIST_FILE_PATH);
+            File file = new File(blacklistFilePath);
             this.blackList = FileUtils.readFileToString(file, "UTF-8");
             bucket.set(file.lastModified());
             initKeyWord();

+ 24 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/StatGroupWrapper.java

@@ -0,0 +1,24 @@
+package com.yonge.cooleshow.biz.dal.wrapper;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 分组统计指标
+ * Created by Eric.Shang on 28/7/17.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class StatGroupWrapper implements Serializable {
+
+    private Long id;
+    private Integer total;
+    private String gid; // 字符串分组ID
+
+}

+ 48 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/coupon/CouponInventoryWrapper.java

@@ -0,0 +1,48 @@
+package com.yonge.cooleshow.biz.dal.wrapper.coupon;
+
+import com.alibaba.fastjson.JSON;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponInventoryEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 优惠券库存量调整
+ * Created by Eric.Shang on 2022/9/5.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CouponInventoryWrapper implements Serializable {
+
+    private Long id;
+    private Long userId;
+    private Long couponId;
+    private CouponInventoryEnum dataType;
+    private Integer number;
+    private String remark;
+    private Integer status;
+    private Date createdTime;
+
+    private String username;
+    private String realName;
+
+    public String jsonString() {
+        return JSON.toJSONString(this);
+    }
+
+    public String getUsername() {
+        if (StringUtils.isEmpty(this.username)
+                && StringUtils.isNotEmpty(getRealName())) {
+
+            return getRealName();
+        }
+        return username;
+    }
+}

+ 138 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/coupon/CouponOrderWrapper.java

@@ -0,0 +1,138 @@
+package com.yonge.cooleshow.biz.dal.wrapper.coupon;
+
+import com.alibaba.fastjson.JSON;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 优惠券订单信息
+ * Created by Eric.Shang on 2022/9/6.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CouponOrderWrapper implements Serializable {
+
+    // 优惠券叠加优惠金额
+    private Double discountedPrices;
+
+    // 订单优惠券信息
+    private List<CouponInfo> couponInfos;
+
+    // 优惠券状态重置标识
+    private Boolean reset;
+    // 订单编号
+    private String orderNo;
+
+    // 使用方式
+    private CouponCategoryEnum useType;
+
+    public String jsonString() {
+
+        return JSON.toJSONString(this);
+    }
+
+    public static CouponOrderWrapper from(String recv) {
+
+        return JSON.parseObject(recv, CouponOrderWrapper.class);
+    }
+
+    public Boolean getReset() {
+        return Optional.ofNullable(reset).orElse(false);
+    }
+
+    public CouponOrderWrapper discountedPrices(Double discountedPrices) {
+        this.discountedPrices = discountedPrices;
+        return this;
+    }
+
+    public CouponOrderWrapper couponInfos(List<CouponInfo> couponInfos) {
+        this.couponInfos = couponInfos;
+        return this;
+    }
+
+    public CouponOrderWrapper reset(Boolean reset) {
+        this.reset = reset;
+        return this;
+    }
+
+    public CouponOrderWrapper orderNo(String orderNo) {
+        this.orderNo = orderNo;
+        return this;
+    }
+
+    public CouponOrderWrapper useType(CouponCategoryEnum useType) {
+        this.useType = useType;
+        return this;
+    }
+
+
+    /**
+     * 用户优惠券信息
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponInfo implements Serializable {
+
+        // 优惠券ID
+        private Long id;
+        // 优惠券名称
+        private String name;
+        // 使用门槛
+        private Double useLimit;
+        // 抵扣金额
+        private Double discountPrice;
+        // 使用状态
+        private Boolean selected;
+        // 发放ID
+        private Long issueId;
+
+        public Double getUseLimit() {
+            return Optional.ofNullable(useLimit).orElse(0D);
+        }
+
+        public Double getDiscountPrice() {
+            return Optional.ofNullable(discountPrice).orElse(0D);
+        }
+
+        public Boolean getSelected() {
+            return Optional.ofNullable(selected).orElse(false);
+        }
+    }
+
+    /**
+     * 订单
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponIssue implements Serializable {
+
+        // 发放ID
+        private Long id;
+        // 使用状态
+        private CouponUseStateEnum useState;
+        // 使用时间
+        private Long useTime;
+        // 订单编号
+        private String orderNo;
+        // 使用方式
+        private CouponCategoryEnum useType;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+    }
+}

+ 188 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/CouponInfoMapper.xml

@@ -0,0 +1,188 @@
+<?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.yonge.cooleshow.biz.dal.mapper.CouponInfoMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.yonge.cooleshow.biz.dal.entity.CouponInfo">
+        <id column="id_" property="id" />
+        <result column="name_" property="name" />
+        <result column="serial_num_" property="serialNum" />
+        <result column="describe_" property="describe" />
+        <result column="client_type_" property="clientType" />
+        <result column="category_" property="category" />
+        <result column="use_limit_" property="useLimit" />
+        <result column="discount_price_" property="discountPrice" />
+        <result column="coupon_type_" property="couponType" />
+        <result column="valid_day_" property="validDay" />
+        <result column="start_time_" property="startTime" />
+        <result column="end_time_" property="endTime" />
+        <result column="inventory_" property="inventory" />
+        <result column="quantity_limit_" property="quantityLimit" />
+        <result column="status_" property="status" />
+        <result column="updated_by_" property="updatedBy" />
+        <result column="update_time_" property="updateTime" />
+        <result column="created_by_" property="createdBy" />
+        <result column="created_time_" property="createdTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id_, name_, serial_num_, describe_, client_type_, category_, use_limit_, discount_price_, coupon_type_, valid_day_, start_time_, end_time_, inventory_, quantity_limit_, status_, updated_by_, update_time_, created_by_, created_time_
+    </sql>
+
+    <!--优惠券分页查询-->
+    <select id="selectCouponPageInfo" resultType="com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper">
+        SELECT t1.id_, t1.name_, t1.client_type_, t1.category_, t1.coupon_type_, t1.inventory_, t1.status_, t1.updated_by_, t1.update_time_, t1.created_time_, t1.valid_type_, t1.end_time_ FROM coupon_info t1
+        <where>
+            <if test="record.couponId != null">
+                AND t1.id_ = #{record.couponId}
+            </if>
+            <if test="record.name != null">
+                AND t1.name_ LIKE '%${record.name}%'
+            </if>
+            <if test="record.clientType != null">
+                AND t1.client_type_ = #{record.clientType}
+            </if>
+            <if test="record.couponType != null">
+                AND t1.coupon_type_ = #{record.couponType}
+            </if>
+            <if test="record.category != null">
+                AND t1.category_ = #{record.category}
+            </if>
+            <if test="record.status != null">
+                AND t1.status_ = #{record.status}
+            </if>
+            <if test="record.timestamp != null">
+                AND (#{record.timestamp} &lt;= t1.end_time_ <if test="record.validType != null"> OR t1.valid_type_ = #{record.validType}</if>)
+            </if>
+        </where>
+        ORDER BY t1.id_ DESC
+    </select>
+
+    <!--优惠券发放统计-->
+    <select id="selectCouponIssueStatInfo" resultType="com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper">
+        SELECT t1.coupon_id_ AS id, COUNT(DISTINCT t1.id_) AS total FROM coupon_issue t1
+        <where>
+            <if test="couponIds != null">
+                AND t1.coupon_id_ IN (<foreach collection="couponIds" separator="," item="item">#{item}</foreach>)
+            </if>
+            <if test="record.useState != null">
+                AND t1.use_state_ = #{record.useState}
+            </if>
+            <if test="record.timestamp != null">
+                AND (t1.start_time_ &lt;= #{record.timestamp} AND #{record.timestamp} &lt;= t1.end_time_)
+            </if>
+            <if test="record.excludeUseState != null">
+                AND t1.use_state_ NOT IN (<foreach collection="record.excludeUseState" separator="," item="item">#{item}</foreach>)
+            </if>
+        </where>
+        GROUP BY t1.coupon_id_
+    </select>
+    <!--优惠券分页查询-->
+
+    <!--优惠券库存调整信息-->
+    <resultMap id="CouponInventoryPageInfoResultMap" type="com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponInventoryWrapper">
+        <id column="id_" property="id" />
+        <result column="user_id_" property="userId" />
+        <result column="coupon_id_" property="couponId" />
+        <result column="data_type_" property="dataType" />
+        <result column="number_" property="number" />
+        <result column="remark_" property="remark" />
+        <result column="created_time_" property="createdTime" />
+
+        <result column="username_" property="username" />
+        <result column="real_name_" property="realName" />
+    </resultMap>
+    <select id="selectCouponInventoryPageInfo" resultMap="CouponInventoryPageInfoResultMap">
+        SELECT t1.id_, t1.user_id_, t1.coupon_id_, t1.data_type_, t1.number_, t1.remark_, t1.created_time_, t2.username_, t2.real_name_
+        FROM coupon_inventory t1 JOIN sys_user t2 ON t1.user_id_ = t2.id_
+        <where>
+            <if test="record.couponId != null">
+                AND t1.coupon_id_ = #{record.couponId}
+            </if>
+            <if test="record.dataType != null">
+                AND t1.data_type_ = #{record.dataType}
+            </if>
+            <if test="record.userId != null">
+                AND t1.user_id_ = #{record.userId}
+            </if>
+            <if test="record.keyword != null">
+                AND (t2.username_ LIKE '%${record.keyword}%' OR t2.real_name_ LIKE '%${record.keyword}%')
+            </if>
+            <if test="record.startTime != null and record.endTime != null">
+                AND (#{record.startTime} &lt;= UNIX_TIMESTAMP(t1.created_time_) AND UNIX_TIMESTAMP(t1.created_time_) &lt;= #{record.endTime})
+            </if>
+        </where>
+        <if test="record.groupByUser != null">GROUP BY t1.user_id_</if> ORDER BY t1.id_ DESC
+    </select>
+    <!--优惠券库存调整信息-->
+
+    <!--活动优惠券统计-->
+    <select id="selectActivityCouponStatInfo"
+            resultType="com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper">
+        SELECT t1.coupon_id_ AS id, COUNT(DISTINCT t1.id_) AS total FROM activity_reward t1
+        <where>
+            <if test="couponIds != null">
+                AND t1.coupon_id_ IN (<foreach collection="couponIds" separator="," item="item">#{item}</foreach>)
+            </if>
+        </where>
+        GROUP BY t1.coupon_id_
+    </select>
+
+    <!--用户订单优惠券信息-->
+    <resultMap id="UserOrderCouponInfoResultMap" type="com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponOrderWrapper$CouponInfo">
+        <id column="id_" property="id" />
+        <result column="name_" property="name" />
+        <result column="use_limit_" property="useLimit" />
+        <result column="discount_price_" property="discountPrice" />
+        <result column="issue_id" property="issueId" />
+    </resultMap>
+    <select id="selectUserOrderCouponInfo" resultMap="UserOrderCouponInfoResultMap">
+        SELECT t1.id_, t1.name_, t1.use_limit_, t1.discount_price_, t2.id_ AS issue_id
+        FROM coupon_info t1 JOIN coupon_issue t2 ON t1.id_ = t2.coupon_id_
+        <where>
+            <if test="userId != null">
+                AND t2.user_id_ = #{userId}
+            </if>
+            <if test="record.issueIds != null">
+                AND t2.id_ IN (<foreach collection="record.issueIds" separator="," item="item">#{item}</foreach>)
+            </if>
+            <if test="record.clientType != null">
+                AND t2.client_type_ = #{record.clientType}
+            </if>
+            <if test="record.useState != null">
+                AND t2.use_state_ = #{record.useState}
+            </if>
+            <if test="record.timestamp != null">
+                AND (t2.start_time_ &lt;= #{record.timestamp} AND #{record.timestamp} &lt;= t2.end_time_)
+            </if>
+            <if test="record.orderNo != null">
+                AND t2.order_no_ = #{record.orderNo}
+            </if>
+            <if test="record.couponTypes != null">
+                AND t1.category_ IN (<foreach collection="record.couponTypes" separator="," item="item">#{item}</foreach>)
+            </if>
+        </where>
+    </select>
+    <!--用户订单优惠券信息-->
+
+    <update id="updateStock">
+        update coupon_info set inventory_ = inventory_
+        <choose>
+            <when test="inOrOut.code == 'IN'">
+                + #{size}
+            </when>
+            <when test="inOrOut.code == 'OUT'">
+                - #{size}
+            </when>
+        </choose>
+        <where>
+            <if  test="inOrOut.code == 'OUT'">
+                and inventory_ - #{size} &gt;= 0
+            </if>
+            and id_ = #{couponId}
+        </where>
+    </update>
+
+
+</mapper>

+ 22 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/CouponInventoryMapper.xml

@@ -0,0 +1,22 @@
+<?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.yonge.cooleshow.biz.dal.mapper.CouponInventoryMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.yonge.cooleshow.biz.dal.entity.CouponInventory">
+        <id column="id_" property="id" />
+        <result column="user_id_" property="userId" />
+        <result column="coupon_id_" property="couponId" />
+        <result column="data_type_" property="dataType" />
+        <result column="number_" property="number" />
+        <result column="remark_" property="remark" />
+        <result column="status_" property="status" />
+        <result column="created_time_" property="createdTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id_, user_id_, coupon_id_, data_type_, number_, remark_, status_, created_time_
+    </sql>
+
+</mapper>

+ 159 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/CouponIssueMapper.xml

@@ -0,0 +1,159 @@
+<?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.yonge.cooleshow.biz.dal.mapper.CouponIssueMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.yonge.cooleshow.biz.dal.entity.CouponIssue">
+        <id column="id_" property="id" />
+        <result column="user_id_" property="userId" />
+        <result column="coupon_id_" property="couponId" />
+        <result column="client_type_" property="clientType" />
+        <result column="use_state_" property="useState" />
+        <result column="use_time_" property="useTime" />
+        <result column="order_no_" property="orderNo" />
+        <result column="start_time_" property="startTime" />
+        <result column="end_time_" property="endTime" />
+        <result column="issue_way_" property="issueWay" />
+        <result column="issuer_" property="issuer" />
+        <result column="issue_time_" property="issueTime" />
+        <result column="remark_" property="remark" />
+        <result column="status_" property="status" />
+        <result column="created_time_" property="createdTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id_, user_id_, coupon_id_, client_type_, use_state_, use_time_, order_no_, start_time_, end_time_, issue_way_, issuer_, issue_time_, remark_, status_, created_time_
+    </sql>
+
+    <select id="queryCouponIssueInfo" resultType="com.yonge.cooleshow.biz.dal.vo.coupon.CouponIssueWrapper">
+        select
+            ci.user_id_ as userId,
+            ci.id_ as couponIssueId,
+            ci.client_type_ as clientType,
+            ci.issue_way_ as issueWay,
+            ci.order_no_ as orderNo,
+            ci.start_time_ as startTime,
+            ci.end_time_ as endTime,
+            ci.issue_time_ as issueTime,
+            ci.use_time_ as useTime,
+            ci.use_state_ as useState,
+            ci.issuer_ as issuer,
+            ci.remark_ as remark,
+            ci.use_type_ as useType,
+            cou.name_ as couponName,
+            cou.id_ as couponId,
+            cou.coupon_type_ as couponType,
+            cou.category_ as couponCategory,
+            cou.use_limit_ as useLimit,
+            cou.discount_price_ as discountPrice
+        from coupon_issue ci
+        <if test="query.userId != null or (query.keyword != null  and query.keyword != '')">
+            left join sys_user su on ci.user_id_ = su.id_
+        </if>
+        left join coupon_info cou on cou.id_ = ci.coupon_id_
+        <where>
+            <if test="query.userId != null">
+                and #{query.userId} = ci.user_id_
+            </if>
+            <if test="query.couponId != null">
+                and #{query.couponId} = ci.coupon_id_
+            </if>
+            <if test="query.clientType != null">
+                and #{query.clientType} = ci.client_type_
+            </if>
+            <if test="query.couponCategory != null">
+                and #{query.couponCategory} = cou.category_
+            </if>
+            <if test="query.couponCategoryList != null and query.couponCategoryList.size() != 0">
+                and cou.category_ in
+                <foreach collection="query.couponCategoryList" open="(" close=")" item="item" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="query.couponType != null">
+                and #{query.couponType} = cou.coupon_type_
+            </if>
+            <if test="query.couponKeyword != null and query.couponKeyword != ''">
+                and (
+                    cou.id_ like '%${query.couponKeyword}%'
+                    or cou.name_ like '%${query.couponKeyword}%'
+                )
+            </if>
+            <if test="query.useState != null">
+                <choose>
+                    <when test="query.useState.code == 'EXPIRED'">
+                        and ci.end_time_ &lt; #{query.expiredTime} and ci.use_state_  = 'USABLE'
+                    </when>
+                    <when test="query.useState.code == 'USABLE'">
+                        and ci.end_time_ &gt;= #{query.expiredTime} and ci.start_time_ &lt;= #{query.expiredTime} and ci.use_state_ = 'USABLE'
+                    </when>
+                    <otherwise>
+                        and ci.use_state_ = #{query.useState}
+                    </otherwise>
+                </choose>
+            </if>
+            <if test="query.issueWay != null">
+                and #{query.issueWay} = ci.issue_way_
+            </if>
+            <if test="query.issueStartTime != null">
+                and  #{query.issueStartTime} &lt;= ci.issue_time_
+            </if>
+            <if test="query.issueEndTime != null">
+                and #{query.issueEndTime} &gt;= ci.issue_time_
+            </if>
+            <if test="query.usedStartTime != null">
+                and #{query.usedStartTime} &lt;= ci.use_time_
+            </if>
+            <if test="query.usedEndTime != null">
+                and #{query.usedEndTime} &gt;= ci.use_time_
+            </if>
+            <if test="query.keyword !=null and query.keyword != ''">
+                and (
+                    su.real_name_ like '%${query.keyword}%'
+                    or su.username_ like '%${query.keyword}%'
+                    or su.phone_ like '%${query.keyword}%'
+                    or su.id_ like '%${query.keyword}%'
+                )
+            </if>
+        </where>
+
+        order by ci.id_ desc
+
+
+    </select>
+
+    <!--用户优惠券状态统计-->
+    <select id="selectCouponStateStatInfo" resultType="com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper">
+        SELECT <if test="record.groupByState != null"> t1.use_state_ AS gid,</if> COUNT(DISTINCT t1.id_) AS total FROM coupon_issue t1 <if test="record.couponType != null"> JOIN coupon_info t2 ON (t1.coupon_id_ = t2.id_)</if>
+        <where>
+            <if test="userId != null">
+                AND t1.user_id_ = #{userId}
+            </if>
+            <if test="record.clientType != null">
+                AND t1.client_type_ = #{record.clientType}
+            </if>
+            <if test="record.couponType != null">
+                AND t2.coupon_type_ = #{record.couponType}
+            </if>
+            <if test="record.timestamp != null">
+                <choose>
+                    <when test="record.userState.code == 'USABLE'">
+                        AND t1.start_time_ &lt;= #{record.timestamp} AND #{record.timestamp} &lt;= t1.end_time_ AND t1.use_state_ = 'USABLE'
+                    </when>
+                    <when test="record.userState.code == 'EXPIRED'">
+                        AND #{record.timestamp} &gt;= t1.end_time_ AND t1.use_state_ = 'USABLE'
+                    </when>
+                    <when test="record.userState.code == 'USED'">
+                        AND t1.order_no_ != ''
+                    </when>
+                    <otherwise>
+                        AND t1.use_state_ = #{record.userState}
+                    </otherwise>
+                </choose>
+            </if>
+        </where>
+        <if test="record.groupByState != null">GROUP BY t1.use_state_</if>
+    </select>
+    <!--用户优惠券状态统计-->
+</mapper>

+ 1 - 1
cooleshow-user/user-biz/src/main/resources/config/mybatis/MusicSheetMapper.xml

@@ -477,7 +477,7 @@
                 or ms.composer_ like concat('%',#{param.idAndName},'%'))
             </if>
             <if test="param.teacherId != null">
-                and ms.create_by_ = #{param.teacherId}
+                and ms.create_by_ = #{param.teacherId} and ms.source_type_ = 'TEACHER'
             </if>
             <if test="param.startTime != null">
                 and ms.create_time_ &gt; #{param.startTime}

+ 69 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/SysUserMapper.xml

@@ -0,0 +1,69 @@
+<?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.yonge.cooleshow.biz.dal.mapper.SysUserMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.yonge.cooleshow.biz.dal.entity.SysUser">
+        <id column="id_" property="id" />
+        <result column="username_" property="username" />
+        <result column="password_" property="password" />
+        <result column="salt_" property="salt" />
+        <result column="phone_" property="phone" />
+        <result column="avatar_" property="avatar" />
+        <result column="create_time_" property="createTime" />
+        <result column="update_time_" property="updateTime" />
+        <result column="lock_flag_" property="lockFlag" />
+        <result column="wx_openid_" property="wxOpenid" />
+        <result column="qq_openid_" property="qqOpenid" />
+        <result column="user_type_" property="userType" />
+        <result column="gender_" property="gender" />
+        <result column="nation_" property="nation" />
+        <result column="birthdate_" property="birthdate" />
+        <result column="email_" property="email" />
+        <result column="im_token_" property="imToken" />
+        <result column="real_name_" property="realName" />
+        <result column="id_card_no_" property="idCardNo" />
+        <result column="certificate_type_" property="certificateType" />
+        <result column="is_super_admin_" property="superAdmin" />
+        <result column="wechat_id_" property="wechatId" />
+        <result column="del_flag_" property="delFlag" />
+        <result column="last_username_time_" property="lastUsernameTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id_, username_, password_, salt_, phone_, avatar_, create_time_, update_time_, lock_flag_, wx_openid_, qq_openid_, user_type_, gender_, nation_, birthdate_, email_, im_token_, real_name_, id_card_no_, certificate_type_, is_super_admin_, wechat_id_, del_flag_, last_username_time_
+    </sql>
+
+    <select id="queryCouponIssueUser" resultType="com.yonge.cooleshow.biz.dal.vo.CouponIssueUserVo">
+
+        select
+            su.id_ as userId,
+            su.username_ as username,
+            su.real_name_ as realName,
+            #{client} as clientType,
+            su.phone_ as phone
+        from
+        <choose>
+            <when test="client.code == 'TEACHER'">
+                teacher t
+            </when>
+            <when test="client.code == 'STUDENT'">
+                student t
+            </when>
+        </choose>
+        left join sys_user su on su.id_ = t.user_id_
+        <where>
+            t.lock_flag_ = 0 and su.del_flag_ = 0
+            <if test="keyword != null and keyword != ''">
+                and (
+                su.real_name_ like concat('%',#{keyword},'%')
+                or su.username_ like concat('%',#{keyword},'%')
+                or su.phone_ like concat('%',#{keyword},'%')
+                or su.id_ like concat('%',#{keyword},'%')
+                )
+            </if>
+        </where>
+
+    </select>
+</mapper>

+ 1 - 1
cooleshow-user/user-classroom/src/main/java/com/yonge/cooleshow/classroom/ClassroomApplication.java

@@ -14,7 +14,7 @@ import org.springframework.context.annotation.Configuration;
 @SpringBootApplication
 @EnableDiscoveryClient
 @EnableFeignClients("com.yonge.cooleshow")
-@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.toolset.payment.core.dao"})
+@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.cooleshow.biz.dal.mapper", "com.yonge.toolset.payment.core.dao"})
 @ComponentScan(basePackages = {
         "com.yonge.cooleshow", "com.yonge.toolset"
 })

+ 1 - 1
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/StudentApplication.java

@@ -18,7 +18,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
 @SpringBootApplication
 @EnableDiscoveryClient
 @EnableFeignClients("com.yonge.cooleshow")
-@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.toolset.payment.core.dao"})
+@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.cooleshow.biz.dal.mapper", "com.yonge.toolset.payment.core.dao"})
 @ComponentScan(basePackages = {"com.yonge.cooleshow", "com.yonge.toolset"})
 @Configuration
 @EnableSwagger2Doc

+ 110 - 0
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/coupon/CouponInfoController.java

@@ -0,0 +1,110 @@
+package com.yonge.cooleshow.student.controller.coupon;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponIssueQueryInfo;
+import com.yonge.cooleshow.biz.dal.service.CouponIssueService;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponIssueWrapper;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import com.yonge.cooleshow.student.io.request.CouponInfoVO;
+import com.yonge.toolset.base.page.PageInfo;
+import com.yonge.toolset.mybatis.support.PageUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.GetMapping;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 优惠券信息
+ * Created by Eric.Shang on 2022/9/2.
+ */
+@RestController
+@RequestMapping("/couponInfo")
+@Api(value = "优惠券信息", tags = "优惠券信息")
+public class CouponInfoController extends BaseController {
+
+    @Autowired
+    private SysUserFeignService sysUserFeignService;
+
+    @Autowired
+    private CouponIssueService couponIssueService;
+
+    /**
+     * 优惠券信息
+     * @param request CouponInfoVO.RequestInfo
+     * @return HttpResponseResult<PageInfo<CouponInfoVO.ResponseInfo>>
+     */
+    @PostMapping("/page")
+    @ApiOperation(value = "查询优惠券分页", notes = "传入CouponInfoVO.PageRequest")
+    public HttpResponseResult<PageInfo<CouponInfoVO.CouponPageInfo>> queryCouponPageInfo(@RequestBody CouponInfoVO.PageRequest request) {
+        SysUser user = sysUserFeignService.queryUserInfo();
+        if (user == null || null == user.getId()) {
+            return failed(HttpStatus.FORBIDDEN, "请登录");
+        }
+
+        request.setUserId(user.getId());
+        request.setClientType(ClientEnum.STUDENT.getCode());
+
+
+        // 用户端的券类型 都包含全品类券
+        if (request.getCouponCategory() != null) {
+            List<CouponCategoryEnum> couponCategoryList = new ArrayList<>();
+            couponCategoryList.add(CouponCategoryEnum.UNIVERSAL);
+            couponCategoryList.add(request.getCouponCategory());
+            request.setCouponCategory(null);
+            request.setCouponCategoryList(couponCategoryList);
+        }
+
+        IPage<CouponIssueWrapper> couponIssueWrapperIPage = couponIssueService.queryCouponIssueInfo(
+                PageUtil.getPage(request), CouponIssueQueryInfo.from(request.jsonString()));
+
+
+        // 数据转换
+        List<CouponInfoVO.CouponPageInfo> pageInfos = JSON.parseArray(JSON.toJSONString(couponIssueWrapperIPage.getRecords()),
+                                                                      CouponInfoVO.CouponPageInfo.class);
+
+        return succeed(PageUtil.getPageInfo(couponIssueWrapperIPage,pageInfos));
+    }
+
+    /**
+     * 优惠券状态统计信息
+     * @return List<CouponInfoVO.CouponIssueStateStat>
+     */
+    @GetMapping("/statInfo")
+    @ApiOperation(value = "查询优惠券统计")
+    public HttpResponseResult<List<CouponInfoVO.CouponStateStat>> findCouponStateStatInfo(
+            @ApiParam(value = "优惠券类型") @RequestParam(value = "couponType", required = false) String couponType) {
+
+        // 登录用户信息
+        SysUser user = sysUserFeignService.queryUserInfo();
+        if (Objects.isNull(user) || Objects.isNull(user.getId())) {
+            return failed(HttpStatus.FORBIDDEN, "请登录");
+        }
+
+        // 优惠券统计
+        List<CouponInfoWrapper.CouponStat> wrappers = couponIssueService.queryCouponStateStatInfo(user.getId(),
+                CouponInfoQuery.CouponStateStatQuery.builder()
+                        .clientType(ClientEnum.STUDENT)
+                        .build().couponType(couponType));
+
+        return succeed(JSON.parseArray(JSON.toJSONString(wrappers), CouponInfoVO.CouponStateStat.class));
+    }
+}

+ 131 - 0
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/io/request/CouponInfoVO.java

@@ -0,0 +1,131 @@
+package com.yonge.cooleshow.student.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.toolset.base.page.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 优惠券信息
+ * Created by Eric.Shang on 2022/9/2.
+ */
+public class CouponInfoVO {
+
+    /**
+     * 优惠券分页请求信息
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(value = "优惠券信息分页请求", description = "优惠券信息分页")
+    public static class PageRequest extends QueryInfo {
+
+        @ApiModelProperty(value = "用户id",hidden = true)
+        private Long userId;
+
+        @ApiModelProperty("客户端类型: TEACHER(老师端), STUDENT(学生端)")
+        private String clientType;
+
+        @ApiModelProperty("优惠券类型: FULL_DISCOUNT(满减券) VOUCHER(代金券) ")
+        private String couponType;
+
+
+        @ApiModelProperty("使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期  WITHDRAW 撤回")
+        private String useState;
+
+
+        @ApiModelProperty("可用品类 UNIVERSAL:全场通用 VIP:小酷Ai PIANO:云酷琴房 MALL:商场购物券 MUSIC:单曲点播券 SPARRING:陪练课购买券 LIVE:直播课购买券  VIDEO:视频课购买券")
+        private CouponCategoryEnum couponCategory;
+
+
+        // 可用品类
+        @ApiModelProperty(hidden = true)
+        private List<CouponCategoryEnum> couponCategoryList;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+    }
+
+    /**
+     * 优惠券分页响应数据
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(value = "优惠券信息分页信息", description = "优惠券信息分页信息")
+    public static class CouponPageInfo implements Serializable {
+
+        @ApiModelProperty("优惠券发放id")
+        private Long couponIssueId;
+
+        @ApiModelProperty("优惠券名称")
+        private String couponName;
+
+        @ApiModelProperty("客户端类型: TEACHER(老师端), STUDENT(学生端)")
+        private String clientType;
+
+        @ApiModelProperty("优惠券类型: FULL_DISCOUNT(满减券) VOUCHER(代金券) ")
+        private String couponType;
+
+        @ApiModelProperty("使用门槛")
+        private BigDecimal useLimit;
+
+        @ApiModelProperty("优惠金额")
+        private BigDecimal discountPrice;
+
+        @ApiModelProperty("用户ID")
+        private Long userId;
+
+        @ApiModelProperty("优惠券ID")
+        private Long couponId;
+
+        @ApiModelProperty("使用状态: EXPIRED(已失效) USABLE(可使用) USED(已使用) ")
+        private CouponUseStateEnum useState;
+
+        @ApiModelProperty("使用时间")
+        private Long useTime;
+
+        @ApiModelProperty("生效时间")
+        @JSONField(format = "yyyy-MM-dd")
+        private Long startTime;
+
+        @ApiModelProperty("失效时间")
+        @JSONField(format = "yyyy-MM-dd")
+        private Long endTime;
+
+
+        @ApiModelProperty("可用品类 UNIVERSAL:全场通用 VIP:小酷Ai PIANO:云酷琴房 MALL:商场购物券 MUSIC:单曲点播券 SPARRING:陪练课购买券 LIVE:直播课购买券  VIDEO:视频课购买券")
+        private CouponCategoryEnum couponCategory;
+
+    }
+
+
+    /**
+     * 优惠券发放状态统计
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponStateStat implements Serializable {
+
+        @ApiModelProperty("使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期")
+        private CouponUseStateEnum useState;
+
+        @ApiModelProperty("数量统计")
+        private Integer total;
+
+    }
+}

+ 1 - 1
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/TeacherApplication.java

@@ -19,7 +19,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
 @SpringBootApplication
 @EnableDiscoveryClient
 @EnableFeignClients("com.yonge.cooleshow")
-@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.toolset.payment.core.dao"})
+@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.cooleshow.biz.dal.mapper", "com.yonge.toolset.payment.core.dao"})
 @ComponentScan(basePackages = {"com.yonge.cooleshow","com.yonge.toolset"})
 @Configuration
 @EnableSwagger2Doc

+ 108 - 0
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/coupon/CouponInfoController.java

@@ -0,0 +1,108 @@
+package com.yonge.cooleshow.teacher.controller.coupon;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
+import com.yonge.cooleshow.biz.dal.queryInfo.CouponIssueQueryInfo;
+import com.yonge.cooleshow.biz.dal.service.CouponIssueService;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper;
+import com.yonge.cooleshow.biz.dal.vo.coupon.CouponIssueWrapper;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import com.yonge.cooleshow.teacher.io.request.CouponInfoVO;
+import com.yonge.toolset.base.page.PageInfo;
+import com.yonge.toolset.mybatis.support.PageUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.GetMapping;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 优惠券信息
+ * Created by Eric.Shang on 2022/9/2.
+ */
+@RestController
+@RequestMapping("/couponInfo")
+@Api(value = "优惠券信息", tags = "优惠券信息")
+public class CouponInfoController extends BaseController {
+
+    @Autowired
+    private SysUserFeignService sysUserFeignService;
+
+    @Autowired
+    private CouponIssueService couponIssueService;
+
+    /**
+     * 优惠券信息
+     * @param request CouponInfoVO.RequestInfo
+     * @return HttpResponseResult<PageInfo<CouponInfoVO.ResponseInfo>>
+     */
+    @PostMapping("/page")
+    @ApiOperation(value = "查询优惠券分页", notes = "传入CouponInfoVO.PageRequest")
+    public HttpResponseResult<PageInfo<CouponInfoVO.CouponPageInfo>> queryCouponPageInfo(@RequestBody CouponInfoVO.PageRequest request) {
+        SysUser user = sysUserFeignService.queryUserInfo();
+        if (user == null || null == user.getId()) {
+            return failed(HttpStatus.FORBIDDEN, "请登录");
+        }
+
+        request.setUserId(user.getId());
+        request.setClientType(ClientEnum.TEACHER.getCode());
+
+        // 用户端的券类型 都包含全品类券
+        if (request.getCouponCategory() != null) {
+            List<CouponCategoryEnum> couponCategoryList = new ArrayList<>();
+            couponCategoryList.add(CouponCategoryEnum.UNIVERSAL);
+            couponCategoryList.add(request.getCouponCategory());
+            request.setCouponCategory(null);
+            request.setCouponCategoryList(couponCategoryList);
+        }
+
+        IPage<CouponIssueWrapper> couponIssueWrapperIPage = couponIssueService.queryCouponIssueInfo(
+                PageUtil.getPage(request), CouponIssueQueryInfo.from(request.jsonString()));
+
+
+        // 数据转换
+        List<CouponInfoVO.CouponPageInfo> pageInfos = JSON.parseArray(JSON.toJSONString(couponIssueWrapperIPage.getRecords()),
+                                                                      CouponInfoVO.CouponPageInfo.class);
+
+        return succeed(PageUtil.getPageInfo(couponIssueWrapperIPage,pageInfos));
+    }
+
+    /**
+     * 优惠券状态统计信息
+     * @return List<CouponInfoVO.CouponIssueStateStat>
+     */
+    @GetMapping("/statInfo")
+    @ApiOperation(value = "查询优惠券统计")
+    public HttpResponseResult<List<CouponInfoVO.CouponStateStat>> findCouponStateStatInfo(
+            @ApiParam(value = "优惠券类型") @RequestParam(value = "couponType", required = false) String couponType) {
+
+        // 登录用户信息
+        SysUser user = sysUserFeignService.queryUserInfo();
+        if (Objects.isNull(user) || Objects.isNull(user.getId())) {
+            return failed(HttpStatus.FORBIDDEN, "请登录");
+        }
+
+        List<CouponInfoWrapper.CouponStat> wrappers = couponIssueService.queryCouponStateStatInfo(user.getId(),
+                CouponInfoQuery.CouponStateStatQuery.builder()
+                .clientType(ClientEnum.TEACHER)
+                .build().couponType(couponType));
+
+        return succeed(JSON.parseArray(JSON.toJSONString(wrappers), CouponInfoVO.CouponStateStat.class));
+    }
+}

+ 134 - 0
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/io/request/CouponInfoVO.java

@@ -0,0 +1,134 @@
+package com.yonge.cooleshow.teacher.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponCategoryEnum;
+import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
+import com.yonge.toolset.base.page.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 优惠券信息
+ * Created by Eric.Shang on 2022/9/2.
+ */
+public class CouponInfoVO {
+
+    /**
+     * 优惠券分页请求信息
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(value = "优惠券信息分页请求", description = "优惠券信息分页")
+    public static class PageRequest extends QueryInfo {
+
+        @ApiModelProperty(value = "用户id",hidden = true)
+        private Long userId;
+
+        @ApiModelProperty("客户端类型: TEACHER(老师端), STUDENT(学生端)")
+        private String clientType;
+
+        @ApiModelProperty("优惠券类型: FULL_DISCOUNT(满减券) VOUCHER(代金券) ")
+        private String couponType;
+
+        @ApiModelProperty("使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期  WITHDRAW 撤回")
+        private String useState;
+
+
+        @ApiModelProperty("可用品类 UNIVERSAL:全场通用 VIP:小酷Ai PIANO:云酷琴房 MALL:商场购物券 MUSIC:单曲点播券 SPARRING:陪练课购买券 LIVE:直播课购买券  VIDEO:视频课购买券")
+        private CouponCategoryEnum couponCategory;
+
+        @ApiModelProperty("订单金额")
+        private BigDecimal amount;
+
+        // 可用品类
+        @ApiModelProperty(hidden = true)
+        private List<CouponCategoryEnum> couponCategoryList;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+    }
+
+    /**
+     * 优惠券分页响应数据
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(value = "优惠券信息分页信息", description = "优惠券信息分页信息")
+    public static class CouponPageInfo implements Serializable {
+
+        @ApiModelProperty("优惠券发放id")
+        private Long couponIssueId;
+
+        @ApiModelProperty("优惠券名称")
+        private String couponName;
+
+        @ApiModelProperty("客户端类型: TEACHER(老师端), STUDENT(学生端)")
+        private String clientType;
+
+        @ApiModelProperty("优惠券类型: FULL_DISCOUNT(满减券) VOUCHER(代金券) ")
+        private String couponType;
+
+        @ApiModelProperty("使用门槛")
+        private BigDecimal useLimit;
+
+        @ApiModelProperty("优惠金额")
+        private BigDecimal discountPrice;
+
+        @ApiModelProperty("用户ID")
+        private Long userId;
+
+        @ApiModelProperty("优惠券ID")
+        private Long couponId;
+
+        @ApiModelProperty("使用状态: EXPIRED(已失效) USABLE(可使用) USED(已使用) ")
+        private CouponUseStateEnum useState;
+
+        @ApiModelProperty("使用时间")
+        private Long useTime;
+
+        @ApiModelProperty("生效时间")
+        @JSONField(format = "yyyy-MM-dd")
+        private Long startTime;
+
+        @ApiModelProperty("失效时间")
+        @JSONField(format = "yyyy-MM-dd")
+        private Long endTime;
+
+        @ApiModelProperty("能使用的")
+        private Integer usable;
+
+
+        @ApiModelProperty("可用品类 UNIVERSAL:全场通用 VIP:小酷Ai PIANO:云酷琴房 MALL:商场购物券 MUSIC:单曲点播券 SPARRING:陪练课购买券 LIVE:直播课购买券  VIDEO:视频课购买券")
+        private CouponCategoryEnum couponCategory;
+
+    }
+
+    /**
+     * 优惠券发放状态统计
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class CouponStateStat implements Serializable {
+
+        @ApiModelProperty("使用状态 USED:已使用 USABLE:未使用 EXPIRED:过期")
+        private CouponUseStateEnum useState;
+
+        @ApiModelProperty("数量统计")
+        private Integer total;
+
+    }
+}

+ 1 - 1
cooleshow-user/user-website/src/main/java/com/yonge/cooleshow/website/WebsiteApplication.java

@@ -17,7 +17,7 @@ import org.springframework.context.annotation.Configuration;
 @SpringBootApplication
 @EnableDiscoveryClient
 @EnableFeignClients("com.yonge.cooleshow")
-@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.toolset.payment.core.dao"})
+@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.cooleshow.biz.dal.mapper", "com.yonge.toolset.payment.core.dao"})
 @ComponentScan(basePackages = {"com.yonge.cooleshow","com.yonge.toolset"})
 @Configuration
 @EnableSwagger2Doc

+ 52 - 0
toolset/toolset-base/src/main/java/com/yonge/toolset/base/util/ThreadPool.java

@@ -0,0 +1,52 @@
+package com.yonge.toolset.base.util;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by Eric.Shang on 23/11/16.
+ */
+public final class ThreadPool {
+    private static ExecutorService executor;
+
+    private ThreadPool(){}
+
+    /**
+     * 线程对象
+     * @return ExecutorService
+     */
+    public static ExecutorService getExecutor() {
+        if (executor == null) {
+            synchronized (ThreadPool.class) {
+                if (executor == null) {
+                    executor = Executors.newFixedThreadPool(100);
+                }
+            }
+        }
+        return executor;
+    }
+
+    /**
+     * 终断超时任务
+     * @param executor ExecutorService
+     * @param future Future
+     * @param timeout 超时时间
+     * @param unit 超时单位
+     */
+    public static Object shutdownTimeoutSubmit(ExecutorService executor, Future<?> future,
+                                             Integer timeout, TimeUnit unit) {
+        Object o = null;
+        // 设置超时时间
+        try {
+            o = future.get(timeout, unit);
+        } catch (Exception e) {
+            future.cancel(true);
+        } finally {
+            executor.shutdown();
+        }
+        return o;
+    }
+
+}