From 94cd851354e7e0ab5ed93063a8195a2fb64f1ef3 Mon Sep 17 00:00:00 2001 From: Stormeye Wu Date: Wed, 28 Aug 2019 16:29:27 +0800 Subject: [PATCH] =?UTF-8?q?[POS][=E4=BF=AE=E6=94=B9]:POS=E4=BC=9A=E5=91=98?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E6=8E=A5=E5=8F=A3=E4=BF=AE=E6=94=B9=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E4=BB=98=E8=AE=A1=E7=AE=97=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iformall/controller/PosController.java | 56 ++- .../java/com/iformall/service/PosService.java | 16 +- .../iformall/service/impl/PosServiceImpl.java | 407 +++++++++++++----- .../com/iformall/pos/test/PosAppTest.java | 43 +- ...ActionType.java => EnumPosActionType.java} | 19 +- .../java/com/iformall/pay/WxPayConstant.java | 3 + 6 files changed, 420 insertions(+), 124 deletions(-) rename mallinkService/src/main/java/com/iformall/enums/{EnumVerifyActionType.java => EnumPosActionType.java} (58%) diff --git a/mallinkPosApi/src/main/java/com/iformall/controller/PosController.java b/mallinkPosApi/src/main/java/com/iformall/controller/PosController.java index 708fe46f1..0078b6477 100644 --- a/mallinkPosApi/src/main/java/com/iformall/controller/PosController.java +++ b/mallinkPosApi/src/main/java/com/iformall/controller/PosController.java @@ -210,7 +210,7 @@ public class PosController extends BaseController { "\"coupon_order_id\":\"string(必填)\"," + "\"scene_type\":\"string(1支付2:核销)(必填)\"," + "\"pos_order_id\":\"string(选填)\"," + - "\"pos_amount\":\"单位(分)(选填)\"}") + "\"pos_amount\":\"string单位(分)(选填)\"}") @PostMapping("/checkCouponOrder") @SystemControllerLog(description = "券检查是否可用-场景(核销/支付)") public Map checkCouponOrder(@RequestBody Map params) { @@ -226,7 +226,47 @@ public class PosController extends BaseController { logger.info(WxPayConstant.RES_KEY + ": " + resKey); try { - retMap = posService.couponOrderVerifyDo(params, EnumVerifyActionType.CHECK); + retMap = posService.couponOrderVerifyDo(params, EnumPosActionType.CHECK); + return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", + WxPayConstant.RET_SUCCESS, null); + } catch (MallinkException e) { + return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", + WxPayConstant.RET_FAIL, + e.getErrorCode(), e.getMessage()); + } catch (Exception e) { + return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", + WxPayConstant.RET_FAIL, + 500, e.getMessage()); + } + } + + @ApiOperation(value = "支付计算接口", notes = "{" + + "\"dev_id\":\"string(必填)\"," + + "\"nonce_str\":\"string(必填)\"," + + "\"tenant_id\":\"string(必填)\"," + + "\"merchant_id\":\"string(必填)\"," + + "\"bu_user_id\":\"string(必填)\"," + + "\"mem_id\":\"string(选填)\"," + + "\"mem_phone\":\"string(选填)\"," + + "\"sel_co_list\":\"json array[券包ID list](必填),例[\"1\",\"2\",\"3\"]\"" + + "\"pos_order_id\":\"string(选填)\"," + + "\"pos_amount\":\"string单位(分)(必填)\"}") + @PostMapping("/calcMem") + @SystemControllerLog(description = "支付计算接口") + public Map calcForMem(@RequestBody Map params) { + JSONObject payContent = new JSONObject(); + + // 1. check sign + Map retMap = checkSign(params, payContent); + if (retMap != null) { + // 签名相关异常返回 + return retMap; + } + String resKey = payContent.getString(WxPayConstant.RES_KEY); + logger.info(WxPayConstant.RES_KEY + ": " + resKey); + + try { + retMap = posService.calcMem(params); return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", WxPayConstant.RET_SUCCESS, null); } catch (MallinkException e) { @@ -264,7 +304,7 @@ public class PosController extends BaseController { logger.info(WxPayConstant.RES_KEY + ": " + resKey); try { - retMap = posService.couponOrderVerifyDo(params, EnumVerifyActionType.PRE_VERIFY); + retMap = posService.couponOrderVerifyDo(params, EnumPosActionType.PRE_VERIFY); return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", WxPayConstant.RET_SUCCESS, null); } catch (MallinkException e) { @@ -301,7 +341,7 @@ public class PosController extends BaseController { logger.info(WxPayConstant.RES_KEY + ": " + resKey); try { - retMap = posService.couponOrderVerifyCancel(params, EnumVerifyActionType.PRE_VERIFY_CANCEL); + retMap = posService.couponOrderVerifyCancel(params, EnumPosActionType.PRE_VERIFY_CANCEL); return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", WxPayConstant.RET_SUCCESS, null); } catch (MallinkException e) { @@ -338,7 +378,7 @@ public class PosController extends BaseController { logger.info(WxPayConstant.RES_KEY + ": " + resKey); try { - retMap = posService.couponOrderVerifyDo(params, EnumVerifyActionType.VERIFY_INDEPENT); + retMap = posService.couponOrderVerifyDo(params, EnumPosActionType.VERIFY_INDEPENT); } catch (MallinkException e) { return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", WxPayConstant.RET_FAIL, @@ -397,7 +437,7 @@ public class PosController extends BaseController { logger.info(WxPayConstant.RES_KEY + ": " + resKey); try { - retMap = posService.couponOrderVerifyDo(params, EnumVerifyActionType.VERIFY_PAY); + retMap = posService.couponOrderVerifyDo(params, EnumPosActionType.VERIFY_PAY); } catch (MallinkException e) { return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", WxPayConstant.RET_FAIL, @@ -437,7 +477,7 @@ public class PosController extends BaseController { logger.info(WxPayConstant.RES_KEY + ": " + resKey); try { - retMap = posService.couponOrderVerifyListDo(params, EnumVerifyActionType.VERIFY_PAY); + retMap = posService.couponOrderVerifyListDo(params, EnumPosActionType.VERIFY_PAY); } catch (MallinkException e) { return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", WxPayConstant.RET_FAIL, @@ -507,7 +547,7 @@ public class PosController extends BaseController { logger.info(WxPayConstant.RES_KEY + ": " + resKey); try { - retMap = posService.couponOrderVerifyCancel(params, EnumVerifyActionType.VERIFY_CANCEL); + retMap = posService.couponOrderVerifyCancel(params, EnumPosActionType.VERIFY_CANCEL); return buildReturnMap(retMap, resKey, WxPayConstant.RET_SUCCESS, "", WxPayConstant.RET_SUCCESS, null); } catch (MallinkException e) { diff --git a/mallinkPosApi/src/main/java/com/iformall/service/PosService.java b/mallinkPosApi/src/main/java/com/iformall/service/PosService.java index 33af9520d..3d818dcf3 100644 --- a/mallinkPosApi/src/main/java/com/iformall/service/PosService.java +++ b/mallinkPosApi/src/main/java/com/iformall/service/PosService.java @@ -1,8 +1,7 @@ package com.iformall.service; -import com.iformall.enums.EnumVerifyActionType; +import com.iformall.enums.EnumPosActionType; import com.iformall.exception.MallinkException; -import org.springframework.web.bind.annotation.RequestBody; import java.util.Map; @@ -35,6 +34,13 @@ public interface PosService { */ Map checkMember(Map params) throws MallinkException; + /** + * 支付计算 + * @param params + * @return + */ + Map calcMem(Map params) throws MallinkException; + /** * 核销动作(check, pre_verify, verify) * @param params @@ -42,7 +48,7 @@ public interface PosService { * @return * @throws MallinkException */ - Map couponOrderVerifyDo(Map params, EnumVerifyActionType actionType) throws MallinkException; + Map couponOrderVerifyDo(Map params, EnumPosActionType actionType) throws MallinkException; /** * 核销取消(check, pre_verify, verify) for selected_coupon_list @@ -51,7 +57,7 @@ public interface PosService { * @return * @throws MallinkException */ - Map couponOrderVerifyCancel(Map params, EnumVerifyActionType actionType) throws MallinkException; + Map couponOrderVerifyCancel(Map params, EnumPosActionType actionType) throws MallinkException; /** * 核销动作(check, pre_verify, verify) for selected_coupon_list @@ -60,7 +66,7 @@ public interface PosService { * @return * @throws MallinkException */ - Map couponOrderVerifyListDo(Map params, EnumVerifyActionType actionType) throws MallinkException; + Map couponOrderVerifyListDo(Map params, EnumPosActionType actionType) throws MallinkException; /** * 消费卡POS预支付 diff --git a/mallinkPosApi/src/main/java/com/iformall/service/impl/PosServiceImpl.java b/mallinkPosApi/src/main/java/com/iformall/service/impl/PosServiceImpl.java index 5d047f1a5..250192127 100644 --- a/mallinkPosApi/src/main/java/com/iformall/service/impl/PosServiceImpl.java +++ b/mallinkPosApi/src/main/java/com/iformall/service/impl/PosServiceImpl.java @@ -58,8 +58,6 @@ public class PosServiceImpl implements PosService { private final WxCouponMapper couponMapper; - - /** * 门店用户登录检查 * @param params @@ -209,6 +207,7 @@ public class PosServiceImpl implements PosService { String tenantId = params.get(WxPayConstant.TENANT_ID); // 租户ID String merchantIdStr = params.get(WxPayConstant.MERCHANT_ID); // 商户ID + String buUserIdStr = params.get(WxPayConstant.BUSER_ID); // POS操作员ID String memIdStr = params.get(WxPayConstant.MEM_ID); // 会员ID String memPhoneStr = params.get(WxPayConstant.MEM_PHONE); // 会员手机 String posOrderIdStr = params.get(WxPayConstant.POS_ORDER_ID); // POS订单ID @@ -234,83 +233,54 @@ public class PosServiceImpl implements PosService { errParam(WxPayConstant.POS_AMOUNT); } - if (StringUtils.isBlank(memIdStr) && StringUtils.isBlank(memPhoneStr)) { - String errMessage = "please give one value for mem_id or mem_phone"; - logger.error(errMessage); - throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), errMessage); - } - WxCUser user = null; - if (StringUtils.isNotBlank(memIdStr)) { - Long memId; - try { - memId = Long.valueOf(memIdStr); - } catch (NumberFormatException e) { - logger.error(e.getMessage()); - throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), e.getMessage()); - } - - user = cUserService.getById(memId); - } - if (StringUtils.isNotBlank(memPhoneStr)) { - WxCUser q = new WxCUser(); - q.setTenantId(tenantId); - q.setPhone(memPhoneStr); - user = cUserService.getByObject(q); - } - - if (user == null) { - logger.error(ErrorCode.USER_NOT_MEMBER.getMessage()); - throw new MallinkException(ErrorCode.USER_NOT_MEMBER); - } - - Long merchantId, posOrderId; - Integer posAmount = 0; + // 1. 商户ID, B端用户ID + Long merchantId, buUserId, posOrderId; + Long posAmount = 0L; try { merchantId = Long.valueOf(merchantIdStr); + buUserId = Long.valueOf(buUserIdStr); posOrderId = Long.valueOf(posOrderIdStr); - posAmount = Integer.valueOf(posAmountStr); + posAmount = Long.valueOf(posAmountStr); } catch (NumberFormatException e) { logger.error(e.getMessage()); throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR); } - Integer discount = 100; - if (config.getDiscount().equals(EnumEnableType.Enable.getCode())) { - - // Level, 折扣 - List levelList = levelConfigService.getByTenantId(tenantId); - - String level = WxLevelConfigService.DEFAULT_LEVEL; - Long levelId = 0L; - for (WxLevelConfig levelConfig : levelList) { - if (user.getScore() >= levelConfig.getPoints()) { - if (levelConfig.getDiscountEnable().equals(EnumLevelConfigDiscountStatus.ENABLE.getCode())) - levelId = levelConfig.getId(); - level = levelConfig.getLevel(); - } - } - - WxLevelMerchant levelMerchant = new WxLevelMerchant(); - levelMerchant.setTenantId(tenantId); - levelMerchant.setLevelId(levelId); - levelMerchant.setMerchantId(merchantId); - List levelMerchantList = levelConfigService.findListCVo(levelMerchant); - if (levelMerchantList.size() >= 1) { - WxLevelMerchantCVo levelMerchantCVo = levelMerchantList.get(0); - if (levelMerchantCVo != null) { - discount = levelMerchantCVo.getDiscount(); - } - } + // 2. check merchant + WxMerchant merchant = checkAndGetMerchant(merchantId); + // 3. check buUser + WxMerchantBUser buUser = checkAndGetMerchantUser(buUserId, merchantId); - retMap.put(WxPayConstant.MEM_LEVEL, level); + // 3. 会员ID + if (StringUtils.isBlank(memIdStr) && StringUtils.isBlank(memPhoneStr)) { + errParam2(WxPayConstant.MEM_ID, WxPayConstant.MEM_PHONE); + } + // 4. 获取会员信息 + WxCUser user = getMemUser(tenantId, memIdStr, memPhoneStr); + if (user == null) { + logger.error(ErrorCode.USER_NOT_MEMBER.getMessage()); + throw new MallinkException(ErrorCode.USER_NOT_MEMBER); } - retMap.put(WxPayConstant.MEM_DISCOUNT, discount.toString()); + // 5. 会员level及折扣 + Integer discount = 100; + if (config.getDiscount().equals(EnumEnableType.Enable.getCode())) { + discount = getMemLevelDiscount(retMap, tenantId, user, merchantId); + } else { + retMap.put(WxPayConstant.MEM_DISCOUNT_RATE, discount.toString()); + } Double remainAmountD = posAmount * 1.0D * discount / 100; - long remainAmount = Math.round(remainAmountD); + Long remainAmount = Math.round(remainAmountD); + Long discountAmount = posAmount - remainAmount; - // 优惠券列表 + retMap.put(WxPayConstant.MEM_ID, user.getId().toString()); + retMap.put(WxPayConstant.MEM_PHONE, user.getPhone()); + retMap.put(WxPayConstant.ORG_AMOUNT, posAmountStr); + retMap.put(WxPayConstant.DISCOUNT_AMOUNT, String.valueOf(discountAmount)); + retMap.put(WxPayConstant.REMAIN_AMOUNT, String.valueOf(remainAmount)); + + // 6. 优惠券列表 if (config.getCoupon().equals(EnumEnableType.Enable.getCode())) { Map coQ = new HashMap<>(); coQ.put("tenantId", tenantId); @@ -340,17 +310,244 @@ public class PosServiceImpl implements PosService { } else { retMap.put(WxPayConstant.AVAIL_CO_LIST, "[]"); } + return retMap; + } + + + + @Override + @Transactional(rollbackFor = Exception.class) + public Map calcMem(Map params) throws MallinkException { + Map retMap = new HashMap<>(); + + String tenantId = params.get(WxPayConstant.TENANT_ID); // 租户ID + String merchantIdStr = params.get(WxPayConstant.MERCHANT_ID); // 商户ID + String buUserIdStr = params.get(WxPayConstant.BUSER_ID); // POS操作员ID + String memIdStr = params.get(WxPayConstant.MEM_ID); // 会员ID + String memPhoneStr = params.get(WxPayConstant.MEM_PHONE); // 会员手机 + String posAmountStr = params.get(WxPayConstant.POS_AMOUNT); // POS订单金额(单位:分) + String selCoListStr = params.get(WxPayConstant.SELECTED_COUPON_ORDER_LIST); // 选中的优惠券列表 + + if (StringUtils.isBlank(tenantId)) { + errParam(WxPayConstant.TENANT_ID); + } + + PosMallConfig config = posMallConfigService.getByTenantId(tenantId); + if (config == null) { + logger.error(ErrorCode.POS_CONFIG_NOT_FOUND.getMessage()); + throw new MallinkException(ErrorCode.POS_CONFIG_NOT_FOUND); + } + + if (StringUtils.isBlank(merchantIdStr)) { + errParam(WxPayConstant.MERCHANT_ID); + } + if (StringUtils.isBlank(posAmountStr)) { + errParam(WxPayConstant.POS_AMOUNT); + } + if (StringUtils.isBlank(selCoListStr)) { + errParam(WxPayConstant.SELECTED_COUPON_ORDER_LIST); + } + if (StringUtils.isBlank(memIdStr) && StringUtils.isBlank(memPhoneStr)) { + errParam2(WxPayConstant.MEM_ID, WxPayConstant.MEM_PHONE); + } + // 1. 商户ID, B端用户ID + Long merchantId, buUserId, posOrderId; + Long posAmount = 0L; + try { + merchantId = Long.valueOf(merchantIdStr); + buUserId = Long.valueOf(buUserIdStr); + posAmount = Long.valueOf(posAmountStr); + } catch (NumberFormatException e) { + logger.error(e.getMessage()); + throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR); + } + + // 2. check merchant + WxMerchant merchant = checkAndGetMerchant(merchantId); + // 3. check buUser + WxMerchantBUser buUser = checkAndGetMerchantUser(buUserId, merchantId); + + // 4. 获取会员信息 + WxCUser user = getMemUser(tenantId, memIdStr, memPhoneStr); + if (user == null) { + logger.error(ErrorCode.USER_NOT_MEMBER.getMessage()); + throw new MallinkException(ErrorCode.USER_NOT_MEMBER); + } + + // 5. 获取折扣率 + Integer discount = 100; + if (config.getDiscount().equals(EnumEnableType.Enable.getCode())) { + discount = getMemLevelDiscount(retMap, tenantId, user, merchantId); + } else { + retMap.put(WxPayConstant.MEM_DISCOUNT_RATE, discount.toString()); + } + // 6. 计算折扣及剩余支付金额(四舍五入) + Double remainAmountD = posAmount * 1.0D * discount / 100; + Long remainAmount = Math.round(remainAmountD); + Long discountAmount = posAmount - remainAmount; retMap.put(WxPayConstant.MEM_ID, user.getId().toString()); retMap.put(WxPayConstant.MEM_PHONE, user.getPhone()); retMap.put(WxPayConstant.ORG_AMOUNT, posAmountStr); + retMap.put(WxPayConstant.DISCOUNT_AMOUNT, String.valueOf(discountAmount)); + + // 7. 计算券抵扣及剩余支付金额(四舍五入) + JSONArray selCoArr = JSONObject.parseArray(selCoListStr); + if (selCoArr.size() <= 0) { + errParam(WxPayConstant.SELECTED_COUPON_ORDER_LIST); + } + JSONArray coRetArr = new JSONArray(); + for(int i=0;i oneRetMap = calcOneCouponVerify(merchantId, posAmount, couponOrderIdStr); + coRetArr.add(oneRetMap); + posAmount = Long.valueOf(oneRetMap.get(WxPayConstant.REMAIN_AMOUNT)); + if (posAmount.equals(0L)) { + break; + } + } catch (MallinkException e) { + throw new MallinkException(e.getErrorCode(), e.getMessage()); + } catch (Exception e) { + throw new MallinkException(500, e.getMessage()); + } + } + } + retMap.put(WxPayConstant.MEM_COUPONS, coRetArr.toJSONString()); + retMap.put(WxPayConstant.REMAIN_AMOUNT, String.valueOf(posAmount)); + return retMap; + } + + private Map calcOneCouponVerify(Long merchantId, Long posAmount, String couponOrderIdStr) { + Map retMap = new HashMap<>(); + // 1. 获取券包信息 + WxCouponOrderCVo couponOrderCVo = couponOrderService.detailCUserVo(couponOrderIdStr); + if (couponOrderCVo == null) { + logger.error(ErrorCode.COUPON_ORDER_IS_NULL.getMessage() + ": " + couponOrderIdStr); + throw new MallinkException(ErrorCode.COUPON_ORDER_IS_NULL); + } + // 2. 检查券类型 + if (!(couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_MULTIMCH.getCode()) || + couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_MANJIAN.getCode()) || + couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_DAIJIN.getCode()) + )) { + logger.error(ErrorCode.VERIFY_COUPON_TYPE_NOT_SUPPORT.getMessage()); + throw new MallinkException(ErrorCode.VERIFY_COUPON_TYPE_NOT_SUPPORT); + } + + // 3. 券包状态检查 + if (couponOrderCVo.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_OVER_TIME.getCode()) { + logger.error("已过期: couponOrder-" + couponOrderIdStr); + throw new MallinkException(ErrorCode.COUPON_ORDER_IS_OVER_TIME); + } + if (couponOrderCVo.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_INVALID.getCode()) { + logger.error("已作废: couponOrder-" + couponOrderIdStr); + throw new MallinkException(ErrorCode.COUPON_ORDER_IS_INVALID); + } + if (couponOrderCVo.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_USED.getCode()) { + logger.error("已经核销过的券: couponOrder-" + couponOrderIdStr); + throw new MallinkException(ErrorCode.COUPON_ORDER_IS_USED); + } + + Date now = new Date(); + // 4. 检查有效期 + if (couponOrderCVo.getValidStartDate() != null && couponOrderCVo.getValidEndDate() != null) { + if (couponOrderCVo.getValidStartDate().getTime() > now.getTime()) { + logger.error("此券有效期未开始:" + couponOrderIdStr); + throw new MallinkException(ErrorCode.COUPON_ORDER_IS_EARLIER_THAN_VALIDDATE); + } + if (couponOrderCVo.getValidEndDate().getTime() < now.getTime()) { + logger.error("此券有效期已结束:" + couponOrderIdStr); + throw new MallinkException(ErrorCode.COUPON_ORDER_IS_EARLIER_THAN_VALIDDATE); + } + } + // 5 券已过期 + if (couponOrderCVo.getExpiredTime().before(now)) { + logger.error("此券已过期:" + couponOrderIdStr); + throw new MallinkException(ErrorCode.COUPON_ORDER_IS_EARLIER_THAN_VALIDDATE); + } + + Long couponOrderId = couponOrderCVo.getId(); + + // 6. 检查适用门店 + WxCouponMerchant wxCouponMerchant = new WxCouponMerchant(); + wxCouponMerchant.setProductId(couponOrderCVo.getCouponId()); + wxCouponMerchant.setStatus(EnumCouponMerchantStatus.COUPON_MERCHANT_STATUS_VALID.getCode()); + List couponMerchantList = couponMerchantMapper.findList(wxCouponMerchant); + if (!couponMerchantList.stream().anyMatch(cm->cm.getMerchantId().equals(merchantId))){ + logger.error("券: coupon-" + couponOrderCVo.getCouponId() + "核销: couponMerchantId-" + merchantId + " 门店不适用"); + throw new MallinkException(ErrorCode.VERIFY_COUPON_ORDER_MERCHANT_IS_NULL); + } + + Long remainAmount = posAmount - couponOrderCVo.getPrice(); + if (remainAmount < 0L) { + remainAmount = 0L; + } + Long discountAmount = posAmount - remainAmount; + retMap.put(WxPayConstant.ID, couponOrderIdStr); + retMap.put(WxPayConstant.PRICE, String.valueOf(couponOrderCVo.getPrice())); + retMap.put(WxPayConstant.DEDUCT_AMOUNT, String.valueOf(discountAmount)); retMap.put(WxPayConstant.REMAIN_AMOUNT, String.valueOf(remainAmount)); return retMap; } + private WxCUser getMemUser(String tenantId, String memIdStr, String memPhoneStr) { + WxCUser user = null; + if (StringUtils.isNotBlank(memIdStr)) { + Long memId; + try { + memId = Long.valueOf(memIdStr); + } catch (NumberFormatException e) { + logger.error(e.getMessage()); + throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), e.getMessage()); + } + + user = cUserService.getById(memId); + } + if (StringUtils.isNotBlank(memPhoneStr)) { + WxCUser q = new WxCUser(); + q.setTenantId(tenantId); + q.setPhone(memPhoneStr); + user = cUserService.getByObject(q); + } + return user; + } + + private Integer getMemLevelDiscount(Map retMap, String tenantId, WxCUser user, Long merchantId) { + Integer discount = 100; + String level = WxLevelConfigService.DEFAULT_LEVEL; + // Level, 折扣 + List levelList = levelConfigService.getByTenantId(tenantId); + Long levelId = 0L; + for (WxLevelConfig levelConfig : levelList) { + if (user.getScore() >= levelConfig.getPoints()) { + if (levelConfig.getDiscountEnable().equals(EnumLevelConfigDiscountStatus.ENABLE.getCode())) + levelId = levelConfig.getId(); + level = levelConfig.getLevel(); + } + } + + WxLevelMerchant levelMerchant = new WxLevelMerchant(); + levelMerchant.setTenantId(tenantId); + levelMerchant.setLevelId(levelId); + levelMerchant.setMerchantId(merchantId); + List levelMerchantList = levelConfigService.findListCVo(levelMerchant); + if (levelMerchantList.size() >= 1) { + WxLevelMerchantCVo levelMerchantCVo = levelMerchantList.get(0); + if (levelMerchantCVo != null) { + discount = levelMerchantCVo.getDiscount(); + } + } + + retMap.put(WxPayConstant.MEM_LEVEL, level); + retMap.put(WxPayConstant.MEM_DISCOUNT_RATE, discount.toString()); + return discount; + } + @Override @Transactional(rollbackFor = Exception.class) - public Map couponOrderVerifyDo(@RequestBody Map params, EnumVerifyActionType actionType) throws MallinkException { + public Map couponOrderVerifyDo(@RequestBody Map params, EnumPosActionType actionType) throws MallinkException { Map retMap = new HashMap<>(); String tenantId = params.get(WxPayConstant.TENANT_ID); // 租户ID @@ -383,7 +580,7 @@ public class PosServiceImpl implements PosService { if (StringUtils.isBlank(couponOrderIdStr)) { errParam(WxPayConstant.COUPON_ORDER_ID); } - if (actionType.equals(EnumVerifyActionType.CHECK)) { + if (actionType.equals(EnumPosActionType.CHECK)) { if (StringUtils.isBlank(sceneTypeStr)) { errParam(WxPayConstant.SCENE_TYPE); } @@ -393,20 +590,20 @@ public class PosServiceImpl implements PosService { logger.error(e.getMessage()); throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), e.getMessage()); } - } else if (actionType.equals(EnumVerifyActionType.VERIFY_INDEPENT)) { + } else if (actionType.equals(EnumPosActionType.VERIFY_INDEPENT)) { sceneType = EnumPosSceneType.VERIFY.getCode(); - } else if (actionType.equals(EnumVerifyActionType.VERIFY_PAY)) { + } else if (actionType.equals(EnumPosActionType.VERIFY_PAY)) { sceneType = EnumPosSceneType.PAY.getCode(); } - if (actionType.equals(EnumVerifyActionType.PRE_VERIFY) || - actionType.equals(EnumVerifyActionType.VERIFY_INDEPENT) || - actionType.equals(EnumVerifyActionType.VERIFY_PAY)) { + if (actionType.equals(EnumPosActionType.PRE_VERIFY) || + actionType.equals(EnumPosActionType.VERIFY_INDEPENT) || + actionType.equals(EnumPosActionType.VERIFY_PAY)) { if (StringUtils.isBlank(posOrderIdStr)) { errParam(WxPayConstant.POS_ORDER_ID); } } - if (actionType.equals(EnumVerifyActionType.PRE_VERIFY) || - actionType.equals(EnumVerifyActionType.VERIFY_PAY)) { + if (actionType.equals(EnumPosActionType.PRE_VERIFY) || + actionType.equals(EnumPosActionType.VERIFY_PAY)) { if (StringUtils.isBlank(posAmountStr)) { errParam(WxPayConstant.POS_AMOUNT); } @@ -417,13 +614,13 @@ public class PosServiceImpl implements PosService { try { merchantId = Long.valueOf(merchantIdStr); buUserId = Long.valueOf(buUserIdStr); - if (actionType.equals(EnumVerifyActionType.PRE_VERIFY) || - actionType.equals(EnumVerifyActionType.VERIFY_INDEPENT) || - actionType.equals(EnumVerifyActionType.VERIFY_PAY)) { + if (actionType.equals(EnumPosActionType.PRE_VERIFY) || + actionType.equals(EnumPosActionType.VERIFY_INDEPENT) || + actionType.equals(EnumPosActionType.VERIFY_PAY)) { posOrderId = Long.valueOf(posOrderIdStr); } - if (actionType.equals(EnumVerifyActionType.PRE_VERIFY) || - actionType.equals(EnumVerifyActionType.VERIFY_PAY)) { + if (actionType.equals(EnumPosActionType.PRE_VERIFY) || + actionType.equals(EnumPosActionType.VERIFY_PAY)) { posAmount = Integer.valueOf(posAmountStr); } } catch (NumberFormatException e) { @@ -455,7 +652,7 @@ public class PosServiceImpl implements PosService { @Override @Transactional(rollbackFor = Exception.class) - public Map couponOrderVerifyListDo(@RequestBody Map params, EnumVerifyActionType actionType) throws MallinkException { + public Map couponOrderVerifyListDo(@RequestBody Map params, EnumPosActionType actionType) throws MallinkException { Map retMap = new HashMap<>(); String tenantId = params.get(WxPayConstant.TENANT_ID); // 租户ID @@ -487,7 +684,7 @@ public class PosServiceImpl implements PosService { if (StringUtils.isBlank(selCoListStr)) { errParam(WxPayConstant.SELECTED_COUPON_ORDER_LIST); } - if (actionType.equals(EnumVerifyActionType.PRE_VERIFY) || actionType.equals(EnumVerifyActionType.VERIFY_INDEPENT)) { + if (actionType.equals(EnumPosActionType.PRE_VERIFY) || actionType.equals(EnumPosActionType.VERIFY_INDEPENT)) { if (StringUtils.isBlank(posOrderIdStr)) { errParam(WxPayConstant.POS_ORDER_ID); } @@ -501,9 +698,9 @@ public class PosServiceImpl implements PosService { try { merchantId = Long.valueOf(merchantIdStr); buUserId = Long.valueOf(buUserIdStr); - if (actionType.equals(EnumVerifyActionType.PRE_VERIFY) || - actionType.equals(EnumVerifyActionType.VERIFY_INDEPENT) || - actionType.equals(EnumVerifyActionType.VERIFY_PAY)) { + if (actionType.equals(EnumPosActionType.PRE_VERIFY) || + actionType.equals(EnumPosActionType.VERIFY_INDEPENT) || + actionType.equals(EnumPosActionType.VERIFY_PAY)) { posOrderId = Long.valueOf(posOrderIdStr); posAmount = Integer.valueOf(posAmountStr); } @@ -547,7 +744,7 @@ public class PosServiceImpl implements PosService { } private Map doVerifyActionForOneCouponOrder( - EnumVerifyActionType actionType, Integer sceneType, + EnumPosActionType actionType, Integer sceneType, String tenantId, String merchantIdStr, Long merchantId, String buUserIdStr, Long buUserId, String posOrderIdStr, Long posOrderId, Integer posAmount, @@ -560,7 +757,7 @@ public class PosServiceImpl implements PosService { throw new MallinkException(ErrorCode.COUPON_ORDER_IS_NULL); } // 4.1 检查券类型 - if (actionType.equals(EnumVerifyActionType.CHECK)) { + if (actionType.equals(EnumPosActionType.CHECK)) { if (sceneType.equals(EnumPosSceneType.VERIFY.getCode())) { if (!(couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_MULTIMCH.getCode()) || couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_LIPIN.getCode()) || @@ -580,7 +777,7 @@ public class PosServiceImpl implements PosService { throw new MallinkException(ErrorCode.VERIFY_COUPON_TYPE_NOT_SUPPORT); } } - } else if (actionType.equals(EnumVerifyActionType.PRE_VERIFY) || actionType.equals(EnumVerifyActionType.VERIFY_PAY)) { + } else if (actionType.equals(EnumPosActionType.PRE_VERIFY) || actionType.equals(EnumPosActionType.VERIFY_PAY)) { if (!(couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_MULTIMCH.getCode()) || couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_MANJIAN.getCode()) || couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_DAIJIN.getCode()) @@ -594,7 +791,7 @@ public class PosServiceImpl implements PosService { throw new MallinkException(ErrorCode.VERIFY_MANJIAN_USER_PRICE_NOT_TOUCH); } } - } else if (actionType.equals(EnumVerifyActionType.VERIFY_INDEPENT)) { + } else if (actionType.equals(EnumPosActionType.VERIFY_INDEPENT)) { if (!(couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_MULTIMCH.getCode()) || couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_LIPIN.getCode()) || couponOrderCVo.getCouponType().equals(EnumCouponType.COUPON_PRESS.getCode()) || @@ -650,7 +847,7 @@ public class PosServiceImpl implements PosService { throw new MallinkException(ErrorCode.VERIFY_COUPON_ORDER_MERCHANT_IS_NULL); } - if (actionType.equals(EnumVerifyActionType.CHECK)) { + if (actionType.equals(EnumPosActionType.CHECK)) { if (couponOrderCVo.getCouponOrderStatus().equals(EnumCouponOrderStatus.POS_PRE_VERIFY.getCode())) { logger.info("券已被预核销: " + couponOrderIdStr); if (!couponOrderCVo.getBUserId().equals(buUserId)) { @@ -701,7 +898,7 @@ public class PosServiceImpl implements PosService { retMap.put(WxPayConstant.STATUS, EnumCouponOrderStatus.getEnum(couponOrderCVo.getCouponOrderStatus()).getMessage()); return retMap; } - } else if (actionType.equals(EnumVerifyActionType.PRE_VERIFY)) { + } else if (actionType.equals(EnumPosActionType.PRE_VERIFY)) { if (couponOrderCVo.getCouponOrderStatus().equals(EnumCouponOrderStatus.POS_PRE_VERIFY.getCode())) { logger.info("券已被预核销: " + couponOrderIdStr); if (!couponOrderCVo.getBUserId().equals(buUserId)) { @@ -811,7 +1008,7 @@ public class PosServiceImpl implements PosService { @Override @Transactional(rollbackFor = Exception.class) - public Map couponOrderVerifyCancel(Map params, EnumVerifyActionType actionType) { + public Map couponOrderVerifyCancel(Map params, EnumPosActionType actionType) { Map retMap = new HashMap<>(); String tenantId = params.get(WxPayConstant.TENANT_ID); // 租户ID @@ -833,7 +1030,7 @@ public class PosServiceImpl implements PosService { if (StringUtils.isBlank(couponOrderIdStr)) { errParam(WxPayConstant.COUPON_ORDER_ID); } - if (actionType.equals(EnumVerifyActionType.PRE_VERIFY) || actionType.equals(EnumVerifyActionType.VERIFY_INDEPENT)) { + if (actionType.equals(EnumPosActionType.PRE_VERIFY) || actionType.equals(EnumPosActionType.VERIFY_INDEPENT)) { if (StringUtils.isBlank(posOrderIdStr)) { errParam(WxPayConstant.POS_ORDER_ID); } @@ -896,7 +1093,7 @@ public class PosServiceImpl implements PosService { // 预核销取消 if (couponOrderCVo.getCouponOrderStatus().equals(EnumCouponOrderStatus.POS_PRE_VERIFY.getCode()) && - actionType.equals(EnumVerifyActionType.PRE_VERIFY_CANCEL)) { + actionType.equals(EnumPosActionType.PRE_VERIFY_CANCEL)) { logger.info("券已被预核销: " + couponOrderIdStr); if (!couponOrderCVo.getBUserId().equals(buUserId)) { logger.error(ErrorCode.VERIFY_PRE_BUSER_NOT_EQUAL.getMessage() + ": " + buUserIdStr); @@ -913,7 +1110,7 @@ public class PosServiceImpl implements PosService { } // 核销取消 if (couponOrderCVo.getCouponOrderStatus().equals(EnumCouponOrderStatus.COUPON_ORDER_USED.getCode()) && - actionType.equals(EnumVerifyActionType.VERIFY_CANCEL)) { + actionType.equals(EnumPosActionType.VERIFY_CANCEL)) { logger.info("券已被核销: " + couponOrderIdStr); boolean force = false; if (StringUtils.isNotBlank(forceStr)) { @@ -1040,7 +1237,7 @@ public class PosServiceImpl implements PosService { * @param buUserId * @param posOrderId */ - private void doVerify(EnumVerifyActionType actionType, WxCouponOrderCVo couponOrderCVo, WxCoupon coupon, + private void doVerify(EnumPosActionType actionType, WxCouponOrderCVo couponOrderCVo, WxCoupon coupon, Long merchantId, Long buUserId, Long posOrderId, Map retMap) { int num = 0; // 1. insert posOrderId @@ -1068,13 +1265,13 @@ public class PosServiceImpl implements PosService { couponOrder.setId(couponOrderCVo.getId()); couponOrder.setTenantId(couponOrderCVo.getTenantId()); couponOrder.setCouponOrderStatus(EnumCouponOrderStatus.COUPON_ORDER_USED.getCode()); //1核销 - if (actionType.equals(EnumVerifyActionType.VERIFY_INDEPENT)) { + if (actionType.equals(EnumPosActionType.VERIFY_INDEPENT)) { couponOrder.setVerifyType(EnumCouponVerifyType.VERIFY_POS_INDEPENDENT.getCode()); } else { couponOrder.setVerifyType(EnumCouponVerifyType.VERIFY_POS_PAY.getCode()); } if (couponOrderCVo.getCouponOrderStatus().equals(EnumCouponOrderStatus.POS_PRE_VERIFY.getCode()) && - actionType.equals(EnumVerifyActionType.VERIFY_PAY)) { + actionType.equals(EnumPosActionType.VERIFY_PAY)) { couponOrder.setBUserId(buUserId); } couponOrder.setUpdateDate(new Date()); @@ -1922,10 +2119,6 @@ public class PosServiceImpl implements PosService { logger.error(ErrorCode.VERIFY_BUSER_MERCHANT_NOT_BELONG.getMessage() + ": " + buUserId); throw new MallinkException(ErrorCode.VERIFY_BUSER_MERCHANT_NOT_BELONG); } - if (buUser.getStatus().equals(EnumMerchantStatus.NOT_VALID.getCode())) { - logger.error(ErrorCode.MERCHANT_BUSER_NOT_VALID.getMessage() + ": " + buUserId); - throw new MallinkException(ErrorCode.MERCHANT_BUSER_NOT_VALID); - } return buUser; } @@ -1940,6 +2133,18 @@ public class PosServiceImpl implements PosService { throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), errMessage); } + /** + * 输入参数错误 + * @param param1Str + * @param param2Str + * @throws MallinkException + */ + private void errParam2(String param1Str, String param2Str) throws MallinkException { + String errMessage = String.format("please give one value for %s or %s", param1Str, param2Str); + logger.error(errMessage); + throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), errMessage); + } + /** * POS订单添加 * @return diff --git a/mallinkPosApi/src/test/java/com/iformall/pos/test/PosAppTest.java b/mallinkPosApi/src/test/java/com/iformall/pos/test/PosAppTest.java index 061477f33..57432fe04 100644 --- a/mallinkPosApi/src/test/java/com/iformall/pos/test/PosAppTest.java +++ b/mallinkPosApi/src/test/java/com/iformall/pos/test/PosAppTest.java @@ -176,6 +176,15 @@ public class PosAppTest { doCheckMem(memId, phone, posOrderId, posAmount); } + @Test + public void checkCalcTest() throws Exception { + String memId = "321178010928119808"; + String phone = "13910154397"; + String posAmount = "20000"; + String selCoList = "[\"321877927162249216\"]"; + doCalcMem(memId, phone, posAmount, selCoList); + } + @Test public void checkCouponOrderForIndeepdentVerifyTest() throws Exception { String couponOrderId = "321877927162249216"; @@ -257,10 +266,11 @@ public class PosAppTest { private void doCheckMem(String memId, String phone, String posOrderId, String posAmount) throws Exception { Map reqObj = new HashMap<>(); - reqObj.put(WxPayConstant.NONCE_STR, "1"); + reqObj.put(WxPayConstant.NONCE_STR, "1"); reqObj.put(WxPayConstant.DEV_ID, devId); reqObj.put(WxPayConstant.TENANT_ID, tenantId); reqObj.put(WxPayConstant.MERCHANT_ID, merchantId); + reqObj.put(WxPayConstant.BUSER_ID, buUserId); if (StringUtils.isNotBlank(memId)) { reqObj.put(WxPayConstant.MEM_ID, memId); } @@ -285,6 +295,37 @@ public class PosAppTest { Assert.assertEquals(resSigned, true); } + private void doCalcMem(String memId, String phone, String posAmount, String selCoList) throws Exception { + Map reqObj = new HashMap<>(); + reqObj.put(WxPayConstant.NONCE_STR, "1"); + reqObj.put(WxPayConstant.DEV_ID, devId); + reqObj.put(WxPayConstant.TENANT_ID, tenantId); + reqObj.put(WxPayConstant.MERCHANT_ID, merchantId); + reqObj.put(WxPayConstant.BUSER_ID, buUserId); + if (StringUtils.isNotBlank(memId)) { + reqObj.put(WxPayConstant.MEM_ID, memId); + } + if (StringUtils.isNotBlank(phone)) { + reqObj.put(WxPayConstant.MEM_PHONE, phone); + } + reqObj.put(WxPayConstant.POS_AMOUNT, posAmount); + reqObj.put(WxPayConstant.SELECTED_COUPON_ORDER_LIST, selCoList); + reqObj.put(WxPayConstant.SIGN, WxPayment.createSignHMAC(reqObj, devReqKey)); + String reqJsonStr = JSONObject.toJSONString(reqObj); + MvcResult result = mockMvc.perform( + MockMvcRequestBuilders.post("/calcMem") + .contentType(MediaType.APPLICATION_JSON) + .content(reqJsonStr)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andDo(MockMvcResultHandlers.print()) + .andReturn(); + String responseStr = result.getResponse().getContentAsString(); + ObjectMapper mapper = new ObjectMapper(); + Map respMap = mapper.readValue(responseStr, Map.class); + boolean resSigned = WxPayment.verifyNotifyHMAC(respMap, devResKey); + Assert.assertEquals(resSigned, true); + } + private void doCheckVerify(String couponOrderId, String verifyType, String posOrderId, String posAmount) throws Exception { Map reqObj = new HashMap<>(); reqObj.put(WxPayConstant.NONCE_STR, "1"); diff --git a/mallinkService/src/main/java/com/iformall/enums/EnumVerifyActionType.java b/mallinkService/src/main/java/com/iformall/enums/EnumPosActionType.java similarity index 58% rename from mallinkService/src/main/java/com/iformall/enums/EnumVerifyActionType.java rename to mallinkService/src/main/java/com/iformall/enums/EnumPosActionType.java index 715ca2824..76954a15e 100644 --- a/mallinkService/src/main/java/com/iformall/enums/EnumVerifyActionType.java +++ b/mallinkService/src/main/java/com/iformall/enums/EnumPosActionType.java @@ -3,20 +3,21 @@ package com.iformall.enums; /** * Created by Stormeye on 2018/08/09. */ -public enum EnumVerifyActionType { +public enum EnumPosActionType { // 0: 检查, 1: 预核销 2: 预核销撤销 3:独立核销 4: 交易核销 5:核销撤销 CHECK(0, "检查"), - PRE_VERIFY(1, "预核销"), - PRE_VERIFY_CANCEL(2, "预核销撤销"), - VERIFY_INDEPENT(3,"独立核销"), - VERIFY_PAY(4,"交易核销"), - VERIFY_CANCEL(5,"核销撤销") + CALC(1, "计算"), + PRE_VERIFY(2, "预核销"), + PRE_VERIFY_CANCEL(3, "预核销撤销"), + VERIFY_INDEPENT(4,"独立核销"), + VERIFY_PAY(5,"交易核销"), + VERIFY_CANCEL(6,"核销撤销") ; - public static EnumVerifyActionType getEnum(Integer code) { - for (EnumVerifyActionType value : values()) { + public static EnumPosActionType getEnum(Integer code) { + for (EnumPosActionType value : values()) { if (value.getCode().equals(code)) { return value; } @@ -27,7 +28,7 @@ public enum EnumVerifyActionType { private Integer code; private String message; - EnumVerifyActionType(Integer code, String message) { + EnumPosActionType(Integer code, String message) { this.code = code; this.message = message; } diff --git a/mallinkService/src/main/java/com/iformall/pay/WxPayConstant.java b/mallinkService/src/main/java/com/iformall/pay/WxPayConstant.java index f37a8643d..4909ac38d 100644 --- a/mallinkService/src/main/java/com/iformall/pay/WxPayConstant.java +++ b/mallinkService/src/main/java/com/iformall/pay/WxPayConstant.java @@ -59,7 +59,9 @@ public class WxPayConstant { public final static String MEM_DISCOUNT = "discount"; + public final static String MEM_DISCOUNT_RATE = "discount_rate"; public final static String MEM_COUPON = "coupon"; + public final static String MEM_COUPONS = "coupons"; public final static String MEM_COUPON_DESC = "coupon_desc"; public final static String MEM_CARD = "card"; public final static String MEM_LEVEL = "level"; @@ -78,6 +80,7 @@ public class WxPayConstant { public final static String SALE_FEE = "sale_fee"; public final static String ORG_AMOUNT = "org_amount"; + public final static String DISCOUNT_AMOUNT = "discount_amount"; public final static String ORG_REMAIN_AMOUNT = "org_remain_amount"; public final static String RET = "ret";