Authored by wujiexiang

优惠券结算

... ... @@ -8,10 +8,10 @@
<result column="skup" jdbcType="INTEGER" property="skup" />
<result column="goods_price" jdbcType="DECIMAL" property="goodsPrice" />
<result column="goods_amount" jdbcType="DECIMAL" property="goodsAmount" />
<result column="coupons_cut_amount" jdbcType="DECIMAL" property="couponCutAmount" />
<result column="coupon_cut_amount" jdbcType="DECIMAL" property="couponCutAmount" />
</resultMap>
<sql id="Base_Column_List">
id, order_code, skup, goods_price, goods_amount, uid
id, order_code, skup, goods_price, goods_amount, uid,coupon_cut_amount
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
... ... @@ -85,7 +85,7 @@
uid,
</if>
<if test="couponCutAmount != null">
coupons_cut_amount,
coupon_cut_amount,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
... ...
... ... @@ -127,7 +127,7 @@ public class ChargeService {
chargeResult.setFinalAmount(newFinalAmount);
CouponPayResult couponPayResult = CouponPayResult.builder().couponCode(userCouponsBo.getCouponCode())
.couponAmount(couponPayAmount).couponTitle(userCouponsBo.getCouponName()).build();
.couponAmount(couponPayAmount).couponTitle(userCouponsBo.getCouponName()).couponCount(1).build();
chargeResult.setCouponPayResult(couponPayResult);
... ... @@ -139,7 +139,7 @@ public class ChargeService {
private UserCouponsBo getOneUsableUserCouponBo(ChargeContext chargeContext) {
ChargeParam chargeParam = chargeContext.getChargeParam();
List<UserCouponsBo> couponsBos = couponProxyService.checkUseCouponsAndGet(chargeParam.getUid(), chargeParam.getCouponCodes());
List<UserCouponsBo> couponsBos = couponProxyService.checkAndGetCoupons(chargeParam.getUid(), chargeParam.getCouponCodes());
if (CollectionUtils.isEmpty(couponsBos)) {
logger.info("[{}] not find any coupons by couponCodes:{}", chargeParam.getUid(), chargeParam.getCouponCodes());
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_VAILD);
... ... @@ -150,6 +150,7 @@ public class ChargeService {
//找到可用的券
List<UserCouponsBo> usableCoupons = couponMatchResults.stream().filter(result -> result.isUsable()).map(result -> result.getUserCouponsBo()).collect(Collectors.toList());
//当前只有使用一张券
if (CollectionUtils.isEmpty(usableCoupons) || usableCoupons.size() != 1) {
logger.info("[{}] get coupons by couponCodes:{} is invalid", chargeParam.getUid(), chargeParam.getCouponCodes());
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_VAILD);
... ...
... ... @@ -3,6 +3,7 @@ package com.yohoufo.order.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.yoho.core.common.utils.DateUtil;
import com.yoho.core.dal.datasource.annotation.Database;
import com.yoho.core.transaction.YHTxCoordinator;
import com.yoho.core.transaction.annoation.YHTransaction;
import com.yoho.error.ServiceError;
import com.yoho.error.exception.ServiceException;
... ... @@ -39,6 +40,9 @@ public class SubmitOrderServiceImpl implements ISubmitOrderService {
@Autowired
private CouponProxyService couponProxyService;
@Autowired
YHTxCoordinator tx;
@YHTransaction
public BuyerOrderSubmitResult doSumbitOrder(OrderBuilder orderBuilder){
... ... @@ -56,6 +60,8 @@ public class SubmitOrderServiceImpl implements ISubmitOrderService {
}
}catch (Exception ex){
tx.rollback();
try{
boolean returnStorageFlag = productProxyService.returnStorage(orderBuilder.getSkup());
... ...
... ... @@ -9,16 +9,19 @@ import com.yoho.core.transaction.annoation.TxCompensateArgs;
import com.yoho.error.ServiceError;
import com.yoho.error.exception.ServiceException;
import com.yohobuy.ufo.model.promotion.UserCouponsBo;
import com.yohobuy.ufo.model.promotion.UserCouponsListBo;
import com.yohoufo.common.ApiResponse;
import com.yohoufo.common.caller.UfoServiceCaller;
import com.yohoufo.common.utils.DateUtil;
import com.yohoufo.order.model.bo.CouponBo;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Created by jiexiang.wu on 2018/11/19.
... ... @@ -38,40 +41,27 @@ public class CouponProxyService implements Compensator {
* @return
*/
public List<UserCouponsBo> getUserAvailableCoupons(int uid) {
UserCouponsBo userCouponsBo = new UserCouponsBo();
userCouponsBo.setCouponCode("a100");
userCouponsBo.setCouponAmount(BigDecimal.valueOf(20));
userCouponsBo.setUseLimitType("2");
userCouponsBo.setUseLimitValue("299");
userCouponsBo.setProductLimitType("1");
userCouponsBo.setProductIdInclude(Lists.newArrayList(88888900));
userCouponsBo.setUseRule("满299可用");
userCouponsBo.setStartTime(1514739661);
userCouponsBo.setEndTime(1546275661);
UserCouponsBo userCouponsBo1 = new UserCouponsBo();
userCouponsBo1.setCouponCode("a200");
userCouponsBo1.setCouponAmount(BigDecimal.valueOf(100));
userCouponsBo1.setUseLimitType("0");
userCouponsBo1.setUseLimitValue("");
userCouponsBo1.setProductLimitType("1");
userCouponsBo1.setProductIdInclude(Lists.newArrayList(88888900));
userCouponsBo1.setStartTime(1514739661);
userCouponsBo1.setEndTime(1546275661);
return Lists.newArrayList(userCouponsBo, userCouponsBo1);
logger.info("[{}] getUserAvailableCoupons", uid);
//未使用的优惠券,包括还未生效的券
ApiResponse resp = serviceCaller.call("app.coupons.listNoUsed", uid);
UserCouponsListBo result = getResultFromApiResponse(resp);
if (Objects.isNull(result) || CollectionUtils.isEmpty(result.getCoupons())) {
return Lists.newArrayList();
}
//过滤未生效的券
int currentTime = DateUtil.getCurrentTimeSecond();
return result.getCoupons().stream().filter(e -> e.getStartTime() < currentTime && e.getEndTime() > currentTime).collect(Collectors.toList());
}
public List<UserCouponsBo> checkUseCouponsAndGet(int uid, List<String> couponCodes) {
UserCouponsBo userCouponsBo = new UserCouponsBo();
userCouponsBo.setCouponCode("a100");
userCouponsBo.setCouponAmount(BigDecimal.valueOf(20));
userCouponsBo.setUseLimitType("2");
userCouponsBo.setUseLimitValue("299");
userCouponsBo.setProductLimitType("1");
userCouponsBo.setProductIdInclude(Lists.newArrayList(88888900));
userCouponsBo.setStartTime(1514739661);
userCouponsBo.setEndTime(1546275661);
return Lists.newArrayList(userCouponsBo);
public List<UserCouponsBo> checkAndGetCoupons(int uid, List<String> couponCodes) {
logger.info("[{}] checkAndGetCoupons:{}", uid, couponCodes);
//未使用的优惠券,包括还未生效的券
ApiResponse resp = serviceCaller.call("app.coupons.checkAndGet", uid, couponCodes);
UserCouponsListBo result = getResultFromApiResponse(resp);
if (Objects.isNull(result) || CollectionUtils.isEmpty(result.getCoupons())) {
return Lists.newArrayList();
}
return result.getCoupons();
}
... ... @@ -87,7 +77,8 @@ public class CouponProxyService implements Compensator {
@TxCompensateArgs("couponBo") CouponBo coupon) {
logger.info("[{}] request to use coupon,couponBo is {}", orderCode, coupon);
ApiResponse resp = serviceCaller.call("app.coupons.use", uid, orderCode, Lists.newArrayList(coupon.getCouponCode()));
if (getResultFromApiResponse(resp) == false) {
Boolean result = getResultFromApiResponse(resp);
if (result == null || result.booleanValue() == false) {
logger.warn("[{}] use couponBo fail,coupons is {}", orderCode, coupon);
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_VAILD);
}
... ... @@ -105,21 +96,22 @@ public class CouponProxyService implements Compensator {
public void orderCancelCoupon(int uid, long orderCode, CouponBo coupon) {
logger.info("[{}] request to cancel coupon,couponBo is {}", orderCode, coupon);
ApiResponse resp = serviceCaller.call("app.coupons.cancel", uid, orderCode, Lists.newArrayList(coupon.getCouponCode()));
if (getResultFromApiResponse(resp) == false) {
Boolean result = getResultFromApiResponse(resp);
if (result == null || result.booleanValue() == false) {
logger.warn("[{}] cancel couponBo fail,coupons is {}", orderCode, coupon);
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_VAILD);
}
logger.info("[{}] cancel coupon success", orderCode);
}
private boolean getResultFromApiResponse(ApiResponse resp) {
private <T> T getResultFromApiResponse(ApiResponse resp) {
if (resp == null) {
return false;
return null;
}
if (resp.getCode() != 200 || resp.getData() == null) {
return false;
return null;
}
return (boolean) resp.getData();
return (T) resp.getData();
}
/**
... ... @@ -137,7 +129,7 @@ public class CouponProxyService implements Compensator {
JSONObject json = JSON.parseObject(message);
uid = json.getIntValue("uid");
orderCode = json.getLongValue("orderCode");
couponBo = json.getObject("coupons", CouponBo.class);
couponBo = json.getObject("couponBo", CouponBo.class);
} catch (Exception ex) {
logger.warn("parse message to json error,message is {}", message, ex);
}
... ...
... ... @@ -3,12 +3,12 @@ package com.yohoufo.order.service.support;
import com.google.common.collect.Lists;
import com.yohobuy.ufo.model.order.constants.OrderConstant;
import com.yohobuy.ufo.model.promotion.UserCouponsBo;
import com.yohoufo.common.utils.DateUtil;
import com.yohoufo.order.charge.model.ChargeResult;
import com.yohoufo.order.charge.model.CouponMatchResult;
import com.yohoufo.order.charge.model.CouponPayResult;
import com.yohoufo.order.model.response.CouponInfo;
import com.yohoufo.order.model.response.ShoppingCoupon;
import com.yohoufo.order.utils.DateUtil;
import com.yohoufo.order.utils.MathUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;
... ... @@ -39,8 +39,8 @@ public class CouponSupport {
shoppingCoupon.setCouponCode(userCouponsBo.getCouponCode());
shoppingCoupon.setCouponName(userCouponsBo.getCouponName());
shoppingCoupon.setCouponValueStr(userCouponsBo.getCouponAmount().intValue() + "");
shoppingCoupon.setCouponValidity(DateUtil.format(DateUtil.getDateFromLong(userCouponsBo.getStartTime()), "yyyy.MM.dd") + "-" +
DateUtil.format(DateUtil.getDateFromLong(userCouponsBo.getEndTime()), "yyyy.MM.dd"));
shoppingCoupon.setCouponValidity(DateUtil.getDateFormat(userCouponsBo.getStartTime()) + "-" +
DateUtil.getDateFormat(userCouponsBo.getEndTime()));
shoppingCoupon.setUseRuleStr(userCouponsBo.getUseRule());
shoppingCoupon.setSelected(OrderConstant.N_STR);
... ...
package com.yohoufo.order.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by jiexiang.wu on 2018/11/20.
*/
public class DateUtil {
public static final String yyyy_MM_dd_HH_mm_SS = "yyyy-MM-dd HH:mm:ss";
public static final String yyyy$MM$dd = "yyyy.MM.dd";
/**
* 将Date类型转换为字符串
*
* @param date 日期类型
* @param pattern 字符串格式
* @return 日期字符串
*/
public static String format(Date date, String pattern) {
if (date == null) {
return "null";
}
if (pattern == null || pattern.equals("") || pattern.equals("null")) {
pattern = yyyy$MM$dd;
}
return new SimpleDateFormat(pattern).format(date);
}
/**
* @param time
* @return java.lang.String
*/
public static Date getDateFromLong(long time) {
Long timestamp = time*1000;
String date = new java.text.SimpleDateFormat(yyyy_MM_dd_HH_mm_SS).format(new java.util.Date(timestamp));
return format(date);
}
/**
* 将字符串转换为Date类型
*
* @param date 字符串类型
* @return 日期类型
*/
public static Date format(String date) {
return format(date, null);
}
/**
* 将字符串转换为Date类型
*
* @param date 字符串类型
* @param pattern 格式
* @return 日期类型
*/
public static Date format(String date, String pattern) {
if (pattern == null || pattern.equals("") || pattern.equals("null")) {
pattern = yyyy_MM_dd_HH_mm_SS;
}
if (date == null || date.equals("") || date.equals("null")) {
return new Date();
}
Date d = null;
try {
d = new SimpleDateFormat(pattern).parse(date);
} catch (ParseException pe) {
}
return d;
}
}
package com.yohoufo.promotion.controller;
import com.yoho.error.ServiceError;
import com.yoho.error.exception.ServiceException;
import com.yohobuy.ufo.model.promotion.UserCouponsListBo;
import com.yohoufo.common.ApiResponse;
import com.yohoufo.promotion.service.ICouponService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -72,7 +75,7 @@ public class OrderCouponController {
* @param uid
* @return
*/
@RequestMapping(params = "method=app.coupons.list")
@RequestMapping(params = "method=app.coupons.listNoUsed")
@ResponseBody
public ApiResponse queryUserNoUsedCoupons(@RequestParam(value = "uid") Integer uid) {
... ... @@ -83,4 +86,23 @@ public class OrderCouponController {
}
/**
* 优惠券列表(订单使用)
*
* @param uid
* @return
*/
@RequestMapping(params = "method=app.coupons.checkAndGet")
@ResponseBody
public ApiResponse checkAndGetCoupons(@RequestParam(value = "uid") int uid,
@RequestParam(value = "couponCodes") List<String> couponCodes) {
logger.info("checkAndGet coupons, uid: {},couponCodes:{}", uid, couponCodes);
if (uid <= 0 || CollectionUtils.isEmpty(couponCodes)) {
throw new ServiceException(ServiceError.PROMOTION_REQUEST_PAREMENT_ERROR);
}
UserCouponsListBo couponBoList = couponService.checkAndGetCoupons(uid, couponCodes);
logger.info("checkAndGet coupons, uid: {},couponCodes:{},success", uid, couponCodes);
return new ApiResponse.ApiResponseBuilder().code(200).data(couponBoList).build();
}
}
... ...
... ... @@ -47,5 +47,13 @@ public interface ICouponService {
*/
public UserCouponsListBo queryUserNoUsedCoupons(Integer uid);
/**
* 校验券并返回券信息
*
* @param uid
* @param couponCodes
* @return
*/
UserCouponsListBo checkAndGetCoupons(int uid, List<String> couponCodes);
}
... ...
... ... @@ -224,9 +224,9 @@ public class CouponServiceImpl implements ICouponService {
List<String> couponIdList = list.stream().map(UserCoupon::getCouponToken).collect(Collectors.toList());
List<Coupon> couponList = couponCacheService.getCouponsWithCache(couponIdList);
Map<Integer, Coupon> couponMap = couponList.stream().filter(coupon -> {
if (coupon.getStatus()!=null && coupon.getStatus().intValue() == CouponsStatusEnum.VALID.getCode()){
if (coupon.getStatus() != null && coupon.getStatus().intValue() == CouponsStatusEnum.VALID.getCode()) {
return true;
}else{
} else {
return false;
}
}).collect(Collectors.toMap(Coupon::getId, Function.identity()));
... ... @@ -234,7 +234,9 @@ public class CouponServiceImpl implements ICouponService {
// 优惠券商品限制 key=couponId, value=ProductId集合
Map<Integer, List<Integer>> couponProductIdMap = getProductIdListMap(couponList);
List<UserCouponsBo> couponBoList = list.stream().map(userCoupon -> {
List<UserCouponsBo> couponBoList = list.stream().filter(userCoupon -> {
return couponMap.containsKey(userCoupon.getCouponId());
}).map(userCoupon -> {
Coupon coupon = couponMap.get(userCoupon.getCouponId());
List<Integer> productIds = couponProductIdMap.get(userCoupon.getCouponId());
... ... @@ -291,12 +293,14 @@ public class CouponServiceImpl implements ICouponService {
}
}).collect(Collectors.toMap(Coupon::getId, Function.identity()));
List<CouponInfo> couponInfoList = list.stream().map(userCoupon -> {
List<CouponInfo> couponInfoList = list.stream().filter(userCoupon -> {
return couponMap.containsKey(userCoupon.getCouponId());
}).map(userCoupon -> {
Coupon coupon = couponMap.get(userCoupon.getCouponId());
// 单个获取couponType
CouponType couponType = couponCacheService.getCouponTypeWithCache(userCoupon.getCouponType()!=null ? userCoupon.getCouponType().intValue() : null);
CouponType couponType = couponCacheService.getCouponTypeWithCache(userCoupon.getCouponType() != null ? userCoupon.getCouponType().intValue() : null);
return CouponConvert.convertCouponInfo(userCoupon, coupon, couponType);
... ... @@ -314,5 +318,62 @@ public class CouponServiceImpl implements ICouponService {
int cnt = userCouponMapper.selectCntUsableCouponByUid(uid, DateUtil.getCurrentTimeSecond());
return cnt;
}
@Override
public UserCouponsListBo checkAndGetCoupons(int uid, List<String> couponCodes) {
//数据校验
if (uid <= 0 || CollectionUtils.isEmpty(couponCodes)) {
logger.warn("check coupon use param error:{}, {}", uid, couponCodes);
throw new ServiceException(ServiceError.PROMOTION_PARAM_IS_ERROR);
}
// 获取用户券记录
List<UserCoupon> userCoupons = userCouponMapper.selectByUidAndCouponCodes(uid, couponCodes);
int time = DateUtil.getCurrentTimeSecond();
List<UserCouponsBo> couponBoList = couponCodes.stream().map(couponCode -> {
UserCoupon userCoupon = userCoupons.stream().filter(e -> couponCode.equals(e.getCouponCode())).findAny().orElse(null);
logger.info("user coupon {},{},{}", uid, couponCode, userCoupon);
if (userCoupon == null) {
logger.warn("not find user coupon by{},{}", uid, couponCode);
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_YOUS);
}
//判断券状态
if (userCoupon.getStatus().intValue() != CouponUseStatusEnum.NOT_USED.getCode()) {
logger.warn("user this coupon is used:{},{}", userCoupon.getUid(), userCoupon.getCouponCode());
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_USE_DOUBLE);
}
//判断生失效时间
if (userCoupon.getStartTime() > time || userCoupon.getEndTime() < time) {
logger.warn("coupon has expire or not arrive time:{},{},{}", userCoupon.getStartTime(), userCoupon.getEndTime(), time);
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_ALLOW_OR_EXPIRE);
}
//券模板的状态
Coupon coupon = couponCacheService.getCouponWithCache(userCoupon.getCouponToken());
if (coupon == null) {
logger.warn("coupons is null");
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_EXISTS);
}
if (coupon.getStatus() != CouponsStatusEnum.VALID.getCode()) {
logger.warn("coupon status can't be use:{},{}", coupon.getId(), coupon.getStatus());
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_VAILD);
}
//所有的校验都通过了,生成bo
Map<Integer, List<Integer>> couponProductIdMap = getProductIdListMap(Lists.newArrayList(coupon));
List<Integer> productIds = couponProductIdMap.get(userCoupon.getCouponId());
UserCouponsBo couponBo = CouponConvert.covertCouponBo(userCoupon, coupon);
couponBo.setProductIdInclude(productIds);
return couponBo;
}).collect(Collectors.toList());
return UserCouponsListBo.builder().coupons(couponBoList).build();
}
}
... ...