Quellcode durchsuchen

Merge branch 'dev' of http://113.31.163.91:7777/quanshu/mp-server into
dev

Conflicts:
mp-service/src/main/resources/mapper/channel/ChannelMapper.xml
mp-service/src/main/resources/mapper/channel/ChannelUserRelMapper.xml

guanglong vor 3 Jahren
Ursprung
Commit
470dfab0f7
76 geänderte Dateien mit 1836 neuen und 413 gelöschten Zeilen
  1. 3 1
      mp-admin/src/main/java/com/qs/mp/web/controller/api/channel/ChannelController.java
  2. 5 4
      mp-admin/src/main/java/com/qs/mp/web/controller/api/channel/mall/ChannelOrderController.java
  3. 107 23
      mp-admin/src/main/java/com/qs/mp/web/controller/api/common/UserController.java
  4. 35 19
      mp-admin/src/main/java/com/qs/mp/web/controller/api/user/UserTicketController.java
  5. 178 0
      mp-admin/src/main/java/com/qs/mp/web/controller/api/user/UserTicketOrderController.java
  6. 16 10
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysLoginController.java
  7. 6 2
      mp-admin/src/main/resources/application.yml
  8. 6 1
      mp-common/src/main/java/com/qs/mp/common/constant/Constants.java
  9. 43 0
      mp-common/src/main/java/com/qs/mp/common/enums/ChannelRoleEnum.java
  10. 49 0
      mp-common/src/main/java/com/qs/mp/common/enums/CoinLogTypeEnum.java
  11. 39 0
      mp-common/src/main/java/com/qs/mp/common/enums/CouponDiscountTypeEnum.java
  12. 38 0
      mp-common/src/main/java/com/qs/mp/common/enums/CouponDistributeTypeEnum.java
  13. 40 0
      mp-common/src/main/java/com/qs/mp/common/enums/CouponStatusEnum.java
  14. 39 0
      mp-common/src/main/java/com/qs/mp/common/enums/CouponTypeEnum.java
  15. 39 0
      mp-common/src/main/java/com/qs/mp/common/enums/CouponUseAreaEnum.java
  16. 39 0
      mp-common/src/main/java/com/qs/mp/common/enums/PrizeStorageInTypeEnum.java
  17. 38 0
      mp-common/src/main/java/com/qs/mp/common/enums/PrizeStorageStatusEnum.java
  18. 44 0
      mp-common/src/main/java/com/qs/mp/common/enums/TicketPrizeTypeEnum.java
  19. 41 0
      mp-common/src/main/java/com/qs/mp/common/enums/UserCouponStatusEnum.java
  20. 8 4
      mp-common/src/main/java/com/qs/mp/common/enums/UserIdentityEnum.java
  21. 87 19
      mp-common/src/main/java/com/qs/mp/common/utils/DateUtils.java
  22. 3 3
      mp-common/src/main/java/com/qs/mp/common/utils/http/HttpUtils.java
  23. 3 1
      mp-framework/src/main/java/com/qs/mp/framework/config/SecurityConfig.java
  24. 33 123
      mp-framework/src/main/java/com/qs/mp/framework/web/service/SysLoginService.java
  25. 29 10
      mp-service/src/main/java/com/qs/mp/admin/domain/Coupon.java
  26. 1 1
      mp-service/src/main/java/com/qs/mp/admin/domain/CouponChannel.java
  27. 1 1
      mp-service/src/main/java/com/qs/mp/admin/domain/CouponTicket.java
  28. 11 1
      mp-service/src/main/java/com/qs/mp/admin/domain/TicketAwardsPrize.java
  29. 19 0
      mp-service/src/main/java/com/qs/mp/admin/domain/dto/TicketDrawNumDTO.java
  30. 7 0
      mp-service/src/main/java/com/qs/mp/admin/domain/param/TicketParam.java
  31. 8 0
      mp-service/src/main/java/com/qs/mp/admin/service/ICouponService.java
  32. 65 0
      mp-service/src/main/java/com/qs/mp/admin/service/impl/CouponServiceImpl.java
  33. 12 7
      mp-service/src/main/java/com/qs/mp/framework/redis/RedisKey.java
  34. 0 18
      mp-service/src/main/java/com/qs/mp/framework/redis/RedisKeyBuilder.java
  35. 66 72
      mp-service/src/main/java/com/qs/mp/framework/service/impl/AppTokenServiceImpl.java
  36. 6 3
      mp-service/src/main/java/com/qs/mp/framework/service/impl/WxSubscribeMessageImpl.java
  37. 0 3
      mp-service/src/main/java/com/qs/mp/framework/service/impl/WxTemplateMessageImpl.java
  38. 3 3
      mp-service/src/main/java/com/qs/mp/framework/service/impl/WxUrlLinkServiceImpl.java
  39. 10 4
      mp-service/src/main/java/com/qs/mp/pay/service/impl/WalletServiceImpl.java
  40. 12 3
      mp-service/src/main/java/com/qs/mp/system/domain/SysUser.java
  41. 7 25
      mp-service/src/main/java/com/qs/mp/system/domain/vo/WxLoginParams.java
  42. 1 7
      mp-service/src/main/java/com/qs/mp/user/domain/UserCoin.java
  43. 5 1
      mp-service/src/main/java/com/qs/mp/user/domain/UserCoinLog.java
  44. 7 3
      mp-service/src/main/java/com/qs/mp/user/domain/UserCoupon.java
  45. 53 0
      mp-service/src/main/java/com/qs/mp/user/domain/UserCouponChannel.java
  46. 20 2
      mp-service/src/main/java/com/qs/mp/user/domain/UserPrizeStorage.java
  47. 6 0
      mp-service/src/main/java/com/qs/mp/user/domain/UserTicketOrderItem.java
  48. 26 0
      mp-service/src/main/java/com/qs/mp/user/domain/param/TicketOrderParam.java
  49. 63 0
      mp-service/src/main/java/com/qs/mp/user/domain/vo/TicketOrderSettleVO.java
  50. 76 0
      mp-service/src/main/java/com/qs/mp/user/domain/vo/UserCouponVO.java
  51. 13 0
      mp-service/src/main/java/com/qs/mp/user/mapper/UserCouponChannelMapper.java
  52. 8 0
      mp-service/src/main/java/com/qs/mp/user/mapper/UserCouponMapper.java
  53. 7 0
      mp-service/src/main/java/com/qs/mp/user/service/IUserCoinService.java
  54. 16 0
      mp-service/src/main/java/com/qs/mp/user/service/IUserCouponChannelService.java
  55. 8 0
      mp-service/src/main/java/com/qs/mp/user/service/IUserCouponService.java
  56. 17 0
      mp-service/src/main/java/com/qs/mp/user/service/IUserHitPrizeService.java
  57. 10 0
      mp-service/src/main/java/com/qs/mp/user/service/IUserPrizeStorageService.java
  58. 7 0
      mp-service/src/main/java/com/qs/mp/user/service/IUserTicketOrderItemService.java
  59. 0 1
      mp-service/src/main/java/com/qs/mp/user/service/impl/UserCoinLogServiceImpl.java
  60. 36 0
      mp-service/src/main/java/com/qs/mp/user/service/impl/UserCoinServiceImpl.java
  61. 20 0
      mp-service/src/main/java/com/qs/mp/user/service/impl/UserCouponChannelServiceImpl.java
  62. 15 0
      mp-service/src/main/java/com/qs/mp/user/service/impl/UserCouponServiceImpl.java
  63. 116 0
      mp-service/src/main/java/com/qs/mp/user/service/impl/UserHitPrizeServiceImpl.java
  64. 29 1
      mp-service/src/main/java/com/qs/mp/user/service/impl/UserPrizeStorageServiceImpl.java
  65. 13 0
      mp-service/src/main/java/com/qs/mp/user/service/impl/UserTicketOrderItemServiceImpl.java
  66. 3 2
      mp-service/src/main/resources/mapper/admin/CouponMapper.xml
  67. 2 1
      mp-service/src/main/resources/mapper/admin/TicketAwardsPrizeMapper.xml
  68. 4 4
      mp-service/src/main/resources/mapper/channel/ChannelCommissionMapper.xml
  69. 1 2
      mp-service/src/main/resources/mapper/channel/ChannelMapper.xml
  70. 1 1
      mp-service/src/main/resources/mapper/channel/ChannelUserRelMapper.xml
  71. 22 20
      mp-service/src/main/resources/mapper/system/SysUserMapper.xml
  72. 2 3
      mp-service/src/main/resources/mapper/user/UserCoinMapper.xml
  73. 19 0
      mp-service/src/main/resources/mapper/user/UserCouponChannelMapper.xml
  74. 8 1
      mp-service/src/main/resources/mapper/user/UserCouponMapper.xml
  75. 4 2
      mp-service/src/main/resources/mapper/user/UserPrizeStorageMapper.xml
  76. 2 1
      mp-service/src/main/resources/mapper/user/UserTicketOrderItemMapper.xml

+ 3 - 1
mp-admin/src/main/java/com/qs/mp/web/controller/api/channel/ChannelController.java

@@ -239,6 +239,7 @@ public class ChannelController extends BaseApiController {
 			}
 		}
 		try {
+			channel.setUserId(oldChannel.getUserId());
 			channelService.updateChannel(channel, mobileChange);
 		} catch (Exception e) {
 			return AjaxResult.error(e.getMessage());
@@ -469,6 +470,7 @@ public class ChannelController extends BaseApiController {
 			 }
 		}
 		try {
+			channel.setUserId(oldChannel.getUserId());
 			channelService.updateChannel(channel, mobileChange);
 		} catch (Exception e) {
 			return AjaxResult.error("经销商'" + channel.getName() + "'编辑失败");
@@ -578,7 +580,7 @@ public class ChannelController extends BaseApiController {
 			channelVO.setParentName(parentChannel.getName());
 		}
 		// 统计今天的分润金额
-			Date zero = DateUtils.getNowZero(); // 获取当天零点零分时间
+		Date zero = DateUtils.getToday(); // 获取当天零点零分时间
 		QueryWrapper<ChannelCommission> queryWrapper = new QueryWrapper<ChannelCommission>();
 		queryWrapper.select("IFNULL(sum(comm_amt) ,0) as commAmt");
 		queryWrapper.lambda().eq(ChannelCommission::getChannelId, channelId);

+ 5 - 4
mp-admin/src/main/java/com/qs/mp/web/controller/api/channel/mall/ChannelOrderController.java

@@ -37,6 +37,7 @@ import com.qs.mp.common.enums.ChannelCertifyStatusEnum;
 import com.qs.mp.common.enums.ErrorCodeEnum;
 import com.qs.mp.common.exception.ServiceException;
 import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.framework.redis.RedisKey;
 import com.qs.mp.pay.service.IWalletService;
 import com.qs.mp.utils.SecurityUtils;
 import com.qs.mp.web.controller.common.BaseApiController;
@@ -89,7 +90,7 @@ public class ChannelOrderController extends BaseApiController {
   @Autowired
   private RedisCache redisCache;
 
-  private final String CHANNEL_ORDER_CACHE_KEY = "channel_settle_order_";
+
 
   /**
    * 订单列表
@@ -177,7 +178,7 @@ public class ChannelOrderController extends BaseApiController {
     orderSettleVO.setPkgNum(pkgNum);
 
     // 缓存订单结算对象
-    redisCache.setCacheObject(CHANNEL_ORDER_CACHE_KEY + channelId, orderSettleVO, 10, TimeUnit.MINUTES);
+    redisCache.setCacheObject(RedisKey.build(RedisKey.CHANNEL_ORDER_KEY, channelId), orderSettleVO, 10, TimeUnit.MINUTES);
     return AjaxResult.success(orderSettleVO);
   }
 
@@ -193,7 +194,7 @@ public class ChannelOrderController extends BaseApiController {
       LogUtil.info(logger, "账号未认证。channelId:{0}", channelId);
       return AjaxResult.error(ErrorCodeEnum.ERROR_CODE_1016);
     }
-    ChannelOrderSettleVO orderSettleVO = redisCache.getCacheObject(CHANNEL_ORDER_CACHE_KEY + channelId);
+    ChannelOrderSettleVO orderSettleVO = redisCache.getCacheObject(RedisKey.build(RedisKey.CHANNEL_ORDER_KEY, channelId));
     if (null == orderSettleVO) {
       return AjaxResult.error("订单已过期,请重新下单");
     }
@@ -203,7 +204,7 @@ public class ChannelOrderController extends BaseApiController {
     String orderId = channelOrderService.submitOrder(channelId, order.getMemo(), orderSettleVO);
 
     // 清除缓存的订单
-    redisCache.deleteObject(CHANNEL_ORDER_CACHE_KEY + channelId);
+    redisCache.deleteObject(RedisKey.build(RedisKey.CHANNEL_ORDER_KEY, channelId));
 
     return AjaxResult.success(orderId);
   }

+ 107 - 23
mp-admin/src/main/java/com/qs/mp/web/controller/api/common/UserController.java

@@ -4,7 +4,9 @@ import cn.jsms.api.ValidSMSResult;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.qs.mp.common.constant.Constants;
 import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.enums.UserIdentityEnum;
 import com.qs.mp.common.enums.WxActTypeEnum;
 import com.qs.mp.common.jsms.JSMSUtils;
 import com.qs.mp.common.qcloud.QcloudFileUtils;
@@ -15,7 +17,9 @@ import com.qs.mp.common.utils.http.HttpUtils;
 import com.qs.mp.core.domain.LoginUser;
 import com.qs.mp.framework.domain.WxAccount;
 import com.qs.mp.framework.security.handle.HostHolder;
+import com.qs.mp.framework.service.IAppTokenService;
 import com.qs.mp.framework.service.IWxAccountService;
+import com.qs.mp.framework.web.service.SysLoginService;
 import com.qs.mp.system.domain.SysUser;
 import com.qs.mp.system.domain.vo.WxLoginParams;
 import com.qs.mp.system.service.ISysUserService;
@@ -60,10 +64,15 @@ public class UserController extends BaseApiController {
   @Autowired
   private IWxAccountService wxAccountService;
 
-  @Value("${wx.appId}")
-  private String appId;
-  @Value("${wx.appSecret}")
-  private String appSecret;
+  @Value("${wx-channel.appId}")
+  private String channelAppId;
+  @Value("${wx-channel.appSecret}")
+  private String channelAppSecret;
+
+  @Value("${wx-user.appId}")
+  private String userAppId;
+  @Value("${wx-user.appSecret}")
+  private String userAppSecret;
 
   /**
    * 文件上传路径
@@ -77,12 +86,24 @@ public class UserController extends BaseApiController {
   @Value("${cloud.public-bucket-name}")
   private String publicBucketName;
 
+  @Autowired
+  private IAppTokenService appTokenService;
+
+  @Autowired
+  private SysLoginService sysLoginService;
+
 
   @RequestMapping(value = "/user/wxauth", method = RequestMethod.POST)
   @ResponseBody
   public AjaxResult wxauth(@RequestBody WxLoginParams wxLoginParams) {
+    String appId = channelAppId;
+    String appSecret = channelAppSecret;
+    if (wxLoginParams.getIdentity() == UserIdentityEnum.USER.ordinal()) {
+      appId = userAppId;
+      appSecret = userAppSecret;
+    }
     //调用微信后台接口获取openId
-    String res = wxLoginCheck(wxLoginParams, appSecret);
+    String res = wxLoginCheck(wxLoginParams, appId, appSecret);
     JSONObject jsonObject = JSONObject.parseObject(res);
     String openId = jsonObject.getString("openid");
     String sessionKey = jsonObject.getString("session_key");
@@ -96,7 +117,12 @@ public class UserController extends BaseApiController {
     LoginUser loginUser = hostHolder.getUser();
     SysUser user = new SysUser();
     user.setUserId(loginUser.getUserId());
-    user.setOpenId(openId);
+    if (wxLoginParams.getIdentity() == UserIdentityEnum.USER.ordinal()) {
+      user.setOpenId(openId);
+    } else {
+      user.setChannelOpenId(openId);
+    }
+
     user.setSessionKey(sessionKey);
     user.setUnionId(unionId);
     user.setLoginDate(new Date());
@@ -131,6 +157,51 @@ public class UserController extends BaseApiController {
     return success("微信授权成功");
   }
 
+
+  @RequestMapping(value = "/user/wxauth/mobile", method = RequestMethod.POST)
+  @ResponseBody
+  public AjaxResult wxauthMobile(@RequestBody WxLoginParams wxLoginParams) {
+    String appId = userAppId;
+    String appSecret = userAppSecret;
+    if (UserIdentityEnum.isChannel(wxLoginParams.getIdentity())) {
+      appId = channelAppId;
+      appSecret = channelAppSecret;
+    }
+    //调用微信后台接口获取openId
+    String res = getWxMobile(wxLoginParams, appId);
+    JSONObject jsonObject = JSONObject.parseObject(res);
+    int errcode = jsonObject.getIntValue("errcode");
+    if (errcode != 0 ) {
+      LogUtil.error(logger, "手机号获取失败:" + res);
+      return error("手机号获取失败");
+    }
+    JSONObject phoneInfo = jsonObject.getJSONObject("phone_info");
+    String phoneNumber = phoneInfo.getString("phoneNumber");
+
+    if (StringUtils.isBlank(phoneNumber)) {
+      LogUtil.error(logger, "微信手机号不存在:" + res);
+      return error("手机号获取失败");
+    }
+    AjaxResult ajax = AjaxResult.success();
+
+    SysUser sysUser = sysUserService.selectUserByUserName(phoneNumber);
+    if (null == sysUser) {
+      if (wxLoginParams.getIdentity() != UserIdentityEnum.USER.ordinal()) {
+        return error("用户不存在,请联系客服");
+      }
+      // C端直接注册新用户
+      sysUser = new SysUser();
+      sysUser.setUserName(phoneNumber);
+      sysUser.setNickName(phoneNumber);
+      sysUserService.registerUser(sysUser);
+    }
+    Map<String, String> result = sysLoginService.wxAuthLogin(phoneNumber, wxLoginParams.getIdentity());
+    for (String key : result.keySet()) {
+      ajax.put(key, result.get(key));
+    }
+    return ajax;
+  }
+
   /**
    * 重置密码
    */
@@ -162,6 +233,8 @@ public class UserController extends BaseApiController {
     jsonObject.put("userName", loginUser.getUsername());
     jsonObject.put("avatar", loginUser.getUser().getAvatar());
     jsonObject.put("nickName", loginUser.getUser().getNickName());
+    jsonObject.put("openId", loginUser.getUser().getOpenId());
+    jsonObject.put("channelOpenId", loginUser.getUser().getChannelOpenId());
     JSONArray roleKeys = new JSONArray();
     JSONArray roles = new JSONArray();
     loginUser.getUser().getRoles().forEach(sysRole -> roles.add(sysRole.getRoleName()));
@@ -179,13 +252,40 @@ public class UserController extends BaseApiController {
    * @param
    * @return
    */
-  private String wxLoginCheck(WxLoginParams params, String appSecret) {
+  private String wxLoginCheck(WxLoginParams params, String appId, String appSecret) {
     String param = "appid=" + appId + "&secret=" + appSecret + "&js_code=" + params.getCode()
         + "&grant_type=authorization_code";
     String res = HttpUtils.sendGet("https://api.weixin.qq.com/sns/jscode2session", param);
     return res;
   }
 
+  /**
+   * code 换取 手机号
+   *{
+   *     "errcode":0,
+   *     "errmsg":"ok",
+   *     "phone_info": {
+   *         "phoneNumber":"xxxxxx",
+   *         "purePhoneNumber": "xxxxxx",
+   *         "countryCode": 86,
+   *         "watermark": {
+   *             "timestamp": 1637744274,
+   *             "appid": "xxxx"
+   *         }
+   *     }
+   * }
+   * @param
+   * @return
+   */
+  private String getWxMobile(WxLoginParams params, String appId) {
+    String accessToken = appTokenService.getAccessToken(appId);
+    JSONObject jsonObject = new JSONObject();
+    jsonObject.put("code", params.getCode());
+    LogUtil.info(logger, JSONObject.toJSONString(params));
+    String res = HttpUtils.sendPost("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken, jsonObject.toJSONString());
+    return res;
+  }
+
   /**
    * { "subscribe": 1, "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", "nickname": "Band", "sex": 1,
    * "language": "zh_CN", "city": "广州", "province": "广东", "country": "中国", "headimgurl":
@@ -204,22 +304,6 @@ public class UserController extends BaseApiController {
     return res;
   }
 
-  /**
-   * {"access_token":"ACCESS_TOKEN","expires_in":7200}
-   *
-   * @param appId
-   * @param appSecret
-   * @return
-   */
-  private String wxGetAccessToken(String appId, String appSecret) {
-    String param = "appid=" + appId + "&secret=" + appSecret + "&grant_type=client_credential";
-    String res = HttpUtils.sendGet("https://api.weixin.qq.com/cgi-bin/token", param);
-    JSONObject tokenJson = JSONObject.parseObject(res);
-    String token = tokenJson.getString("access_token");
-    return token;
-
-  }
-
   private JSONObject saveHeadImage(String serverUrl,String userId) throws Exception {
     String outFilePath = filePath+"/headImage";
     File outPath = new File(outFilePath);

+ 35 - 19
mp-admin/src/main/java/com/qs/mp/web/controller/api/common/UserTicketController.java → mp-admin/src/main/java/com/qs/mp/web/controller/api/user/UserTicketController.java

@@ -8,7 +8,7 @@
  * 版权所有,侵权必究!
  */
 
-package com.qs.mp.web.controller.api.common;
+package com.qs.mp.web.controller.api.user;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -32,6 +32,7 @@ import com.qs.mp.common.enums.UserTicketOrderStatusEnum;
 import com.qs.mp.common.utils.StringUtils;
 import com.qs.mp.user.domain.UserTicketOrder;
 import com.qs.mp.user.domain.UserTicketOrderItem;
+import com.qs.mp.user.service.IUserHitPrizeService;
 import com.qs.mp.user.service.IUserTicketOrderItemService;
 import com.qs.mp.user.service.IUserTicketOrderService;
 import com.qs.mp.utils.SecurityUtils;
@@ -51,7 +52,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 @RequestMapping("/api/v1/mp/user")
-@Api(tags = "渠道盲票商城接口")
+@Api(tags = "盲票商城接口")
 @AllArgsConstructor
 public class UserTicketController extends BaseApiController {
 
@@ -68,6 +69,9 @@ public class UserTicketController extends BaseApiController {
   @Autowired
   private IUserTicketOrderItemService userTicketOrderItemService;
 
+  @Autowired
+  private IUserHitPrizeService userHitPrizeService;
+
   @Autowired
   private MapperFacade mapperFacade;
 
@@ -128,9 +132,8 @@ public class UserTicketController extends BaseApiController {
 
     // 已付款的盲票,需要校验当前用户是否有权限查看
     if (ticket.getStatus() == TicketStatusEnum.ACTIVATED) {
-      List<UserTicketOrderItem> itemList = userTicketOrderItemService.list(new QueryWrapper<UserTicketOrderItem>().eq("t1.ticket_id", param.getTicketId())
-          .eq("t1.userId", userId).eq("t2.status", UserTicketOrderStatusEnum.FINISHED));
-      if (CollectionUtils.isEmpty(itemList)) {
+      UserTicketOrderItem orderItem = userTicketOrderItemService.queryFinishedOrderItem(userId, param.getTicketId());
+      if (null == orderItem) {
         return AjaxResult.error(ErrorCodeEnum.ERROR_CODE_1018);
       }
     }
@@ -158,6 +161,7 @@ public class UserTicketController extends BaseApiController {
   @PostMapping("/ticket/queryHitPrizeList")
   @ApiOperation(value = "查看兑奖奖品" , notes = "根据盲票ID,查看兑奖奖品")
   public AjaxResult queryHitPrizeList(@RequestBody TicketParam param) {
+    Long userId = SecurityUtils.getLoginUser().getUserId();
     if (StringUtils.isBlank(param.getTicketId())) {
       return AjaxResult.error("参数异常,盲票ID缺失");
     }
@@ -173,23 +177,35 @@ public class UserTicketController extends BaseApiController {
       return AjaxResult.error(ErrorCodeEnum.ERROR_CODE_1019);
     }
 
-//    UserTicketOrder ticketOrder =
+    List<TicketAwardsPrize> ticketAwardsPrizes = userHitPrizeService.listPrize(ticket, userId);
+    return AjaxResult.success(ticketAwardsPrizes);
+  }
 
-    TicketBox ticketBox = ticketBoxService.getById(ticket.getBoxId());
+  /**
+   * 兑奖
+   */
+  @PostMapping("/ticket/cashPrize")
+  @ApiOperation(value = "兑奖" , notes = "选择奖品兑奖")
+  public AjaxResult cashPrize(@RequestBody TicketParam param) {
+    Long userId = SecurityUtils.getLoginUser().getUserId();
+    if (StringUtils.isBlank(param.getTicketId()) || StringUtils.isBlank(param.getAwardsId())
+    || StringUtils.isBlank(param.getPrizeId())) {
+      return AjaxResult.error("参数缺失");
+    }
 
-    TicketVO ticketVO = new TicketVO();
-    ticketVO.setPrizeList(ticketAwardsPrizeService.listPrizeVO(new QueryWrapper<TicketAwardsPrize>()
-        .eq("t1.box_id", ticketBox.getBoxId()).orderByAsc("t1.sort").orderByDesc("t2.value")));
-    ticketVO.setTicketId(ticket.getTicketId());
-    ticketVO.setBoxId(ticket.getBoxId());
-    ticketVO.setTitle(ticketBox.getTitle());
-    ticketVO.setSerialNo(ticket.getSerialNo());
-    ticketVO.setFacePrice(ticket.getFacePrice());
-    ticketVO.setSalePrice(ticketBox.getSalePrice());
-    ticketVO.setPlainLuckyNum(ticket.getPlainLuckyNum());
-    ticketVO.setStatus(ticket.getStatus().getValue());
+    Ticket ticket = ticketService.getById(param.getTicketId());
+    if (null == ticket) {
+      return AjaxResult.error("参数异常,盲票不存在");
+    }
+    if (ticket.getStatus() == TicketStatusEnum.CASHED) {
+      return AjaxResult.error(ErrorCodeEnum.ERROR_CODE_1017);
+    }
+    if (ticket.getStatus() != TicketStatusEnum.ACTIVATED) {
+      return AjaxResult.error(ErrorCodeEnum.ERROR_CODE_1019);
+    }
 
-    return AjaxResult.success(ticketVO);
+    userHitPrizeService.cashPrize(ticket, userId, param.getAwardsId(), param.getPrizeId());
+    return AjaxResult.success();
   }
 
 }

+ 178 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/api/user/UserTicketOrderController.java

@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.qs.mp.web.controller.api.user;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qs.mp.admin.domain.Coupon;
+import com.qs.mp.admin.domain.Ticket;
+import com.qs.mp.admin.domain.TicketAwardsPrize;
+import com.qs.mp.admin.domain.TicketBox;
+import com.qs.mp.admin.domain.param.TicketBoxParam;
+import com.qs.mp.admin.domain.param.TicketParam;
+import com.qs.mp.admin.domain.vo.TicketBoxListVO;
+import com.qs.mp.admin.domain.vo.TicketBoxVO;
+import com.qs.mp.admin.domain.vo.TicketVO;
+import com.qs.mp.admin.service.ITicketAwardsPrizeService;
+import com.qs.mp.admin.service.ITicketBoxService;
+import com.qs.mp.admin.service.ITicketService;
+import com.qs.mp.channel.domain.Channel;
+import com.qs.mp.channel.domain.ChannelAddr;
+import com.qs.mp.channel.domain.ChannelCart;
+import com.qs.mp.channel.domain.ChannelOrder;
+import com.qs.mp.channel.domain.param.ChannelOrderParam;
+import com.qs.mp.channel.domain.vo.ChannelCartVO;
+import com.qs.mp.channel.domain.vo.ChannelOrderSettleVO;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.core.redis.RedisCache;
+import com.qs.mp.common.enums.ChannelCertifyStatusEnum;
+import com.qs.mp.common.enums.ErrorCodeEnum;
+import com.qs.mp.common.enums.TicketBoxStatusEnum;
+import com.qs.mp.common.enums.TicketStatusEnum;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.framework.redis.RedisKey;
+import com.qs.mp.user.domain.UserTicketOrder;
+import com.qs.mp.user.domain.UserTicketOrderItem;
+import com.qs.mp.user.domain.param.TicketOrderParam;
+import com.qs.mp.user.domain.vo.TicketOrderSettleVO;
+import com.qs.mp.user.domain.vo.UserCouponVO;
+import com.qs.mp.user.service.IUserCouponService;
+import com.qs.mp.user.service.IUserHitPrizeService;
+import com.qs.mp.user.service.IUserTicketOrderItemService;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.common.BaseApiController;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import javax.validation.Valid;
+import lombok.AllArgsConstructor;
+import ma.glasnost.orika.MapperFacade;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+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;
+
+
+@RestController
+@RequestMapping("/api/v1/mp/user/ticket")
+@Api(tags = "用户端购买盲票接口")
+@AllArgsConstructor
+public class UserTicketOrderController extends BaseApiController {
+
+
+  @Autowired
+  private ITicketBoxService ticketBoxService;
+
+  @Autowired
+  private ITicketAwardsPrizeService ticketAwardsPrizeService;
+
+  @Autowired
+  private ITicketService ticketService;
+
+  @Autowired
+  private IUserTicketOrderItemService userTicketOrderItemService;
+
+  @Autowired
+  private IUserHitPrizeService userHitPrizeService;
+
+  @Autowired
+  private IUserCouponService userCouponService;
+
+  @Autowired
+  private MapperFacade mapperFacade;
+
+  @Autowired
+  private RedisCache redisCache;
+
+
+  /**
+   * 订单结算,每次更换优惠券时需重新请求此接口
+   */
+  @PostMapping("/order/settle")
+  @ApiOperation(value = "订单结算" , notes = "购物车里点结算或者单个盲票组商品上点立即购买")
+  public AjaxResult settle(@Valid @RequestBody TicketOrderParam param) {
+    if (StringUtils.isBlank(param.getTicketId()) && StringUtils.isBlank(param.getBoxId())) {
+      return AjaxResult.error("参数缺失");
+    }
+    Long userId = SecurityUtils.getLoginUser().getUserId();
+
+    TicketOrderSettleVO orderSettleVO = new TicketOrderSettleVO();
+
+
+    // 订单金额计算
+    Integer orderAmt = 0;
+    Integer discountAmt = 0;
+    String picUrl;
+    if (StringUtils.isNotBlank(param.getTicketId())) {
+      // 线下扫码购票
+      Ticket ticket = ticketService.getById(param.getTicketId());
+      TicketBox ticketBox = ticketBoxService.getById(ticket.getBoxId());
+      orderAmt = ticketBox.getSalePrice();
+      picUrl = ticketBox.getPicUrl();
+    } else {
+      // 线上购票
+      TicketBox ticketBox = ticketBoxService.getById(param.getBoxId());
+      orderAmt = ticketBox.getSalePrice() * param.getOrderNum();
+      picUrl = ticketBox.getPicUrl();
+    }
+
+    if (param.getAutoCoupon() == 1) {
+      // 系统自动选择优惠券,按照金额从大到小排序
+      List<UserCouponVO> userCouponList = userCouponService.listTicketOrderCoupon(userId);
+      for (UserCouponVO userCouponVO : userCouponList) {
+        if (orderAmt >= userCouponVO.getMinOrderAmt() && orderAmt >= userCouponVO.getDiscount()) {
+          orderSettleVO.getCouponList().add(userCouponVO);
+          discountAmt = userCouponVO.getDiscount();
+        }
+      }
+    } else {
+      // 查询指定券
+    }
+    orderSettleVO.setOrderAmt(orderAmt);
+    orderSettleVO.setDiscountAmt(discountAmt);
+    // 当前没有运费和优惠,实付金额=订单金额
+    orderSettleVO.setPayAmt(orderAmt - discountAmt);
+    orderSettleVO.setOrderNum(param.getOrderNum());
+    orderSettleVO.setBoxId(param.getBoxId());
+    orderSettleVO.setTicketId(param.getTicketId());
+    orderSettleVO.setPicUrl(picUrl);
+
+    // 缓存订单结算对象
+    redisCache.setCacheObject(RedisKey.build(RedisKey.USER_TICKET_ORDER_KEY, userId), orderSettleVO, 10, TimeUnit.MINUTES);
+    return AjaxResult.success(orderSettleVO);
+  }
+
+
+  /**
+   * 提交订单
+   */
+  @PostMapping("/order/submit")
+  @ApiOperation(value = "提交订单" , notes = "在订单确认页面提交")
+  public AjaxResult submit(@RequestBody UserTicketOrder order) {
+    Long userId = SecurityUtils.getLoginUser().getUserId();
+    TicketOrderSettleVO orderSettleVO = redisCache.getCacheObject(RedisKey.build(RedisKey.USER_TICKET_ORDER_KEY, userId));
+    if (null == orderSettleVO) {
+      return AjaxResult.error("订单已过期,请重新下单");
+    }
+    //String orderId = channelOrderService.submitOrder(channelId, order.getMemo(), orderSettleVO);
+
+    // 清除缓存的订单
+    redisCache.deleteObject(RedisKey.build(RedisKey.USER_TICKET_ORDER_KEY, userId));
+
+    return AjaxResult.success();
+  }
+
+}

+ 16 - 10
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysLoginController.java

@@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 /**
  * 登录验证
- * 
+ *
  * @author ygp
  */
 @RestController
@@ -46,7 +46,7 @@ public class SysLoginController
 
     /**
      * 登录方法
-     * 
+     *
      * @param loginBody 登录信息
      * @return 结果
      */
@@ -59,9 +59,11 @@ public class SysLoginController
             loginBody.getUuid());
 
         // 生成令牌
-        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
+        Map<String, String> result = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
                 loginBody.getUuid(), loginBody.getIdentity());
-        ajax.put(Constants.TOKEN, token);
+        for (String key : result.keySet()) {
+            ajax.put(key, result.get(key));
+        }
         return ajax;
     }
 
@@ -70,9 +72,11 @@ public class SysLoginController
     {
         AjaxResult ajax = AjaxResult.success();
         // 生成令牌
-        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
+        Map<String, String> result = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
             loginBody.getUuid(), loginBody.getIdentity());
-        ajax.put(Constants.TOKEN, token);
+        for (String key : result.keySet()) {
+            ajax.put(key, result.get(key));
+        }
         return ajax;
     }
 
@@ -84,14 +88,16 @@ public class SysLoginController
             return AjaxResult.error("请输入6位验证码");
         }
         AjaxResult ajax = AjaxResult.success();
-        String token = loginService.login(smsCode);
-        ajax.put(Constants.TOKEN, token);
+        Map<String, String> result = loginService.login(smsCode);
+        for (String key : result.keySet()) {
+            ajax.put(key, result.get(key));
+        }
         return ajax;
     }
 
     /**
      * 获取用户信息
-     * 
+     *
      * @return 用户信息
      */
     @GetMapping("getInfo")
@@ -111,7 +117,7 @@ public class SysLoginController
 
     /**
      * 获取路由信息
-     * 
+     *
      * @return 路由信息
      */
     @GetMapping("getRouters")

+ 6 - 2
mp-admin/src/main/resources/application.yml

@@ -123,10 +123,14 @@ cloud:
   #private-bucket-name: ygp-auth
 
 
-#小程序appid和密钥
-wx:
+#渠道端小程序appid和密钥
+wx-channel:
   appId: wxc3a684f1196f6e15
   appSecret: 7708655ba65f04de2a42f23d20ab4134
+#用户端小程序appid和密钥
+wx-user:
+  appId: wx8533800e393dbd6b
+  appSecret: f7a104e09d6c2e55445be2a45c4ab551
 #公众号appid
 wxgzh:
   appId: wxfe9785e665c741a2

+ 6 - 1
mp-common/src/main/java/com/qs/mp/common/constant/Constants.java

@@ -4,7 +4,7 @@ import io.jsonwebtoken.Claims;
 
 /**
  * 通用常量信息
- * 
+ *
  * @author ygp
  */
 public class Constants
@@ -94,6 +94,11 @@ public class Constants
      */
     public static final String TOKEN = "token";
 
+    /**
+     * 渠道端角色
+     */
+    public static final String CHANNEL_ROLE = "channelRole";
+
     /**
      * 令牌前缀
      */

+ 43 - 0
mp-common/src/main/java/com/qs/mp/common/enums/ChannelRoleEnum.java

@@ -0,0 +1,43 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONType;
+import com.baomidou.mybatisplus.annotation.IEnum;
+import com.qs.mp.common.json.EnumValueDeserializer;
+
+/**
+ *
+ * 渠道角色
+ *
+ */
+@JSONType(deserializer = EnumValueDeserializer.class)
+public enum ChannelRoleEnum implements IEnum<String> {
+
+  CHANNEL("channel", "渠道"),
+  SALESITE("salesite", "经销商");
+
+  private final String value;
+  private final String desc;
+
+  ChannelRoleEnum(final String value, final String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public String getValue() {
+    return value;
+  }
+
+  /**
+   * 重写toString,单个转化成json
+   * @return
+   */
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value",value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 49 - 0
mp-common/src/main/java/com/qs/mp/common/enums/CoinLogTypeEnum.java

@@ -0,0 +1,49 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ * @auther zhongcp
+ * @create 2022 2022/3/7 2:30 下午
+ * @describe
+ */
+public enum CoinLogTypeEnum implements IEnum<Integer> {
+
+  PRIZE(1, "盲票奖品"),
+  CASHED(2, "商品兑换");
+
+
+  private final int value;
+  private final String desc;
+  public static final int INCOME = 1;   //收支类型 1收入
+  public static final int EXPENSES = 0;  //0支出
+
+  CoinLogTypeEnum(int value, String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  public static CoinLogTypeEnum getCoinLogTypeEnum(int value) {
+    for (CoinLogTypeEnum coinLogTypeEnum : CoinLogTypeEnum.values()) {
+      if (coinLogTypeEnum.getValue() == value) {
+        return coinLogTypeEnum;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public Integer getValue() {
+    return value;
+  }
+
+
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value", value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 39 - 0
mp-common/src/main/java/com/qs/mp/common/enums/CouponDiscountTypeEnum.java

@@ -0,0 +1,39 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ * @auther zhongcp
+ * @create 2022 2022/3/7 2:30 下午
+ * @describe
+ */
+public enum CouponDiscountTypeEnum implements IEnum<Integer> {
+
+  MONEY_OFF(1, "代金券"),
+  DISCOUNT(2, "折扣券"),
+  EXCHANGE(3, "兑换券");
+
+
+  private final int value;
+  private final String desc;
+
+  CouponDiscountTypeEnum(int value, String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public Integer getValue() {
+    return value;
+  }
+
+
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value", value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 38 - 0
mp-common/src/main/java/com/qs/mp/common/enums/CouponDistributeTypeEnum.java

@@ -0,0 +1,38 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ * @auther zhongcp
+ * @create 2022 2022/3/7 2:30 下午
+ * @describe
+ */
+public enum CouponDistributeTypeEnum implements IEnum<Integer> {
+
+  SYSTEM(1, "系统发放"),
+  USER_DRAW(2, "用户主动领取");
+
+
+  private final int value;
+  private final String desc;
+
+  CouponDistributeTypeEnum(int value, String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public Integer getValue() {
+    return value;
+  }
+
+
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value", value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 40 - 0
mp-common/src/main/java/com/qs/mp/common/enums/CouponStatusEnum.java

@@ -0,0 +1,40 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ *
+ * 卡券上下架状态
+ *
+ */
+public enum CouponStatusEnum implements IEnum<String> {
+
+  PUT_ON("on", "上架"),
+  PUT_OFF("off", "下架");
+
+  private final String value;
+  private final String desc;
+
+  CouponStatusEnum(final String value, final String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public String getValue() {
+    return value;
+  }
+
+  /**
+   * 重写toString,单个转化成json
+   * @return
+   */
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value",value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 39 - 0
mp-common/src/main/java/com/qs/mp/common/enums/CouponTypeEnum.java

@@ -0,0 +1,39 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ * @auther zhongcp
+ * @create 2022 2022/3/7 2:30 下午
+ * @describe
+ */
+public enum CouponTypeEnum implements IEnum<Integer> {
+
+  TICKET_ORDER(1, "用户盲票购买优惠券"),
+  SITE_CONSUME(2, "用户门店消费优惠券"),
+  CHANNEL(3, "经销商盲票采购优惠券");
+
+
+  private final int value;
+  private final String desc;
+
+  CouponTypeEnum(int value, String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public Integer getValue() {
+    return value;
+  }
+
+
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value", value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 39 - 0
mp-common/src/main/java/com/qs/mp/common/enums/CouponUseAreaEnum.java

@@ -0,0 +1,39 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ * @auther zhongcp
+ * @create 2022 2022/3/7 2:30 下午
+ * @describe
+ */
+public enum CouponUseAreaEnum implements IEnum<Integer> {
+
+  COMMON(0, "通用"),
+  PRE_SCOPE(1, "指定范围"), // 生成优惠券的时候设定使用范围
+  POST_SCOPE(2, "指定范围"); // 发放的时候动态设定使用范围
+
+
+  private final int value;
+  private final String desc;
+
+  CouponUseAreaEnum(int value, String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public Integer getValue() {
+    return value;
+  }
+
+
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value", value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 39 - 0
mp-common/src/main/java/com/qs/mp/common/enums/PrizeStorageInTypeEnum.java

@@ -0,0 +1,39 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ * 奖品入库类型
+ * @auther zhongcp
+ * @create 2022 2022/3/7 2:30 下午
+ * @describe
+ */
+public enum PrizeStorageInTypeEnum implements IEnum<Integer> {
+
+  TICKET_CASHED(1, "奖品兑奖"),
+  COIN_EXCHANGE(2, "盲豆兑换");
+
+
+  private final int value;
+  private final String desc;
+
+  PrizeStorageInTypeEnum(int value, String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public Integer getValue() {
+    return value;
+  }
+
+
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value", value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 38 - 0
mp-common/src/main/java/com/qs/mp/common/enums/PrizeStorageStatusEnum.java

@@ -0,0 +1,38 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ * @auther zhongcp
+ * @create 2022 2022/3/7 2:30 下午
+ * @describe
+ */
+public enum PrizeStorageStatusEnum implements IEnum<Integer> {
+
+  NOT_DISTRIBUTED(1, "待提货"),
+  HAS_DISTRIBUTED(2, "已提货");
+
+
+  private final int value;
+  private final String desc;
+
+  PrizeStorageStatusEnum(int value, String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public Integer getValue() {
+    return value;
+  }
+
+
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value", value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 44 - 0
mp-common/src/main/java/com/qs/mp/common/enums/TicketPrizeTypeEnum.java

@@ -0,0 +1,44 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONType;
+import com.baomidou.mybatisplus.annotation.IEnum;
+import com.qs.mp.common.json.EnumValueDeserializer;
+
+/**
+ *
+ * 盲票奖品类型
+ *
+ */
+@JSONType(deserializer = EnumValueDeserializer.class)
+public enum TicketPrizeTypeEnum implements IEnum<String> {
+
+  GOODS("goods", "商品"),
+  COUPON("coupon", "优惠券"),
+  COIN("coin", "盲币");
+
+  private final String value;
+  private final String desc;
+
+  TicketPrizeTypeEnum(final String value, final String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public String getValue() {
+    return value;
+  }
+
+  /**
+   * 重写toString,单个转化成json
+   * @return
+   */
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value",value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 41 - 0
mp-common/src/main/java/com/qs/mp/common/enums/UserCouponStatusEnum.java

@@ -0,0 +1,41 @@
+package com.qs.mp.common.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ *
+ * 用户卡券使用状态
+ *
+ */
+public enum UserCouponStatusEnum implements IEnum<Integer> {
+
+  UNUSED(1, "未使用"),
+  USED(2, "已使用"),
+  OVERDUE(3, "已过期"),;
+
+  private final Integer value;
+  private final String desc;
+
+  UserCouponStatusEnum(final Integer value, final String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public Integer getValue() {
+    return value;
+  }
+
+  /**
+   * 重写toString,单个转化成json
+   * @return
+   */
+  @Override
+  public String toString() {
+    JSONObject object = new JSONObject();
+    object.put("value",value);
+    object.put("desc", desc);
+    return object.toString();
+  }
+}

+ 8 - 4
mp-common/src/main/java/com/qs/mp/common/enums/UserIdentityEnum.java

@@ -8,11 +8,15 @@ package com.qs.mp.common.enums;
  */
 public enum UserIdentityEnum {
 
-	CHANNEL, // 代理渠道
-	SALESITE // 经销商
+	CHANNEL, // 渠道端
+	USER, // 用户
+	ADMIN // 管理后台
 	;
-	public static boolean isChannel(UserIdentityEnum identity) {
-		return identity == CHANNEL || identity == SALESITE;
+	public static boolean isChannel(int identity) {
+		return identity == CHANNEL.ordinal();
+	}
+	public static boolean isUser(int identity) {
+		return identity == USER.ordinal();
 	}
 
 	public static UserIdentityEnum valueOf(int ordinal) {

+ 87 - 19
mp-common/src/main/java/com/qs/mp/common/utils/DateUtils.java

@@ -6,10 +6,15 @@ import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
 import org.apache.commons.lang3.time.DateFormatUtils;
+import org.joda.time.DateTime;
+import org.joda.time.Days;
+import org.joda.time.Months;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
 
 /**
  * 时间工具类
- * 
+ *
  * @author ygp
  */
 public class DateUtils extends org.apache.commons.lang3.time.DateUtils
@@ -18,20 +23,26 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
 
     public static String YYYY_MM = "yyyy-MM";
 
+    public static String YYYYMM = "yyyyMM";
+
     public static String YYYY_MM_DD = "yyyy-MM-dd";
 
     public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
 
+    public static String YYYYMMDD = "yyyyMMdd";
+
+    public static String YYYYDOTMMDOTDD = "yyyy.MM.dd";
+
     public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
-    
+
     private static String[] parsePatterns = {
-            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", 
+            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
             "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
             "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
 
     /**
      * 获取当前Date型日期
-     * 
+     *
      * @return Date() 当前日期
      */
     public static Date getNowDate()
@@ -39,9 +50,14 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
         return new Date();
     }
 
+    public static Date getToday() {
+        String s = parseDateToStr(YYYY_MM_DD, new Date());
+        return dateTime(YYYY_MM_DD, s);
+    }
+
     /**
      * 获取当前日期, 默认格式为yyyy-MM-dd
-     * 
+     *
      * @return String
      */
     public static String getDate()
@@ -86,6 +102,45 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
         }
     }
 
+    public static final Date parseDateToDay(Date date) {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        String s = sdf.format(date);
+        try {
+            return sdf.parse(s);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 计算两个日期之间相差多少天,一个月按30天算
+     * @param begin
+     * @param end
+     * @return
+     */
+    public static final int diff(Date begin, Date end) {
+        DateTimeFormatter formatter = DateTimeFormat.forPattern(YYYY_MM_DD);
+        DateTime startDay = formatter.parseDateTime(parseDateToStr(YYYY_MM_DD, begin));
+        DateTime endDay = formatter.parseDateTime(parseDateToStr(YYYY_MM_DD, end));
+        int days = Days.daysBetween(startDay, endDay).getDays();
+        return days;
+    }
+
+    /**
+     * 计算两个日期之间相差多少个月
+     * @param begin
+     * @param end
+     * @return
+     */
+    public static final int diffMonth(Date begin, Date end) {
+
+        DateTimeFormatter formatter = DateTimeFormat.forPattern(YYYY_MM_DD);
+        DateTime startDay = formatter.parseDateTime(parseDateToStr(YYYY_MM_DD, begin));
+        DateTime endDay = formatter.parseDateTime(parseDateToStr(YYYY_MM_DD, end));
+        int months = Months.monthsBetween(startDay, endDay).getMonths();
+        return months;
+    }
+
     /**
      * 日期路径 即年/月/日 如2018/08/08
      */
@@ -104,6 +159,20 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
         return DateFormatUtils.format(now, "yyyyMMdd");
     }
 
+    public static final Date parseStrToDate(String str, String pattern){
+        if (str == null)
+        {
+            return null;
+        }
+        try
+        {
+            return parseDate(str, pattern);
+        }
+        catch (ParseException e)
+        {
+            return null;
+        }
+    }
     /**
      * 日期型字符串转化为日期 格式
      */
@@ -122,7 +191,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
             return null;
         }
     }
-    
+
     /**
      * 获取服务器启动时间
      */
@@ -153,18 +222,17 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
         // long sec = diff % nd % nh % nm / ns;
         return day + "天" + hour + "小时" + min + "分钟";
     }
-    
-    /**
-     * 获取当天零点零分时间
-     * @return
-     */
-    public static Date getNowZero() {
-	    Calendar calendar = Calendar.getInstance();
-        calendar.setTime(new Date());
-        calendar.set(Calendar.HOUR_OF_DAY, 0);
-        calendar.set(Calendar.MINUTE, 0);
-        calendar.set(Calendar.SECOND, 0);
-        Date zero = calendar.getTime();
-        return zero;
+
+    public static Date getNowAddMonth(Date now, int months) {
+    	SimpleDateFormat sj = new SimpleDateFormat("yyyyMMdd");
+        try {
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(now);
+            calendar.add(Calendar.MONTH, months);
+            return calendar.getTime();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
     }
 }

+ 3 - 3
mp-common/src/main/java/com/qs/mp/common/utils/http/HttpUtils.java

@@ -22,7 +22,7 @@ import com.qs.mp.common.constant.Constants;
 
 /**
  * 通用http发送方法
- * 
+ *
  * @author ygp
  */
 public class HttpUtils
@@ -119,7 +119,7 @@ public class HttpUtils
         try
         {
             String urlNameString = url;
-            log.info("sendPost - {}", urlNameString);
+            log.info("sendPost - {}", urlNameString, param);
             URL realUrl = new URL(urlNameString);
             URLConnection conn = realUrl.openConnection();
             conn.setRequestProperty("accept", "*/*");
@@ -259,4 +259,4 @@ public class HttpUtils
             return true;
         }
     }
-}
+}

+ 3 - 1
mp-framework/src/main/java/com/qs/mp/framework/config/SecurityConfig.java

@@ -106,7 +106,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                     "/captchaImage",
                     "/auth/mobile",
                     "/wxlogin",
-                    "/api/v1/mp/sms/sendSmsCode").anonymous()
+                    "/api/v1/mp/sms/sendSmsCode",
+                    "/api/v1/mp/user/wxauth/mobile").anonymous()
                 .antMatchers(
                         HttpMethod.GET,
                         "/",
@@ -124,6 +125,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .antMatchers("/*/api-docs").anonymous()
                 .antMatchers("/druid/**").anonymous()
                 .antMatchers("/service/notify/**").anonymous()
+                .antMatchers("/api/v1/mp/user/mall/**").anonymous()
 
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()

+ 33 - 123
mp-framework/src/main/java/com/qs/mp/framework/web/service/SysLoginService.java

@@ -1,9 +1,11 @@
 package com.qs.mp.framework.web.service;
 
+import com.alibaba.fastjson.JSONObject;
 import com.qs.mp.channel.domain.Channel;
 import com.qs.mp.channel.service.IChannelService;
 import com.qs.mp.common.constant.Constants;
 import com.qs.mp.common.core.redis.RedisCache;
+import com.qs.mp.common.enums.ChannelRoleEnum;
 import com.qs.mp.common.enums.UserIdentityEnum;
 import com.qs.mp.common.exception.ServiceException;
 import com.qs.mp.common.exception.user.CaptchaException;
@@ -22,6 +24,8 @@ import com.qs.mp.system.domain.SysUser;
 import com.qs.mp.system.service.ISysConfigService;
 import com.qs.mp.system.service.ISysUserService;
 
+import java.util.HashMap;
+import java.util.Map;
 import javax.annotation.Resource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,6 +34,7 @@ import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.stereotype.Component;
 
 /**
@@ -61,6 +66,9 @@ public class SysLoginService
     @Autowired
     private IChannelService channelService;
 
+    @Autowired
+    private UserDetailsService userDetailsService;
+
     /**
      * 登录验证
      *
@@ -71,7 +79,7 @@ public class SysLoginService
      * @param identity 唯一标识
      * @return 结果
      */
-    public String login(String username, String password, String code, String uuid, int identity)
+    public Map<String, String> login(String username, String password, String code, String uuid, int identity)
     {
         // 用户验证
         Authentication authentication = null;
@@ -97,84 +105,35 @@ public class SysLoginService
         }
         AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
         LoginUser loginUser = (LoginUser) authentication.getPrincipal();
+        return buildUserToken(identity, loginUser);
+    }
+
+    private Map<String, String> buildUserToken(int identity, LoginUser loginUser) {
         recordLoginInfo(loginUser.getUser());
+        HashMap<String, String> result = new HashMap<>();
 
         loginUser.setIdentity(UserIdentityEnum.valueOf(identity));
-        if (!loginUser.getUsername().equals("admin") && UserIdentityEnum.isChannel(loginUser.getIdentity())) {
-            fillChannelInfo(loginUser);
+        if (!loginUser.getUsername().equals("admin") && UserIdentityEnum.isChannel(
+            loginUser.getIdentity().ordinal())) {
+            ChannelRoleEnum roleEnum = fillChannelInfo(loginUser);
+            result.put(Constants.CHANNEL_ROLE, roleEnum.getValue());
         }
-/*
-        // 根据用户身份,设置对应的属性
-        loginUser.setIdentity(UserIdentityEnum.valueOf(identity));
 
-        if(loginUser.isCustomer()){
-            fillCustomerInfo(loginUser);
-        }*/
-//        if(loginUser.isMerchant()){
-//            fillMerchantInfoByStaffs(loginUser);
-//        }
         // 生成token
-        return tokenService.createToken(loginUser);
+        String token = tokenService.createToken(loginUser);
+        result.put(Constants.TOKEN, token);
+
+        return result;
     }
 
-    /**
-     * 登录验证
-     *
-     * @param username 用户名
-     * @param code 验证码
-     * @param uuid 唯一标识
-     * @param identity 唯一标识
-     * @return 结果
-     */
-    public String loginBySms(String username, String code, String uuid, int identity)
+    public Map<String, String> wxAuthLogin(String username, int identity)
     {
-        // 校验短信验证码
-        validateSMSCaptcha(username, code, uuid);
-
-        // 用户验证
-        Authentication authentication = null;
-        try
-        {
-            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
-            authentication = authenticationManager
-                    .authenticate(new UsernamePasswordAuthenticationToken(username, code));
-        }
-        catch (Exception e)
-        {
-            if (e instanceof BadCredentialsException)
-            {
-                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
-                throw new UserPasswordNotMatchException();
-            }
-            else
-            {
-                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
-                throw new ServiceException(e.getMessage());
-            }
-        }
         AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
-        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
-        recordLoginInfo(loginUser.getUser());
-
-        loginUser.setIdentity(UserIdentityEnum.valueOf(identity));
-        if (!loginUser.getUsername().equals("admin") && UserIdentityEnum.isChannel(loginUser.getIdentity())) {
-            fillChannelInfo(loginUser);
-        }
-/*
-        // 根据用户身份,设置对应的属性
-        loginUser.setIdentity(UserIdentityEnum.valueOf(identity));
-
-        if(loginUser.isCustomer()){
-            fillCustomerInfo(loginUser);
-        }*/
-//        if(loginUser.isMerchant()){
-//            fillMerchantInfoByStaffs(loginUser);
-//        }
-        // 生成token
-        return tokenService.createToken(loginUser);
+        LoginUser loginUser = (LoginUser) userDetailsService.loadUserByUsername(username);
+        return buildUserToken(identity, loginUser);
     }
 
-    public String login(SmsCode smsCode)
+    public Map<String, String> login(SmsCode smsCode)
     {
         // 用户验证
         Authentication authentication = null;
@@ -201,29 +160,7 @@ public class SysLoginService
         }
         AsyncManager.me().execute(AsyncFactory.recordLogininfor(smsCode.getMobile(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
         LoginUser loginUser = (LoginUser) authentication.getPrincipal();
-        recordLoginInfo(loginUser.getUser());
-
-        loginUser.setIdentity(UserIdentityEnum.valueOf(smsCode.getIdentity()));
-        if (!loginUser.getUsername().equals("admin") && UserIdentityEnum.isChannel(loginUser.getIdentity())) {
-            fillChannelInfo(loginUser);
-        }
-/*
-        // 根据用户身份,设置对应的属性
-        loginUser.setIdentity(UserIdentityEnum.valueOf(smsCode.getIdentity()));
-        logger.info("login user info :"+ JSONObject.toJSONString(loginUser));
-
-        if(loginUser.isCustomer()){
-            fillCustomerInfo(loginUser);
-        }*/
-//        if(loginUser.isMerchant()){
-//            fillMerchantInfoByStaffs(loginUser);
-//        }
-
-        logger.info("prepare create token 。。。");
-        String token = tokenService.createToken(loginUser);
-        logger.info("token:"+token);
-        // 生成token
-        return token;
+        return buildUserToken(smsCode.getIdentity(), loginUser);
     }
 
     /**
@@ -251,31 +188,6 @@ public class SysLoginService
         }
     }
 
-    /**
-     * 校验验证码
-     *
-     * @param username 用户名
-     * @param code 短信验证码
-     * @param uuid 唯一标识
-     * @return 结果
-     */
-    public void validateSMSCaptcha(String username, String code, String uuid)
-    {
-        String verifyKey = Constants.SMS_CAPTCHA_CODE_KEY + uuid;
-        String captcha = redisCache.getCacheObject(verifyKey);
-        redisCache.deleteObject(verifyKey);
-        if (captcha == null)
-        {
-            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
-            throw new CaptchaExpireException();
-        }
-        if (!code.equalsIgnoreCase(captcha))
-        {
-            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
-            throw new CaptchaException();
-        }
-    }
-
     /**
      * 记录登录信息
      */
@@ -290,21 +202,19 @@ public class SysLoginService
      * 填充注入Channel 渠道/经销商  信息
      * @param loginUser
      */
-    public void fillChannelInfo(LoginUser loginUser){
+    public ChannelRoleEnum fillChannelInfo(LoginUser loginUser){
     	Channel channel = channelService.getChannelByUserId(loginUser.getUserId());
         if (null != channel && null != channel.getChannelId()){
-            // 校验渠道身份标识
-            if (loginUser.getIdentity() == UserIdentityEnum.CHANNEL && channel.getLevel() <= 0) {
-                throw new ServiceException("此账号的渠道不存在");
-            } else if (loginUser.getIdentity() == UserIdentityEnum.SALESITE && channel.getLevel() != 0) {
-                throw new ServiceException("此账号的经销商不存在");
-            }
           loginUser.setChannelId(channel.getChannelId());
           loginUser.setChannelNo(channel.getChannelNo());
+          if (channel.getLevel() == 0) {
+              return ChannelRoleEnum.SALESITE;
+          } else {
+              return ChannelRoleEnum.CHANNEL;
+          }
         } else {
             throw new ServiceException("登录账号不存在");
         }
-
     }
 
 

+ 29 - 10
mp-service/src/main/java/com/qs/mp/admin/domain/Coupon.java

@@ -1,9 +1,16 @@
 package com.qs.mp.admin.domain;
 
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 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 com.qs.mp.common.enums.CouponDiscountTypeEnum;
+import com.qs.mp.common.enums.CouponDistributeTypeEnum;
+import com.qs.mp.common.enums.CouponStatusEnum;
+import com.qs.mp.common.enums.CouponTypeEnum;
+import com.qs.mp.common.enums.CouponUseAreaEnum;
 import java.io.Serializable;
 import java.util.Date;
 import lombok.Data;
@@ -22,8 +29,8 @@ public class Coupon implements Serializable {
   /**
    * 主键
    */
-  @TableId(value = "coupon_id", type = IdType.AUTO)
-  private Long couponId;
+  @TableId(value = "coupon_id", type = IdType.INPUT)
+  private String couponId;
 
   /**
    * 标题
@@ -35,7 +42,8 @@ public class Coupon implements Serializable {
    * 类型;1用户盲票购买优惠券、2用户门店消费优惠券、3经销商盲票采购优惠券
    */
   @TableField("type")
-  private Integer type;
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private CouponTypeEnum type;
 
   /**
    * 图片
@@ -53,7 +61,8 @@ public class Coupon implements Serializable {
    * 优惠类型;1代金券、2折扣券、3兑换券
    */
   @TableField("discount_type")
-  private Integer discountType;
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private CouponDiscountTypeEnum discountType;
 
   /**
    * 优惠金额(比例)
@@ -68,22 +77,32 @@ public class Coupon implements Serializable {
   private Integer minOrderAmt;
 
   /**
-   * 状态;0下架 1正常
+   * 渠道代金券,渠道承担比例
+   */
+  @TableField("channel_shared_rate")
+  private Integer channelSharedRate;
+
+
+  /**
+   * 状态;off下架 on正常
    */
   @TableField("status")
-  private Integer status;
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private CouponStatusEnum status;
 
   /**
-   * 使用范围;0通用 1指定范围
+   * 使用范围;0通用 1生成券时指定范围 2发放时动态指定范围
    */
   @TableField("use_area")
-  private Integer useArea;
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private CouponUseAreaEnum useArea;
 
   /**
-   * 发放方式;0系统发放 1用户主动领取
+   * 发放方式;1系统发放 2用户主动领取
    */
   @TableField("distribute_type")
-  private Integer distributeType;
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private CouponDistributeTypeEnum distributeType;
 
   /**
    * 叠加使用;0不允许 1允许

+ 1 - 1
mp-service/src/main/java/com/qs/mp/admin/domain/CouponChannel.java

@@ -29,7 +29,7 @@ public class CouponChannel implements Serializable {
    * 优惠券ID
    */
   @TableField("coupon_id")
-  private Long couponId;
+  private String couponId;
 
   /**
    * 渠道ID

+ 1 - 1
mp-service/src/main/java/com/qs/mp/admin/domain/CouponTicket.java

@@ -29,7 +29,7 @@ public class CouponTicket implements Serializable {
    * 优惠券ID
    */
   @TableField("coupon_id")
-  private Long couponId;
+  private String couponId;
 
   /**
    * 盲票组ID

+ 11 - 1
mp-service/src/main/java/com/qs/mp/admin/domain/TicketAwardsPrize.java

@@ -1,9 +1,12 @@
 package com.qs.mp.admin.domain;
 
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 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 com.qs.mp.common.enums.TicketPrizeTypeEnum;
 import java.io.Serializable;
 import java.util.Date;
 import lombok.Data;
@@ -59,7 +62,8 @@ public class TicketAwardsPrize implements Serializable {
    * 奖品类型 1 实物商品 2优惠券 3平台代币
    */
   @TableField("prize_type")
-  private String prizeType;
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private TicketPrizeTypeEnum prizeType;
 
   /**
    * 奖品数
@@ -73,6 +77,12 @@ public class TicketAwardsPrize implements Serializable {
   @TableField("cashed_qty")
   private Integer cashedQty;
 
+  /**
+   * 剩余奖品数
+   */
+  @TableField("remain_qty")
+  private Integer remainQty;
+
   /**
    * 奖品价值
    */

+ 19 - 0
mp-service/src/main/java/com/qs/mp/admin/domain/dto/TicketDrawNumDTO.java

@@ -0,0 +1,19 @@
+package com.qs.mp.admin.domain.dto;
+
+import lombok.Data;
+
+/**
+ * 盲票抽奖号码
+ * @author zhongcp
+ * @Date 2022/3/8
+ */
+@Data
+public class TicketDrawNumDTO {
+  // 奖项ID
+  private String id;
+  // 奖项名
+  private String name;
+  // 号码
+  private int num;
+
+}

+ 7 - 0
mp-service/src/main/java/com/qs/mp/admin/domain/param/TicketParam.java

@@ -13,4 +13,11 @@ public class TicketParam {
 	@ApiModelProperty(value = "盲票ID",required=false)
 	private String ticketId;
 
+	@ApiModelProperty(value = "奖项ID",required=false)
+	private String awardsId;
+
+	@ApiModelProperty(value = "奖品ID",required=false)
+	private String prizeId;
+
+
 }

+ 8 - 0
mp-service/src/main/java/com/qs/mp/admin/service/ICouponService.java

@@ -2,6 +2,7 @@ package com.qs.mp.admin.service;
 
 import com.qs.mp.admin.domain.Coupon;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.qs.mp.admin.domain.Ticket;
 
 /**
  * <p>
@@ -13,4 +14,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface ICouponService extends IService<Coupon> {
 
+  /**
+   * 发放
+   * @param ticket
+   * @param userId
+   * @param couponId
+   */
+  void distribute(Ticket ticket, Long userId, String couponId);
 }

+ 65 - 0
mp-service/src/main/java/com/qs/mp/admin/service/impl/CouponServiceImpl.java

@@ -1,10 +1,28 @@
 package com.qs.mp.admin.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
 import com.qs.mp.admin.domain.Coupon;
+import com.qs.mp.admin.domain.Ticket;
 import com.qs.mp.admin.mapper.CouponMapper;
 import com.qs.mp.admin.service.ICouponService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qs.mp.common.enums.CouponUseAreaEnum;
+import com.qs.mp.common.enums.UserCouponStatusEnum;
+import com.qs.mp.common.exception.ServiceException;
+import com.qs.mp.common.utils.DateUtils;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.system.service.id.BizIdGenerator;
+import com.qs.mp.user.domain.UserCoupon;
+import com.qs.mp.user.domain.UserCouponChannel;
+import com.qs.mp.user.domain.UserTicketOrderItem;
+import com.qs.mp.user.service.IUserCouponChannelService;
+import com.qs.mp.user.service.IUserCouponService;
+import com.qs.mp.user.service.IUserTicketOrderItemService;
+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;
 
 /**
  * <p>
@@ -17,4 +35,51 @@ import org.springframework.stereotype.Service;
 @Service
 public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {
 
+  protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+  @Autowired
+  private IUserCouponService userCouponService;
+
+  @Autowired
+  private IUserCouponChannelService userCouponChannelService;
+
+  @Autowired
+  private BizIdGenerator bizIdGenerator;
+
+  @Autowired
+  private IUserTicketOrderItemService userTicketOrderItemService;
+
+  @Override
+  @Transactional
+  public void distribute(Ticket ticket, Long userId, String couponId) {
+    Coupon coupon = getById(couponId);
+    UserCoupon userCoupon = new UserCoupon();
+    userCoupon.setId(bizIdGenerator.newId());
+    userCoupon.setUserId(userId);
+    userCoupon.setVerifyCode(bizIdGenerator.newId());
+    userCoupon.setCouponId(coupon.getCouponId());
+    if (coupon.getDueDays() > 0) {
+      userCoupon.setValidStart(DateUtils.getToday());
+      userCoupon.setValidEnd(DateUtils.addDays(userCoupon.getValidStart(), coupon.getDueDays() - 1));
+    } else {
+      userCoupon.setValidStart(coupon.getValidStart());
+      userCoupon.setValidEnd(coupon.getValidEnd());
+    }
+    userCoupon.setStatus(UserCouponStatusEnum.UNUSED);
+    userCouponService.save(userCoupon);
+
+    // 确定限定范围
+    if (coupon.getUseArea() == CouponUseAreaEnum.POST_SCOPE) {
+      UserTicketOrderItem orderItem = userTicketOrderItemService.queryFinishedOrderItem(userId, ticket.getTicketId());
+      if (null == orderItem || null == orderItem.getChannelId()) {
+        LogUtil.error(logger, "优惠券的限定使用范围类型为发放时生成,但找不到关联的渠道ID。orderItem:{0}", new Object[]{
+            JSONObject.toJSONString(orderItem)});
+        throw new ServiceException("优惠券发放失败");
+      }
+      UserCouponChannel userCouponChannel = new UserCouponChannel();
+      userCouponChannel.setUserCouponId(userCoupon.getId());
+      userCouponChannel.setChannelId(orderItem.getChannelId());
+      userCouponChannelService.save(userCouponChannel);
+    }
+  }
 }

+ 12 - 7
mp-service/src/main/java/com/qs/mp/framework/redis/RedisKey.java

@@ -1,21 +1,26 @@
 package com.qs.mp.framework.redis;
 
+import com.qs.mp.common.utils.MessageHelper;
+
 /**
  * redis Key管理模版
- * 
+ *
  * @author duota
  *
  */
 public enum RedisKey {
-    
-    App_Secret_Key("app_secret:{appId}", new String[] { "appId"}),
-    User_Token_Key("user_token:{token}", new String[] { "token"});
+    WX_APP_TOKEN_KEY("app_token_key_{0}", "小程序app token"),
+    CHANNEL_ORDER_KEY("channel_order_{0}", "经销商下的盲票采购单"),
+    USER_TICKET_ORDER_KEY("user_ticket_order_key_{0}", "用户购票订单");
 
     public String keyTemplate;
-    public String[] keyNames;
+    public String desc;
 
-    RedisKey(String keyTemplate, String[] keyNames) {
+    RedisKey(String keyTemplate, String desc) {
         this.keyTemplate = keyTemplate;
-        this.keyNames = keyNames;
+        this.desc = desc;
+    }
+    public static String build(RedisKey redisKey, Object... param) {
+        return MessageHelper.formatMsg(redisKey.keyTemplate, param);
     }
 }

+ 0 - 18
mp-service/src/main/java/com/qs/mp/framework/redis/RedisKeyBuilder.java

@@ -1,18 +0,0 @@
-package com.qs.mp.framework.redis;
-
-import java.util.Map;
-
-public class RedisKeyBuilder {
-    
-    public static String build(RedisKey redisKey, Map<String, String> params) {
-        String key = redisKey.keyTemplate;
-        String[] keyNames = redisKey.keyNames;
-        for (String keyName : keyNames) {
-            String param = params.get(keyName);
-            param = param == null ? "*" : param;
-
-            key = key.replace("{" + keyName + "}", param);
-        }
-        return key;
-    }
-}

+ 66 - 72
mp-service/src/main/java/com/qs/mp/framework/service/impl/AppTokenServiceImpl.java

@@ -10,12 +10,9 @@ import com.qs.mp.common.utils.http.HttpUtils;
 import com.qs.mp.framework.domain.AppToken;
 import com.qs.mp.framework.mapper.AppTokenMapper;
 import com.qs.mp.framework.redis.RedisKey;
-import com.qs.mp.framework.redis.RedisKeyBuilder;
 import com.qs.mp.framework.redis.RedisUtil;
 import com.qs.mp.framework.service.IAppTokenService;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -23,82 +20,79 @@ import org.springframework.stereotype.Service;
 
 @Service
 public class AppTokenServiceImpl implements IAppTokenService {
-    protected final Logger logger = LoggerFactory.getLogger(AppTokenServiceImpl.class);
 
-    @Autowired
-    private AppTokenMapper appTokenMapper;
+  protected final Logger logger = LoggerFactory.getLogger(AppTokenServiceImpl.class);
 
+  @Autowired
+  private AppTokenMapper appTokenMapper;
 
-    @Autowired
-    private RedisUtil redis;
-    
-    @Override
-    public AppToken selectByPrimaryKey(String appId) {
-        return appTokenMapper.selectByPrimaryKey(appId);
-    }
 
-    @Override
-    public String getAccessToken(String appId) {
-        Map<String, String> keyParams = new HashMap<String, String>();
-        keyParams.put("appId", appId);
-        String cacheKey = RedisKeyBuilder.build(RedisKey.App_Secret_Key, keyParams);
-
-        boolean updateCache = false;
-
-        String appInfo = redis.get(cacheKey);
-        AppToken token = null;
-        if (StringUtils.isBlank(appInfo)) {
-            token = selectByPrimaryKey(appId);
-            if (null == token) {
-                LogUtil.error(logger, "app_token记录不存在,appId:{0}", appId);
-                throw new ServiceException();
-            }
-            updateCache = true;
-        } else {
-            token = JSON.parseObject(appInfo, AppToken.class);
-        }
-
-        LogUtil.info(logger, "token: {0}", JSON.toJSONString(token));
-        if (token.getExpireTimestamp().getTime() < System.currentTimeMillis()) {
-            String res = wxGetAccessToken(appId, token.getAppSecret());
-            if (StringUtils.isEmpty(res)){
-                LogUtil.error(logger, "获取access_token为空,appId:{0}, res:{1}", new Object[]{appId,
-                                                                                          res});
-                throw new ServiceException();
-            }
-            JSONObject jsonObject = JSONObject.parseObject(res);
-            String accessToken = jsonObject.getString("access_token");
-            Integer expiresIn = jsonObject.getInteger("expires_in");
-            if(StringUtils.isEmpty(accessToken)){
-                LogUtil.error(logger, "获取access_token为空,appId:{0}, res:{1}",
-                        new Object[] { appId, res });
-                throw new ServiceException();
-            }
-            token.setAccessToken(accessToken);
-            // 防止时间不同步,此处提前100s超时
-            token.setExpireTimestamp(DateUtils.addSeconds(new Date(), expiresIn-100));
-            appTokenMapper.updateByPrimaryKeySelective(token);
-            updateCache = true;
-
-        }
-
-        if(updateCache) {
-            boolean result = redis.set(cacheKey, JSON.toJSONString(token));
-            if (!result) {
-                LogUtil.error(logger, "appsecret key set error.cacheKey:", cacheKey);
-            }
-        }
-
-
-
-        return token.getAccessToken();
+  @Autowired
+  private RedisUtil redis;
+
+  @Override
+  public AppToken selectByPrimaryKey(String appId) {
+    return appTokenMapper.selectByPrimaryKey(appId);
+  }
+
+  @Override
+  public String getAccessToken(String appId) {
+    String cacheKey = RedisKey.build(RedisKey.WX_APP_TOKEN_KEY, appId);
+
+    boolean updateCache = false;
+
+    String appInfo = redis.get(cacheKey);
+    AppToken token = null;
+    if (StringUtils.isBlank(appInfo)) {
+      token = selectByPrimaryKey(appId);
+      if (null == token) {
+        LogUtil.error(logger, "app_token记录不存在,appId:{0}", appId);
+        throw new ServiceException();
+      }
+      updateCache = true;
+    } else {
+      token = JSON.parseObject(appInfo, AppToken.class);
     }
 
-    private String wxGetAccessToken(String appId, String appSecret) {
-        String param = "appid=" + appId + "&secret=" + appSecret + "&grant_type=client_credential";
-        String res = HttpUtils.sendGet("https://api.weixin.qq.com/cgi-bin/token", param);
-        return res;
+    LogUtil.info(logger, "token: {0}", JSON.toJSONString(token));
+    if (token.getExpireTimestamp().getTime() < System.currentTimeMillis()) {
+      String res = wxGetAccessToken(appId, token.getAppSecret());
+      if (StringUtils.isEmpty(res)) {
+        LogUtil.error(logger, "获取access_token为空,appId:{0}, res:{1}", appId,
+            res);
+        throw new ServiceException();
+      }
+      JSONObject jsonObject = JSONObject.parseObject(res);
+      String accessToken = jsonObject.getString("access_token");
+      Integer expiresIn = jsonObject.getInteger("expires_in");
+      if (StringUtils.isEmpty(accessToken)) {
+        LogUtil.error(logger, "获取access_token为空,appId:{0}, res:{1}",
+            appId, res);
+        throw new ServiceException();
+      }
+      token.setAccessToken(accessToken);
+      // 防止时间不同步,此处提前100s超时
+      token.setExpireTimestamp(DateUtils.addSeconds(new Date(), expiresIn - 100));
+      appTokenMapper.updateByPrimaryKeySelective(token);
+      updateCache = true;
 
     }
 
+    if (updateCache) {
+      boolean result = redis.set(cacheKey, JSON.toJSONString(token));
+      if (!result) {
+        LogUtil.error(logger, "appsecret key set error.cacheKey:", cacheKey);
+      }
+    }
+
+    return token.getAccessToken();
+  }
+
+  private String wxGetAccessToken(String appId, String appSecret) {
+    String param = "appid=" + appId + "&secret=" + appSecret + "&grant_type=client_credential";
+    String res = HttpUtils.sendGet("https://api.weixin.qq.com/cgi-bin/token", param);
+    return res;
+
+  }
+
 }

+ 6 - 3
mp-service/src/main/java/com/qs/mp/framework/service/impl/WxSubscribeMessageImpl.java

@@ -31,8 +31,11 @@ public class WxSubscribeMessageImpl implements IWxSubscribeMessage {
   @Autowired
   private IAppTokenService appTokenService;
 
-  @Value("${wx.appId}")
-  private String appId;
+  @Value("${wx-channel.appId}")
+  private String channelAppId;
+
+  @Value("${wx-user.appId}")
+  private String userAppId;
 
   @Value("${miniprogram.state}")
   private String state;
@@ -40,7 +43,7 @@ public class WxSubscribeMessageImpl implements IWxSubscribeMessage {
 
   private boolean sendNotify(Long userId, String bizId, WxSubscribeMessage message) {
     // 获取access_token
-    String accessToken = appTokenService.getAccessToken(appId);
+    String accessToken = appTokenService.getAccessToken(channelAppId);
     String res = HttpUtils.sendPost(
         "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken,
         JSON.toJSONString(message));

+ 0 - 3
mp-service/src/main/java/com/qs/mp/framework/service/impl/WxTemplateMessageImpl.java

@@ -36,9 +36,6 @@ public class WxTemplateMessageImpl implements IWxTemplateMessage {
   @Value("${wxgzh.appId}")
   private String gzhAppId;
 
-  @Value("${wx.appId}")
-  private String appId;
-
   private boolean sendNotify(Long userId, String bizId, WxTemplateMessage message, String msg) {
     // 获取access_token
     String accessToken = appTokenService.getAccessToken(gzhAppId);

+ 3 - 3
mp-service/src/main/java/com/qs/mp/framework/service/impl/WxUrlLinkServiceImpl.java

@@ -28,8 +28,8 @@ public class WxUrlLinkServiceImpl implements IWxUrlLinkService {
   @Autowired
   private IAppTokenService appTokenService;
 
-  @Value("${wx.appId}")
-  private String appId;
+  @Value("${wx-channel.appId}")
+  private String channelAppId;
 
   @Override
   public String generate(String path, String query) {
@@ -43,7 +43,7 @@ public class WxUrlLinkServiceImpl implements IWxUrlLinkService {
     param.put("expire_interval", 30);
     // 云管铺小程序永久短链接https://wxaurl.cn/YL8D48CQbMr
     // 获取access_token
-    String accessToken = appTokenService.getAccessToken(appId);
+    String accessToken = appTokenService.getAccessToken(channelAppId);
     String res = HttpUtils.sendPost(
         "https://api.weixin.qq.com/wxa/generate_urllink?access_token=" + accessToken,
         JSON.toJSONString(param));

+ 10 - 4
mp-service/src/main/java/com/qs/mp/pay/service/impl/WalletServiceImpl.java

@@ -47,8 +47,10 @@ public class WalletServiceImpl implements IWalletService {
   private String callbackUrl;  //支付回调地址
   @Value("${pay.base.url}")
   private String baseUrl = "https://jlpays.kakapaypay.com/";
-  @Value("${wx.appId}")
-  private String appId = "wxb86cb7f459fc3675";
+  @Value("${wx-channel.appId}")
+  private String channelAppId;
+  @Value("${wx-user.appId}")
+  private String userAppId;
 
   @Value("${pay.shopNo}")
   private String payShopNo;  //平台商户号
@@ -62,7 +64,11 @@ public class WalletServiceImpl implements IWalletService {
   @Override
   public JSONObject pay(BizTypeEnum bizType, String bizId, String openId, int money) {
     String reqUrl = baseUrl + "/pay/payMoneyCTB";
-    JSONObject params = buildPayOrderReqData(payShopNo, paySign, money, openId);
+    String appId = channelAppId;
+    if (bizType == BizTypeEnum.USER_ORDER) {
+      appId = userAppId;
+    }
+    JSONObject params = buildPayOrderReqData(payShopNo, paySign, money, openId, appId);
     String result = OkHttpUtil.postJsonParams(reqUrl, params.toJSONString());
     logger.info("request params:" + params.toJSONString() + "  result:" + result);
     if (StringUtils.isBlank(result)) {
@@ -143,7 +149,7 @@ public class WalletServiceImpl implements IWalletService {
     return payOrder;
   }
 
-  private JSONObject buildPayOrderReqData(String shopNo, String sign, int money, String openId) {
+  private JSONObject buildPayOrderReqData(String shopNo, String sign, int money, String openId, String appId) {
 
     JSONObject sendData = new JSONObject();
     sendData.put("shop_no" , shopNo);

+ 12 - 3
mp-service/src/main/java/com/qs/mp/system/domain/SysUser.java

@@ -17,7 +17,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 
 /**
  * 用户对象 sys_user
- * 
+ *
  * @author ygp
  */
 public class SysUser extends BaseEntity
@@ -96,7 +96,8 @@ public class SysUser extends BaseEntity
     private Long roleId;
 
     private String gzhOpenId; // 关注公众号的openid
-    private String openId;   //绑定微信openId
+    private String openId;   //绑定微信用户端小程序openId
+    private String channelOpenId;   //绑定微信渠道端小程序openId
     private String unionId;  //用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回
     private String sessionKey; //会话密钥
 
@@ -116,6 +117,14 @@ public class SysUser extends BaseEntity
         return openId;
     }
 
+    public String getChannelOpenId() {
+        return channelOpenId;
+    }
+
+    public void setChannelOpenId(String channelOpenId) {
+        this.channelOpenId = channelOpenId;
+    }
+
     public String getUnionId() {
         return unionId;
     }
@@ -375,7 +384,7 @@ public class SysUser extends BaseEntity
             .append("dept", getDept())
             .toString();
     }
-    
+
     public static void main(String[] args) {
     	String mobile = "15112682435";
     	String ss = mobile.length()>6?mobile.substring(mobile.length()-6):mobile;

+ 7 - 25
mp-service/src/main/java/com/qs/mp/system/domain/vo/WxLoginParams.java

@@ -1,38 +1,20 @@
 package com.qs.mp.system.domain.vo;
 
+import lombok.Data;
+
 /**
  * @author duota
  *
  */
+@Data
 public class WxLoginParams {
 
     private String code;   //微信返回code,用于服务端换取鉴权信息
     private String nickName;
     private String avatarUrl;
-
-
-    public void setNickName(String nickName) {
-        this.nickName = nickName;
-    }
-
-    public void setAvatarUrl(String avatarUrl) {
-        this.avatarUrl = avatarUrl;
-    }
-
-    public String getNickName() {
-        return nickName;
-    }
-
-    public String getAvatarUrl() {
-        return avatarUrl;
-    }
-
-    public String getCode() {
-        return code;
-    }
-
-    public void setCode(String code) {
-        this.code = code;
-    }
+    /**
+     * 登录身份
+     */
+    private int identity = 2;
 
 }

+ 1 - 7
mp-service/src/main/java/com/qs/mp/user/domain/UserCoin.java

@@ -19,16 +19,10 @@ public class UserCoin implements Serializable {
 
   private static final long serialVersionUID = 1L;
 
-  /**
-   * 主键
-   */
-  @TableId(value = "id", type = IdType.AUTO)
-  private Long id;
-
   /**
    * 用户ID
    */
-  @TableField("user_id")
+  @TableId(value = "user_id", type = IdType.INPUT)
   private Long userId;
 
   /**

+ 5 - 1
mp-service/src/main/java/com/qs/mp/user/domain/UserCoinLog.java

@@ -1,9 +1,12 @@
 package com.qs.mp.user.domain;
 
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 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 com.qs.mp.common.enums.CoinLogTypeEnum;
 import java.io.Serializable;
 import java.util.Date;
 import lombok.Data;
@@ -35,7 +38,8 @@ public class UserCoinLog implements Serializable {
    * 交易类型
    */
   @TableField("type")
-  private Integer type;
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private CoinLogTypeEnum type;
 
   /**
    * 交易后余额

+ 7 - 3
mp-service/src/main/java/com/qs/mp/user/domain/UserCoupon.java

@@ -1,9 +1,12 @@
 package com.qs.mp.user.domain;
 
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 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 com.qs.mp.common.enums.UserCouponStatusEnum;
 import java.io.Serializable;
 import java.util.Date;
 import lombok.Data;
@@ -23,7 +26,7 @@ public class UserCoupon implements Serializable {
    * 主键
    */
   @TableId(value = "id", type = IdType.INPUT)
-  private Long id;
+  private String id;
 
   /**
    * 用户ID
@@ -41,7 +44,7 @@ public class UserCoupon implements Serializable {
    * 优惠券ID
    */
   @TableField("coupon_id")
-  private Long couponId;
+  private String couponId;
 
   /**
    * 生效日
@@ -71,7 +74,8 @@ public class UserCoupon implements Serializable {
    * 状态;1未使用 2已使用 3已过期
    */
   @TableField("status")
-  private Integer status;
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private UserCouponStatusEnum status;
 
   /**
    * 创建时间

+ 53 - 0
mp-service/src/main/java/com/qs/mp/user/domain/UserCouponChannel.java

@@ -0,0 +1,53 @@
+package com.qs.mp.user.domain;
+
+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 lombok.Data;
+
+/**
+ * @describe 经销商门店优惠券使用范围限制实体类
+ * @auther quanshu
+ * @create 2022-03-09 09:23:17
+ */
+@TableName("mp_user_coupon_channel")
+@Data
+public class UserCouponChannel implements Serializable {
+
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * 主键
+   */
+  @TableId(value = "id", type = IdType.AUTO)
+  private Long id;
+
+  /**
+   * 用户优惠券ID
+   */
+  @TableField("user_coupon_id")
+  private String userCouponId;
+
+  /**
+   * 渠道ID
+   */
+  @TableField("channel_id")
+  private Long channelId;
+
+  /**
+   * 创建时间
+   */
+  @TableField("created_time")
+  private Date createdTime;
+
+  /**
+   * 更新时间
+   */
+  @TableField("updated_time")
+  private Date updatedTime;
+
+
+}

+ 20 - 2
mp-service/src/main/java/com/qs/mp/user/domain/UserPrizeStorage.java

@@ -1,9 +1,13 @@
 package com.qs.mp.user.domain;
 
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 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 com.qs.mp.common.enums.PrizeStorageInTypeEnum;
+import com.qs.mp.common.enums.PrizeStorageStatusEnum;
 import java.io.Serializable;
 import java.util.Date;
 import lombok.Data;
@@ -65,13 +69,27 @@ public class UserPrizeStorage implements Serializable {
    * 商品数
    */
   @TableField("goods_num")
-  private String goodsNum;
+  private Integer goodsNum;
+
+  /**
+   * 入库类型;1盲票兑奖、2盲豆兑换
+   */
+  @TableField("in_type")
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private PrizeStorageInTypeEnum inType;
+
+  /**
+   * 关联业务ID
+   */
+  @TableField("ref_id")
+  private String refId;
 
   /**
    * 状态;1待提货、2已提货
    */
   @TableField("status")
-  private Integer status;
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private PrizeStorageStatusEnum status;
 
   /**
    * 创建时间

+ 6 - 0
mp-service/src/main/java/com/qs/mp/user/domain/UserTicketOrderItem.java

@@ -49,6 +49,12 @@ public class UserTicketOrderItem implements Serializable {
   @TableField("ticket_id")
   private String ticketId;
 
+  /**
+   * 线下购买渠道ID
+   */
+  @TableField("channel_id")
+  private Long channelId;
+
   /**
    * 创建时间
    */

+ 26 - 0
mp-service/src/main/java/com/qs/mp/user/domain/param/TicketOrderParam.java

@@ -0,0 +1,26 @@
+package com.qs.mp.user.domain.param;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.List;
+import lombok.Data;
+
+@ApiModel(value= "订单参数")
+@Data
+public class TicketOrderParam {
+
+	@ApiModelProperty(value = "优惠券IDs",required=false)
+	private List<String> couponIds;
+
+	@ApiModelProperty(value = "盲票组ID",required=false)
+	private String boxId;
+
+	@ApiModelProperty(value = "购买数量",required=false)
+	private Integer orderNum;
+
+	@ApiModelProperty(value = "盲票ID",required=false)
+	private String ticketId;
+
+	@ApiModelProperty(value = "系统自动选择优惠券,1代表系统自动选择,忽略couponIds,首次请求时传1;0代表以传入的couponIds为准",required=false)
+	private Integer autoCoupon;
+}

+ 63 - 0
mp-service/src/main/java/com/qs/mp/user/domain/vo/TicketOrderSettleVO.java

@@ -0,0 +1,63 @@
+package com.qs.mp.user.domain.vo;
+
+import com.qs.mp.admin.domain.Coupon;
+import com.qs.mp.channel.domain.ChannelAddr;
+import com.qs.mp.channel.domain.vo.ChannelCartVO;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Data;
+
+/**
+ * @author zhongcp
+ * @Date 2022/3/3
+ */
+@Data
+public class TicketOrderSettleVO {
+
+  /**
+   * 订单金额
+   */
+  private Integer orderAmt = 0;
+
+  /**
+   * 优惠金额
+   */
+  private Integer discountAmt = 0;
+
+  /**
+   * 实付金额
+   */
+  private Integer payAmt = 0;
+
+  /**
+   * 票组ID
+   */
+  private String boxId;
+
+  /**
+   * 订购数量
+   */
+  private Integer orderNum;
+
+  /**
+   * 盲票ID
+   */
+  private String ticketId;
+
+  /**
+   * 盲票图片
+   */
+  private String picUrl;
+
+  /**
+   * 盲票标题
+   */
+  private String title;
+
+  /**
+   * 使用优惠券列表
+   */
+  List<UserCouponVO> couponList = new ArrayList<>();
+
+}

+ 76 - 0
mp-service/src/main/java/com/qs/mp/user/domain/vo/UserCouponVO.java

@@ -0,0 +1,76 @@
+package com.qs.mp.user.domain.vo;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+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 com.qs.mp.common.enums.CouponDiscountTypeEnum;
+import com.qs.mp.common.enums.CouponDistributeTypeEnum;
+import com.qs.mp.common.enums.CouponStatusEnum;
+import com.qs.mp.common.enums.CouponTypeEnum;
+import com.qs.mp.common.enums.CouponUseAreaEnum;
+import com.qs.mp.common.enums.UserCouponStatusEnum;
+import com.qs.mp.user.domain.UserCoupon;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * @describe 用户的优惠券
+ * @auther zhongcp
+ * @create 2022-03-07 20:45:43
+ */
+@Data
+public class UserCouponVO extends UserCoupon {
+  /**
+   * 标题
+   */
+  private String title;
+
+  /**
+   * 类型;1用户盲票购买优惠券、2用户门店消费优惠券、3经销商盲票采购优惠券
+   */
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private CouponTypeEnum type;
+
+  /**
+   * 图片
+   */
+  private String picUrl;
+
+  /**
+   * 使用说明
+   */
+  private String description;
+
+  /**
+   * 优惠类型;1代金券、2折扣券、3兑换券
+   */
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private CouponDiscountTypeEnum discountType;
+
+  /**
+   * 优惠金额(比例)
+   */
+  private Integer discount;
+
+  /**
+   * 最低消费金额
+   */
+  private Integer minOrderAmt;
+
+  /**
+   * 使用范围;0通用 1生成券时指定范围 2发放时动态指定范围
+   */
+  @JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
+  private CouponUseAreaEnum useArea;
+
+  /**
+   * 叠加使用;0不允许 1允许
+   */
+  private Integer compositeUse;
+
+
+}

+ 13 - 0
mp-service/src/main/java/com/qs/mp/user/mapper/UserCouponChannelMapper.java

@@ -0,0 +1,13 @@
+package com.qs.mp.user.mapper;
+
+import com.qs.mp.user.domain.UserCouponChannel;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @auther quanshu
+ * @create 2022-03-09 09:23:17
+ * @describe 经销商门店优惠券使用范围限制mapper类
+ */
+public interface UserCouponChannelMapper extends BaseMapper<UserCouponChannel> {
+
+}

+ 8 - 0
mp-service/src/main/java/com/qs/mp/user/mapper/UserCouponMapper.java

@@ -1,7 +1,14 @@
 package com.qs.mp.user.mapper;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.qs.mp.admin.domain.TicketAwardsPrize;
+import com.qs.mp.admin.domain.vo.TicketAwardsPrizeVO;
 import com.qs.mp.user.domain.UserCoupon;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qs.mp.user.domain.vo.UserCouponVO;
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * @auther quanshu
@@ -10,4 +17,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
 public interface UserCouponMapper extends BaseMapper<UserCoupon> {
 
+  List<UserCouponVO> listUserCouponVO(@Param(Constants.WRAPPER) Wrapper<UserCoupon> queryWrapper);
 }

+ 7 - 0
mp-service/src/main/java/com/qs/mp/user/service/IUserCoinService.java

@@ -13,4 +13,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface IUserCoinService extends IService<UserCoin> {
 
+  /**
+   * 发放代币
+   * @param userId
+   * @param logCoin
+   * @param bizId
+   */
+  void produce(Long userId, Integer logCoin, String bizId);
 }

+ 16 - 0
mp-service/src/main/java/com/qs/mp/user/service/IUserCouponChannelService.java

@@ -0,0 +1,16 @@
+package com.qs.mp.user.service;
+
+import com.qs.mp.user.domain.UserCouponChannel;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 经销商门店优惠券使用范围限制 服务类
+ * </p>
+ *
+ * @author quanshu
+ * @since 2022-03-09
+ */
+public interface IUserCouponChannelService extends IService<UserCouponChannel> {
+
+}

+ 8 - 0
mp-service/src/main/java/com/qs/mp/user/service/IUserCouponService.java

@@ -2,6 +2,8 @@ package com.qs.mp.user.service;
 
 import com.qs.mp.user.domain.UserCoupon;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.qs.mp.user.domain.vo.UserCouponVO;
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +15,10 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface IUserCouponService extends IService<UserCoupon> {
 
+  /**
+   * 查询用户当前可用于盲票购买的优惠券,按优惠大小排序
+   * @param userId
+   * @return
+   */
+  List<UserCouponVO> listTicketOrderCoupon(Long userId);
 }

+ 17 - 0
mp-service/src/main/java/com/qs/mp/user/service/IUserHitPrizeService.java

@@ -1,7 +1,11 @@
 package com.qs.mp.user.service;
 
+import com.qs.mp.admin.domain.Ticket;
+import com.qs.mp.admin.domain.TicketAwardsPrize;
+import com.qs.mp.admin.domain.vo.TicketAwardsPrizeVO;
 import com.qs.mp.user.domain.UserHitPrize;
 import com.baomidou.mybatisplus.extension.service.IService;
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +17,17 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface IUserHitPrizeService extends IService<UserHitPrize> {
 
+  /**
+   * 查看盲票中奖奖品列表
+   * @param ticket
+   * @return
+   */
+  List<TicketAwardsPrize> listPrize(Ticket ticket, Long userId);
+
+  /**
+   * 兑奖
+   * @param ticket
+   * @return
+   */
+  void cashPrize(Ticket ticket, Long userId, String awardsId, String prizeId);
 }

+ 10 - 0
mp-service/src/main/java/com/qs/mp/user/service/IUserPrizeStorageService.java

@@ -1,5 +1,7 @@
 package com.qs.mp.user.service;
 
+import com.qs.mp.admin.domain.TicketAwardsPrize;
+import com.qs.mp.common.enums.PrizeStorageInTypeEnum;
 import com.qs.mp.user.domain.UserPrizeStorage;
 import com.baomidou.mybatisplus.extension.service.IService;
 
@@ -13,4 +15,12 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface IUserPrizeStorageService extends IService<UserPrizeStorage> {
 
+  /**
+   * 商品入用户的奖品库
+   * @param userId
+   * @param ticketAwardsPrize
+   * @param inTypeEnum
+   * @param refId
+   */
+  void takeInStorage(Long userId, TicketAwardsPrize ticketAwardsPrize, PrizeStorageInTypeEnum inTypeEnum, String refId);
 }

+ 7 - 0
mp-service/src/main/java/com/qs/mp/user/service/IUserTicketOrderItemService.java

@@ -13,4 +13,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface IUserTicketOrderItemService extends IService<UserTicketOrderItem> {
 
+  /**
+   * 查询购票订单明细
+   * @param userId
+   * @param ticketId
+   * @return
+   */
+  UserTicketOrderItem queryFinishedOrderItem(Long userId, String ticketId);
 }

+ 0 - 1
mp-service/src/main/java/com/qs/mp/user/service/impl/UserCoinLogServiceImpl.java

@@ -16,5 +16,4 @@ import org.springframework.stereotype.Service;
  */
 @Service
 public class UserCoinLogServiceImpl extends ServiceImpl<UserCoinLogMapper, UserCoinLog> implements IUserCoinLogService {
-
 }

+ 36 - 0
mp-service/src/main/java/com/qs/mp/user/service/impl/UserCoinServiceImpl.java

@@ -1,10 +1,17 @@
 package com.qs.mp.user.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.qs.mp.common.enums.CoinLogTypeEnum;
 import com.qs.mp.user.domain.UserCoin;
+import com.qs.mp.user.domain.UserCoinLog;
 import com.qs.mp.user.mapper.UserCoinMapper;
+import com.qs.mp.user.service.IUserCoinLogService;
 import com.qs.mp.user.service.IUserCoinService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.util.Date;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 /**
  * <p>
@@ -17,4 +24,33 @@ import org.springframework.stereotype.Service;
 @Service
 public class UserCoinServiceImpl extends ServiceImpl<UserCoinMapper, UserCoin> implements IUserCoinService {
 
+  @Autowired
+  private IUserCoinLogService userCoinLogService;
+
+  @Override
+  @Transactional
+  public void produce(Long userId, Integer logCoin, String bizId) {
+    UserCoin userCoin = getById(userId);
+    if (null == userCoin) {
+      userCoin = new UserCoin();
+      userCoin.setUserId(userId);
+      userCoin.setCoin(logCoin);
+      save(userCoin);
+    } else {
+      update(new LambdaUpdateWrapper<UserCoin>().set(UserCoin::getCoin, userCoin.getCoin() + logCoin)
+          .eq(UserCoin::getUserId, userId).eq(UserCoin::getCoin, userCoin.getCoin()));
+    }
+
+    UserCoinLog userCoinLog = new UserCoinLog();
+    userCoinLog.setUserId(userId);
+    userCoinLog.setType(CoinLogTypeEnum.PRIZE);
+    userCoinLog.setMoney(userCoin.getCoin() + logCoin);
+    userCoinLog.setLogMoney(logCoin);
+    userCoinLog.setIncomeExpense(CoinLogTypeEnum.INCOME);
+    userCoinLog.setLogText("盲票奖品");
+    userCoinLog.setBizTime(new Date());
+    userCoinLog.setRefType(1);
+    userCoinLog.setRefId(bizId);
+    userCoinLogService.save(userCoinLog);
+  }
 }

+ 20 - 0
mp-service/src/main/java/com/qs/mp/user/service/impl/UserCouponChannelServiceImpl.java

@@ -0,0 +1,20 @@
+package com.qs.mp.user.service.impl;
+
+import com.qs.mp.user.domain.UserCouponChannel;
+import com.qs.mp.user.mapper.UserCouponChannelMapper;
+import com.qs.mp.user.service.IUserCouponChannelService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 经销商门店优惠券使用范围限制 服务实现类
+ * </p>
+ *
+ * @author quanshu
+ * @since 2022-03-09
+ */
+@Service
+public class UserCouponChannelServiceImpl extends ServiceImpl<UserCouponChannelMapper, UserCouponChannel> implements IUserCouponChannelService {
+
+}

+ 15 - 0
mp-service/src/main/java/com/qs/mp/user/service/impl/UserCouponServiceImpl.java

@@ -1,9 +1,15 @@
 package com.qs.mp.user.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qs.mp.common.enums.CouponTypeEnum;
+import com.qs.mp.common.enums.UserCouponStatusEnum;
+import com.qs.mp.common.utils.DateUtils;
 import com.qs.mp.user.domain.UserCoupon;
+import com.qs.mp.user.domain.vo.UserCouponVO;
 import com.qs.mp.user.mapper.UserCouponMapper;
 import com.qs.mp.user.service.IUserCouponService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.util.List;
 import org.springframework.stereotype.Service;
 
 /**
@@ -17,4 +23,13 @@ import org.springframework.stereotype.Service;
 @Service
 public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {
 
+  @Override
+  public List<UserCouponVO> listTicketOrderCoupon(Long userId) {
+    List<UserCouponVO> userCouponVOList = getBaseMapper().listUserCouponVO(new QueryWrapper<UserCoupon>()
+        .eq("t1.user_id", userId).eq("t2.type", CouponTypeEnum.TICKET_ORDER)
+        .eq("t1.status", UserCouponStatusEnum.UNUSED)
+        .le("t1.valid_start", DateUtils.getToday()).ge("t1.valid_end", DateUtils.getToday())
+        .orderByDesc("t2.discount"));
+    return userCouponVOList;
+  }
 }

+ 116 - 0
mp-service/src/main/java/com/qs/mp/user/service/impl/UserHitPrizeServiceImpl.java

@@ -1,10 +1,47 @@
 package com.qs.mp.user.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.qs.mp.admin.domain.Coupon;
+import com.qs.mp.admin.domain.Ticket;
+import com.qs.mp.admin.domain.TicketAwardsPrize;
+import com.qs.mp.admin.domain.dto.TicketDrawNumDTO;
+import com.qs.mp.admin.service.ICouponService;
+import com.qs.mp.admin.service.ITicketAwardsPrizeService;
+import com.qs.mp.admin.service.ITicketService;
+import com.qs.mp.common.enums.CouponUseAreaEnum;
+import com.qs.mp.common.enums.PrizeStorageInTypeEnum;
+import com.qs.mp.common.enums.PrizeStorageStatusEnum;
+import com.qs.mp.common.enums.TicketPrizeTypeEnum;
+import com.qs.mp.common.enums.UserCouponStatusEnum;
+import com.qs.mp.common.exception.ServiceException;
+import com.qs.mp.common.utils.DateUtils;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.system.service.id.BizIdGenerator;
+import com.qs.mp.user.domain.UserCoupon;
+import com.qs.mp.user.domain.UserCouponChannel;
 import com.qs.mp.user.domain.UserHitPrize;
+import com.qs.mp.user.domain.UserPrizeStorage;
+import com.qs.mp.user.domain.UserTicketOrderItem;
 import com.qs.mp.user.mapper.UserHitPrizeMapper;
+import com.qs.mp.user.service.IUserCoinService;
+import com.qs.mp.user.service.IUserCouponChannelService;
+import com.qs.mp.user.service.IUserCouponService;
 import com.qs.mp.user.service.IUserHitPrizeService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qs.mp.user.service.IUserPrizeStorageService;
+import com.qs.mp.user.service.IUserTicketOrderItemService;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import org.codehaus.janino.IClass.IField;
+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 org.springframework.util.CollectionUtils;
 
 /**
  * <p>
@@ -17,4 +54,83 @@ import org.springframework.stereotype.Service;
 @Service
 public class UserHitPrizeServiceImpl extends ServiceImpl<UserHitPrizeMapper, UserHitPrize> implements IUserHitPrizeService {
 
+  protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+  @Autowired
+  private IUserTicketOrderItemService userTicketOrderItemService;
+
+  @Autowired
+  private ITicketAwardsPrizeService ticketAwardsPrizeService;
+
+  @Autowired
+  private IUserPrizeStorageService userPrizeStorageService;
+
+  @Autowired
+  private IUserCoinService userCoinService;
+
+  @Autowired
+  private ICouponService couponService;
+
+  @Override
+  public List<TicketAwardsPrize> listPrize(Ticket ticket, Long userId) {
+    UserHitPrize userHitPrize = getOne(new LambdaQueryWrapper<UserHitPrize>().eq(UserHitPrize::getTicketId, ticket.getTicketId()));
+    UserTicketOrderItem orderItem = userTicketOrderItemService.queryFinishedOrderItem(userId, ticket.getTicketId());
+    if (null == orderItem) {
+      LogUtil.error(logger, "查询盲票的奖品信息,盲票订单不存在。ticketId:{0}, userId:{1}",
+          new Object[]{ticket.getTicketId(), userId});
+      throw new ServiceException("盲票订单不存在");
+    }
+
+    if (null == userHitPrize) {
+      List<TicketDrawNumDTO> drawNumDTOList = JSONObject.parseArray(ticket.getDrawNum(), TicketDrawNumDTO.class);
+      for (TicketDrawNumDTO ticketDrawNumDTO : drawNumDTOList) {
+        if (ticketDrawNumDTO.getNum() == ticket.getPlainLuckyNum()) {
+          userHitPrize = new UserHitPrize();
+          userHitPrize.setUserId(userId);
+          userHitPrize.setOrderId(orderItem.getOrderId());
+          userHitPrize.setOrderItemId(orderItem.getItemId());
+          userHitPrize.setTicketId(ticket.getTicketId());
+          userHitPrize.setAwardsId(ticketDrawNumDTO.getId());
+          // 奖品ID等用户选择后填入
+//          userHitPrize.setPrizeId();
+          save(userHitPrize);
+        }
+      }
+    }
+    List<TicketAwardsPrize> awardsPrizes = ticketAwardsPrizeService.list(new LambdaQueryWrapper<TicketAwardsPrize>()
+        .eq(TicketAwardsPrize::getAwardsId, userHitPrize.getAwardsId()).orderByDesc(TicketAwardsPrize::getRemainQty));
+    return awardsPrizes;
+  }
+
+  @Override
+  @Transactional
+  public void cashPrize(Ticket ticket, Long userId, String awardsId, String prizeId) {
+    // 更新奖品已兑奖数量
+    TicketAwardsPrize ticketAwardsPrize = ticketAwardsPrizeService.getById(prizeId);
+    if (ticketAwardsPrize.getRemainQty() <= 0) {
+      throw new ServiceException("奖品已兑完,请重新选择");
+    }
+    // 乐观锁
+    ticketAwardsPrizeService.update(new LambdaUpdateWrapper<TicketAwardsPrize>().set(TicketAwardsPrize::getCashedQty, ticketAwardsPrize.getCashedQty() + 1)
+        .set(TicketAwardsPrize::getRemainQty, ticketAwardsPrize.getRemainQty() - 1)
+        .eq(TicketAwardsPrize::getPrizeId, prizeId).eq(TicketAwardsPrize::getCashedQty, ticketAwardsPrize.getCashedQty()));
+
+    // 保存奖品到中奖记录中
+    UserHitPrize userHitPrize = getOne(new LambdaQueryWrapper<UserHitPrize>().eq(UserHitPrize::getTicketId, ticket.getTicketId()));
+    userHitPrize.setPrizeId(prizeId);
+    updateById(userHitPrize);
+
+    // 放入仓库
+    if (ticketAwardsPrize.getPrizeType() == TicketPrizeTypeEnum.COIN) {
+      userCoinService.produce(userId, ticketAwardsPrize.getValue(), String.valueOf(userHitPrize.getId()));
+    } else if (ticketAwardsPrize.getPrizeType() == TicketPrizeTypeEnum.COUPON) {
+      couponService.distribute(ticket, userId, ticketAwardsPrize.getRefId());
+    } else {
+      userPrizeStorageService.takeInStorage(userId, ticketAwardsPrize, PrizeStorageInTypeEnum.TICKET_CASHED, String.valueOf(userHitPrize.getId()));
+    }
+  }
+
+
+
+
 }

+ 29 - 1
mp-service/src/main/java/com/qs/mp/user/service/impl/UserPrizeStorageServiceImpl.java

@@ -1,5 +1,10 @@
 package com.qs.mp.user.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.qs.mp.admin.domain.TicketAwardsPrize;
+import com.qs.mp.common.enums.PrizeStorageInTypeEnum;
+import com.qs.mp.common.enums.PrizeStorageStatusEnum;
 import com.qs.mp.user.domain.UserPrizeStorage;
 import com.qs.mp.user.mapper.UserPrizeStorageMapper;
 import com.qs.mp.user.service.IUserPrizeStorageService;
@@ -16,5 +21,28 @@ import org.springframework.stereotype.Service;
  */
 @Service
 public class UserPrizeStorageServiceImpl extends ServiceImpl<UserPrizeStorageMapper, UserPrizeStorage> implements IUserPrizeStorageService {
-
+  @Override
+  public void takeInStorage(Long userId, TicketAwardsPrize ticketAwardsPrize, PrizeStorageInTypeEnum inTypeEnum, String refId) {
+    UserPrizeStorage userPrizeStorage = getOne(new LambdaQueryWrapper<UserPrizeStorage>()
+        .eq(UserPrizeStorage::getGoodsId, ticketAwardsPrize.getRefId()).eq(UserPrizeStorage::getUserId,
+            userId)
+        .eq(UserPrizeStorage::getStatus, PrizeStorageStatusEnum.NOT_DISTRIBUTED));
+    if (null == userPrizeStorage) {
+      userPrizeStorage = new UserPrizeStorage();
+      userPrizeStorage.setUserId(userId);
+      userPrizeStorage.setGoodsId(ticketAwardsPrize.getRefId());
+      userPrizeStorage.setSkuId(null);
+      userPrizeStorage.setProperties(null);
+      userPrizeStorage.setTitle(ticketAwardsPrize.getTitle());
+      userPrizeStorage.setPicUrl(ticketAwardsPrize.getPicUrl());
+      userPrizeStorage.setGoodsNum(1);
+      userPrizeStorage.setInType(inTypeEnum);
+      userPrizeStorage.setRefId(refId);
+      userPrizeStorage.setStatus(PrizeStorageStatusEnum.NOT_DISTRIBUTED);
+      save(userPrizeStorage);
+    } else {
+      update(new LambdaUpdateWrapper<UserPrizeStorage>().set(UserPrizeStorage::getGoodsNum, userPrizeStorage.getGoodsNum() + 1)
+          .eq(UserPrizeStorage::getStorageId, userPrizeStorage.getStorageId()).eq(UserPrizeStorage::getGoodsNum, userPrizeStorage.getGoodsNum()));
+    }
+  }
 }

+ 13 - 0
mp-service/src/main/java/com/qs/mp/user/service/impl/UserTicketOrderItemServiceImpl.java

@@ -1,10 +1,14 @@
 package com.qs.mp.user.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qs.mp.common.enums.UserTicketOrderStatusEnum;
 import com.qs.mp.user.domain.UserTicketOrderItem;
 import com.qs.mp.user.mapper.UserTicketOrderItemMapper;
 import com.qs.mp.user.service.IUserTicketOrderItemService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.util.List;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 
 /**
  * <p>
@@ -17,4 +21,13 @@ import org.springframework.stereotype.Service;
 @Service
 public class UserTicketOrderItemServiceImpl extends ServiceImpl<UserTicketOrderItemMapper, UserTicketOrderItem> implements IUserTicketOrderItemService {
 
+  @Override
+  public UserTicketOrderItem queryFinishedOrderItem(Long userId, String ticketId) {
+    List<UserTicketOrderItem> itemList = getBaseMapper().listTicketOrderItemVO(new QueryWrapper<UserTicketOrderItem>().eq("t1.ticket_id", ticketId)
+        .eq("t1.userId", userId).eq("t2.status", UserTicketOrderStatusEnum.FINISHED));
+    if (CollectionUtils.isEmpty(itemList)) {
+      return null;
+    }
+    return itemList.get(0);
+  }
 }

+ 3 - 2
mp-service/src/main/resources/mapper/admin/CouponMapper.xml

@@ -12,6 +12,7 @@
         <result column="discount_type" property="discountType" />
         <result column="discount" property="discount" />
         <result column="min_order_amt" property="minOrderAmt" />
+        <result column="channel_shared_rate" property="channelSharedRate" />
         <result column="status" property="status" />
         <result column="use_area" property="useArea" />
         <result column="distribute_type" property="distributeType" />
@@ -27,7 +28,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        coupon_id, title, type, pic_url, description, discount_type, discount, min_order_amt, status, use_area, distribute_type, composite_use, due_days, valid_start, valid_end, quantity, distribute_qty, created_time, updated_time
+        coupon_id, title, type, pic_url, description, discount_type, discount, min_order_amt, channel_shared_rate, status, use_area, distribute_type, composite_use, due_days, valid_start, valid_end, quantity, distribute_qty, created_time, updated_time
     </sql>
 
-</mapper>
+</mapper>

+ 2 - 1
mp-service/src/main/resources/mapper/admin/TicketAwardsPrizeMapper.xml

@@ -13,6 +13,7 @@
         <result column="prize_type" property="prizeType" />
         <result column="quantity" property="quantity" />
         <result column="cashed_qty" property="cashedQty" />
+        <result column="remain_qty" property="remainQty" />
         <result column="value" property="value" />
         <result column="created_time" property="createdTime" />
         <result column="updated_time" property="updatedTime" />
@@ -20,7 +21,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        prize_id, box_id, awards_id, ref_id, title, pic_url, prize_type, quantity, cashed_qty, value, created_time, updated_time
+        prize_id, box_id, awards_id, ref_id, title, pic_url, prize_type, quantity, cashed_qty, remain_qty, value, created_time, updated_time
     </sql>
 
     <select id="listPrizeVO" resultType="com.qs.mp.admin.domain.vo.TicketAwardsPrizeVO">

+ 4 - 4
mp-service/src/main/resources/mapper/channel/ChannelCommissionMapper.xml

@@ -21,7 +21,7 @@
     <sql id="Base_Column_List">
         id, channel_id, order_id, ticket_id, sale_amt, sale_comm_rate, sale_comm_amt, comm_rate, comm_amt, created_time, updated_time
     </sql>
-    
+
     <!-- 查询子渠道一定时间范围内的佣金金额 -->
     <select id="getChannelCommAmtCnt" resultType="integer">
 		select IFNULL(SUM(t1.comm_amt),0) as commAmt
@@ -30,13 +30,13 @@
 		where (t2.channel_no like concat(#{channelNo},'.%') or t2.channel_no = #{channelNo})
 		AND t1.created_time between DATE_ADD(CURDATE(),INTERVAL #{days} DAY) and NOW()
 	</select>
-    
-    
+
+
     <!-- 查询子渠道一定时间范围内的销售金额 -->
     <select id="getChannelSaleAmtCnt" resultType="integer">
 		select IFNULL(SUM(t3.sale_amt),0) as saleAmt
 		from (
-			select t1.ticket_id, MIN(t1.sale_amt)
+			select t1.ticket_id, MIN(t1.sale_amt) sale_amt
 			from mp_channel_commission t1
 			left join mp_channel t2 on t1.channel_id = t2.channel_id
 			where (t2.channel_no like concat(#{channelNo},'.%') or t2.channel_no = #{channelNo})

+ 1 - 2
mp-service/src/main/resources/mapper/channel/ChannelMapper.xml

@@ -50,9 +50,8 @@
     <select id="getChannelSiteCnt" resultType="integer">
         select count(t1.channel_id) as siteCnt
 		from mp_channel t1
-		left join mp_channel t2 on t1.parent_id = t2.channel_id
 		where t1.`level`= 0
-		and (t2.channel_no like concat(#{channelNo}, '.%') or t2.channel_no = #{channelNo})
+		and (t1.channel_no like concat(#{channelNo}, '.%') or t2.channel_no = #{channelNo})
     </select>
 
 </mapper>

+ 1 - 1
mp-service/src/main/resources/mapper/channel/ChannelUserRelMapper.xml

@@ -30,7 +30,7 @@
 		from mp_channel_user_rel t1
 		left join mp_channel t2 on t1.channel_id = t2.channel_id
 		where (t2.channel_no like concat(#{channelNo}, '.%') or t2.channel_no = #{channelNo})
-		<if test="days != null and days != ''">
+		<if test="days != null">
 			<!-- AND date_format(t1.created_time,'%Y-%m-%d') between date_format(DATE_ADD(CURDATE(),INTERVAL #{days} DAY),'%Y-%m-%d') and date_format(CURDATE(),'%Y-%m-%d') -->
 			AND t1.created_time between DATE_ADD(CURDATE(),INTERVAL #{days} DAY) and NOW()
 		 </if>

+ 22 - 20
mp-service/src/main/resources/mapper/system/SysUserMapper.xml

@@ -25,13 +25,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		<result property="remark"       column="remark"       />
 		<result property="gzhOpenId"       column="gzh_open_id"       />
 		<result property="openId"       column="open_id"       />
+		<result property="channelOpenId"       column="channel_open_id"       />
 		<result property="unionId"       column="union_id"       />
 		<result property="sessionKey"       column="session_key"       />
 
 		<association property="dept"    column="dept_id" javaType="com.qs.mp.system.domain.SysDept" resultMap="deptResult" />
 		<collection  property="roles"   javaType="java.util.List"        resultMap="RoleResult" />
 	</resultMap>
-	
+
 	<resultMap id="deptResult" type="com.qs.mp.system.domain.SysDept">
 		<id     property="deptId"   column="dept_id"     />
 		<result property="parentId" column="parent_id"   />
@@ -40,7 +41,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		<result property="leader"   column="leader"      />
 		<result property="status"   column="dept_status" />
 	</resultMap>
-	
+
 	<resultMap id="RoleResult" type="com.qs.mp.system.domain.SysRole">
 		<id     property="roleId"       column="role_id"        />
 		<result property="roleName"     column="role_name"      />
@@ -49,17 +50,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		<result property="dataScope"     column="data_scope"    />
 		<result property="status"       column="role_status"    />
 	</resultMap>
-	
+
 	<sql id="selectUserVo">
-        select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, 
-        u.gzh_open_id,u.open_id,u.union_id,u.session_key,d.dept_id, d.parent_id, d.dept_name, d.order_num, d.leader, d.status as dept_status,
+        select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
+        u.gzh_open_id,u.open_id,u.channel_open_id,u.union_id,u.session_key,d.dept_id, d.parent_id, d.dept_name, d.order_num, d.leader, d.status as dept_status,
         r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
         from sys_user u
 		    left join sys_dept d on u.dept_id = d.dept_id
 		    left join sys_user_role ur on u.user_id = ur.user_id
 		    left join sys_role r on r.role_id = ur.role_id
     </sql>
-    
+
     <select id="selectUserList" parameterType="com.qs.mp.system.domain.SysUser" resultMap="SysUserResult">
 		select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
 		left join sys_dept d on u.dept_id = d.dept_id
@@ -88,7 +89,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		<!-- 数据范围过滤 -->
 		${params.dataScope}
 	</select>
-	
+
 	<select id="selectAllocatedList" parameterType="com.qs.mp.system.domain.SysUser" resultMap="SysUserResult">
 	    select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
 	    from sys_user u
@@ -105,7 +106,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		<!-- 数据范围过滤 -->
 		${params.dataScope}
 	</select>
-	
+
 	<select id="selectUnallocatedList" parameterType="com.qs.mp.system.domain.SysUser" resultMap="SysUserResult">
 	    select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
 	    from sys_user u
@@ -132,7 +133,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	    <include refid="selectUserVo"/>
 		where u.user_name = #{userName} and u.del_flag = '0'
 	</select>
-	
+
 	<select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
 		<include refid="selectUserVo"/>
 		where u.user_id = #{userId}
@@ -141,15 +142,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	<select id="checkUserNameUnique" parameterType="String" resultType="int">
 		select count(1) from sys_user where user_name = #{userName} and del_flag = '0' limit 1
 	</select>
-	
+
 	<select id="checkPhoneUnique" parameterType="String" resultMap="SysUserResult">
 		select user_id, phonenumber from sys_user where phonenumber = #{phonenumber} and del_flag = '0' limit 1
 	</select>
-	
+
 	<select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult">
 		select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1
 	</select>
-	
+
 	<insert id="insertUser" parameterType="com.qs.mp.system.domain.SysUser" useGeneratedKeys="true" keyProperty="userId">
  		insert into sys_user(
  			<if test="userId != null and userId != 0">user_id,</if>
@@ -181,7 +182,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  			sysdate()
  		)
 	</insert>
-	
+
 	<update id="updateUser" parameterType="com.qs.mp.system.domain.SysUser">
  		update sys_user
  		<set>
@@ -200,6 +201,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  			<if test="remark != null">remark = #{remark},</if>
 			<if test="gzhOpenId != null">gzh_open_id = #{gzhOpenId},</if>
 			<if test="openId != null">open_id = #{openId},</if>
+			<if test="channelOpenId != null">channel_open_id = #{channelOpenId},</if>
 			<if test="unionId != null">union_id = #{unionId},</if>
 			<if test="sessionKey != null">session_key = #{sessionKey},</if>
  			update_time = sysdate()
@@ -214,24 +216,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	<update id="updateUserStatus" parameterType="com.qs.mp.system.domain.SysUser">
  		update sys_user set status = #{status} where user_id = #{userId}
 	</update>
-	
+
 	<update id="updateUserAvatar" parameterType="com.qs.mp.system.domain.SysUser">
  		update sys_user set avatar = #{avatar} where user_name = #{userName} and del_flag = '0'
 	</update>
-	
+
 	<update id="resetUserPwd" parameterType="com.qs.mp.system.domain.SysUser">
  		update sys_user set password = #{password} where user_name = #{userName} and del_flag = '0'
 	</update>
-	
+
 	<delete id="deleteUserById" parameterType="Long">
 		update sys_user set del_flag = '1' where user_id = #{userId}
  	</delete>
- 	
+
  	<delete id="deleteUserByIds" parameterType="Long">
  		update sys_user set del_flag = '1' where user_id in
  		<foreach collection="array" item="userId" open="(" separator="," close=")">
  			#{userId}
-        </foreach> 
+        </foreach>
  	</delete>
-	
-</mapper> 
+
+</mapper>

+ 2 - 3
mp-service/src/main/resources/mapper/user/UserCoinMapper.xml

@@ -4,7 +4,6 @@
 
     <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.qs.mp.user.domain.UserCoin">
-        <id column="id" property="id" />
         <result column="user_id" property="userId" />
         <result column="coin" property="coin" />
         <result column="created_time" property="createdTime" />
@@ -13,7 +12,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, user_id, coin, created_time, updated_time
+        user_id, coin, created_time, updated_time
     </sql>
 
-</mapper>
+</mapper>

+ 19 - 0
mp-service/src/main/resources/mapper/user/UserCouponChannelMapper.xml

@@ -0,0 +1,19 @@
+<?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.qs.mp.user.mapper.UserCouponChannelMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.qs.mp.user.domain.UserCouponChannel">
+        <id column="id" property="id" />
+        <result column="user_coupon_id" property="userCouponId" />
+        <result column="channel_id" property="channelId" />
+        <result column="created_time" property="createdTime" />
+        <result column="updated_time" property="updatedTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, user_coupon_id, channel_id, created_time, updated_time
+    </sql>
+
+</mapper>

+ 8 - 1
mp-service/src/main/resources/mapper/user/UserCouponMapper.xml

@@ -22,4 +22,11 @@
         id, user_id, verify_code, coupon_id, valid_start, valid_end, order_id, verify_time, status, created_time, updated_time
     </sql>
 
-</mapper>
+    <select id="listUserCouponVO" resultType="com.qs.mp.user.domain.vo.UserCouponVO">
+        select t1.*, t2.coupon_id, t2.title, t2.type, t2.pic_url, t2.description, t2.discount_type, t2.discount, t2.min_order_amt, t2.use_area, t2.distribute_type, t2.composite_use
+        from mp_user_coupon t1
+        left join mp_coupon t2 on t1.coupon_id = t2.coupon_id
+            ${ew.customSqlSegment}
+    </select>
+
+</mapper>

+ 4 - 2
mp-service/src/main/resources/mapper/user/UserPrizeStorageMapper.xml

@@ -12,6 +12,8 @@
         <result column="title" property="title" />
         <result column="pic_url" property="picUrl" />
         <result column="goods_num" property="goodsNum" />
+        <result column="in_type" property="inType" />
+        <result column="ref_id" property="refId" />
         <result column="status" property="status" />
         <result column="created_time" property="createdTime" />
         <result column="updated_time" property="updatedTime" />
@@ -19,7 +21,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        storage_id, user_id, goods_id, sku_id, properties, title, pic_url, goods_num, status, created_time, updated_time
+        storage_id, user_id, goods_id, sku_id, properties, title, pic_url, goods_num, in_type, ref_id, status, created_time, updated_time
     </sql>
 
-</mapper>
+</mapper>

+ 2 - 1
mp-service/src/main/resources/mapper/user/UserTicketOrderItemMapper.xml

@@ -9,13 +9,14 @@
         <result column="user_id" property="userId" />
         <result column="box_id" property="boxId" />
         <result column="ticket_id" property="ticketId" />
+        <result column="channel_id" property="channelId" />
         <result column="created_time" property="createdTime" />
         <result column="updated_time" property="updatedTime" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        item_id, order_id, user_id, box_id, ticket_id, created_time, updated_time
+        item_id, order_id, user_id, box_id, ticket_id, channel_id, created_time, updated_time
     </sql>
 
     <select id="listTicketOrderItemVO" resultMap="BaseResultMap">