Authored by chenchao

use quartz as Scheduler

... ... @@ -2055,6 +2055,10 @@ public class DateUtil {
public static String getOnlyDate(Date date, String fmt){
FastDateFormat fdf = FastDateFormat.getInstance(fmt);
return fdf.format(date);
}
public static String getDatePart(Date date) {
... ... @@ -2172,4 +2176,5 @@ public class DateUtil {
cal.set(Calendar.MILLISECOND, 0);
return (int) (cal.getTimeInMillis() / 1000);
}
}
... ...
... ... @@ -9,7 +9,6 @@ import java.util.List;
public interface BuyerOrderMapper {
int deleteByPrimaryKey(Integer id);
int insert(BuyerOrder record);
... ... @@ -62,4 +61,5 @@ public interface BuyerOrderMapper {
List<BuyerOrder> selectByOrderCodes(@Param("orderCodes") Collection<Long> orderCodes,
@Param("statusList")Collection<Integer> statusList);
}
\ No newline at end of file
... ...
package com.yohoufo.dal.order;
import com.yohoufo.dal.order.model.BuyerOrder;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Created by chao.chen on 2019/2/18.
*/
public interface BuyerOrderViewMapper {
int selectAllCntByAttr(@Param("status") Integer status, @Param("attributes") Integer attr);
List<BuyerOrder> selectAllByAttr(@Param("status") Integer status, @Param("attributes") Integer attr,
@Param("tailId") Integer tailId,@Param("offset") int offset);
}
... ...
... ... @@ -3,6 +3,9 @@ package com.yohoufo.dal.order;
import com.yohoufo.dal.order.model.OrdersPay;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
public interface OrdersPayMapper {
/**
* This method was generated by MyBatis Generator.
... ... @@ -56,4 +59,6 @@ public interface OrdersPayMapper {
int updateByPrimaryKey(OrdersPay record);
int addMoney(OrdersPay record);
List<OrdersPay> selectAllByOrderCodes(@Param("orderCodes") Collection<Long> orderCode);
}
\ No newline at end of file
... ...
... ... @@ -128,10 +128,6 @@
limit 1
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from buyer_order
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.yohoufo.dal.order.model.BuyerOrder" useGeneratedKeys="true">
insert into buyer_order (uid, order_code, seller_uid,
... ...
<?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.yohoufo.dal.order.BuyerOrderViewMapper">
<resultMap id="BaseResultMap" type="com.yohoufo.dal.order.model.BuyerOrder">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="uid" jdbcType="INTEGER" property="uid" />
<result column="order_code" jdbcType="BIGINT" property="orderCode" />
<result column="seller_uid" jdbcType="INTEGER" property="sellerUid" />
<result column="client_type" jdbcType="INTEGER" property="clientType" />
<result column="payment" jdbcType="INTEGER" property="payment" />
<result column="payment_type" jdbcType="TINYINT" property="paymentType" />
<result column="is_cancel" jdbcType="TINYINT" property="isCancel" />
<result column="amount" jdbcType="DECIMAL" property="amount" />
<result column="ship_fee" jdbcType="DECIMAL" property="shipFee" />
<result column="status" jdbcType="INTEGER" property="status" />
<result column="create_time" jdbcType="INTEGER" property="createTime" />
<result column="update_time" jdbcType="INTEGER" property="updateTime" />
<result column="buyer_order_status" jdbcType="INTEGER" property="buyerOrderStatus" />
<result column="seller_order_status" jdbcType="INTEGER" property="sellerOrderStatus" />
<result column="channel_no" jdbcType="VARCHAR" property="channelNo" />
<result column="attributes" jdbcType="INTEGER" property="attributes" />
</resultMap>
<sql id="Base_Column_List">
id, uid, order_code, seller_uid, client_type, payment, payment_type, is_cancel, amount,
ship_fee, status, create_time, update_time, buyer_order_status, seller_order_status, channel_no, attributes
</sql>
<select id="selectAllByAttr" resultMap="BaseResultMap">
select <include refid="Base_Column_List" /> from buyer_order
where status = #{status,jdbcType=INTEGER}
and attributes = #{attributes,jdbcType=INTEGER}
<if test="tailId !=null"> and id &gt; #{tailId,jdbcType=INTEGER}</if>
order by id limit {offset}
</select>
<select id="selectCntByUid" resultType="java.lang.Integer">
select count(*) from buyer_order
where status = #{status,jdbcType=INTEGER}
and attributes = #{attributes,jdbcType=INTEGER}
</select>
</mapper>
\ No newline at end of file
... ...
... ... @@ -193,4 +193,14 @@
update orders_pay set amount = amount + #{amount,jdbcType=DECIMAL} where order_code = #{orderCode,jdbcType=BIGINT}
and uid = #{uid,jdbcType=INTEGER} and amount + #{amount,jdbcType=DECIMAL} &gt;= 0
</update>
<select id="selectAllByOrderCodes" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from orders_pay
where order_code IN
<foreach collection="orderCodes" item="orderCode" separator="," open="(" close=")">
#{orderCode,jdbcType=BIGINT}
</foreach>
</select>
</mapper>
\ No newline at end of file
... ...
... ... @@ -85,5 +85,9 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.yoho.quartz</groupId>
<artifactId>yoho-quartz-client</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
... ...
... ... @@ -62,7 +62,7 @@ public class NotDeliverNoticeDelayMsgConsumer implements YhConsumer {
sog.setProductName(msg.getPrdName());
}
LOGGER.info("ready notice sellerDeliverNotice, msg {}", msg);
inBoxFacade.sellerDeliverNotice(sog,orderCode, 2);
inBoxFacade.sellerDeliverNotice(sog,orderCode, 2, 12);
}
... ...
... ... @@ -62,9 +62,9 @@ public class CacheKeyBuilder {
SELLER_BASE_FUNC_CONFIG("ufo:order:seller:base:func:Config", ""),
SELLER_LEVEL_FUNC_CONFIG("ufo:order:seller:stageLevel:func:Config", "");
SELLER_LEVEL_FUNC_CONFIG("ufo:order:seller:stageLevel:func:Config", ""),
ORDER_DELIVER_NOTICE_TIMES("ufo:order:deliver:","orderCode:{}:date:{}:times");
private String fix;
... ... @@ -152,4 +152,8 @@ public class CacheKeyBuilder {
public static RedisKeyBuilder sellerFuncLevelConfigKey(){
return KeyTemp.SELLER_LEVEL_FUNC_CONFIG.builderKeyOnlyFixed();
}
public static RedisKeyBuilder orderDeliverNoticeTimesKey(long orderCode, String date){
return KeyTemp.ORDER_DELIVER_NOTICE_TIMES.builderKey(new Object[]{orderCode, date});
}
}
... ...
... ... @@ -155,7 +155,7 @@ public class OrderCacheService {
public OrderDetailInfo getOrderDetailInfo(int uid, long orderCode, TabType actor){
logger.info("hit OrderDetail cache ,uid {} orderCode {} actor {}", uid, orderCode, actor);
logger.info("try fetch OrderDetail from cache ,uid {} orderCode {} actor {}", uid, orderCode, actor);
RedisKeyBuilder kb = CacheKeyBuilder.orderDetailKey(uid, actor.getValue(), orderCode);
OrderDetailInfo orderDetailInfo = cacheClient.get(kb.getKey(), OrderDetailInfo.class);
logger.info("hit OrderDetail cache ,uid {} orderCode {} actor {} orderDetailInfo {}",
... ... @@ -163,4 +163,18 @@ public class OrderCacheService {
return orderDetailInfo;
}
public void cacheOrderDeliverNoticeTimes(long orderCode, String date, int times, long timeOut){
RedisKeyBuilder kb = CacheKeyBuilder.orderDeliverNoticeTimesKey(orderCode, date);
cacheClient.setEx(kb, times, timeOut);
}
public Integer getOrderDeliverNoticeTimes(long orderCode, String date){
RedisKeyBuilder kb = CacheKeyBuilder.orderDeliverNoticeTimesKey(orderCode, date);
Integer times = cacheClient.get(kb, Integer.class);
logger.info("hit cache key {} value {}", kb.getKey(), times);
return times;
}
}
... ...
... ... @@ -167,7 +167,7 @@ public class BuyerOrderCancelService {
// 退买家货款
.withRefundGoodsMoney(payRefundService::refund).refundCase(RefundCase.BUYER_GOODS_MONEY).and()
// 通知卖家商品发货超时
.withNoticeSeller((soa, code) -> inBoxFacade.sellerDeliverNotice(soa, code, 3)).and()
.withNoticeSeller((soa, code) -> inBoxFacade.sellerDeliverNotice(soa, code, 3, 0)).and()
// 通知买家卖家商品发货超时
.withNoticeBuyer(inBoxFacade::noticeBuyerOfSellerSendOutTimeout).and()
// 退优惠券
... ...
... ... @@ -985,27 +985,31 @@ public class InBoxFacade {
}
}
public void sellerDeliverNotice(SellerOrderGoods sog,long orderCode, int times) {
public void sellerDeliverNotice(SellerOrderGoods sog, long orderCode, int times, int leftTime) {
Integer sellerUid = sog.getUid();
String prdName = sog.getProductName();
String sizeName = sog.getSizeName();
InboxBusinessTypeEnum ibt;
InboxBusinessTypeEnum smsInboxBusinessTypeEnum;
if (times == 2) {
ibt = InboxBusinessTypeEnum.NOTICE_SELLER_DELIVER_GOODS;
smsInboxBusinessTypeEnum = InboxBusinessTypeEnum.SMS_NOTIFIED_SEND_SECOND;
} else if (times == 3) {
ibt = InboxBusinessTypeEnum.NOTICE_SELLER_DELIVER_GOODS_FAIL;
smsInboxBusinessTypeEnum = InboxBusinessTypeEnum.SMS_NOTIFIED_SEND_FAILED;
} else {
return;
}
String params ;
String content;
try {
if (times == 2) {
params = buildParams(prdName, sizeName, leftTime);
ibt = InboxBusinessTypeEnum.NOTICE_SELLER_DELIVER_GOODS;
smsInboxBusinessTypeEnum = InboxBusinessTypeEnum.SMS_NOTIFIED_SEND_SECOND;
content = getReplacedContent(smsInboxBusinessTypeEnum.getContent(),prdName,orderCode, leftTime);
} else if (times == 3) {
params = buildParams(prdName, sizeName);
ibt = InboxBusinessTypeEnum.NOTICE_SELLER_DELIVER_GOODS_FAIL;
smsInboxBusinessTypeEnum = InboxBusinessTypeEnum.SMS_NOTIFIED_SEND_FAILED;
content = getReplacedContent(smsInboxBusinessTypeEnum.getContent(),prdName,orderCode);
} else {
return;
}
executorService.execute(() -> {
logger.info("record sellerDeliverNotice inbox sms msg,sellerUid {}, prdName {}, times {}",
sellerUid, prdName, times);
String params = buildParams(prdName, sizeName);
InboxReqVO req = buildInboxReqVO(sellerUid, params, ibt);
InBoxResponse resp = inBoxSDK.addInbox(req);
logger.info("record sellerDeliverNotice inbox msg,sellerUid {}, prdName {},sizeName {} times {},resp {}",
... ... @@ -1018,7 +1022,7 @@ public class InBoxFacade {
return;
}
List<String> mobileList = Arrays.asList(phone);
String content = getReplacedContent(smsInboxBusinessTypeEnum.getContent(),prdName,orderCode);
sendSmsService.smsSendByMobile(content, mobileList);
logger.info("record sellerDeliverNotice inbox sms msg,sellerUid {}, prdName {}, times {},resp {}",
sellerUid, prdName, times);
... ...
package com.yohoufo.order.service.quartz;
import com.yoho.quartz.annotation.JobType;
import com.yoho.quartz.annotation.MisfiredPolicy;
import com.yoho.quartz.annotation.YhJobDef;
import com.yoho.quartz.domain.JobProcessResult;
import com.yoho.quartz.job.YhJob;
import com.yohobuy.ufo.model.order.common.OrderAttributes;
import com.yohobuy.ufo.model.order.common.OrderStatus;
import com.yohobuy.ufo.model.order.constants.ConfirmDesc;
import com.yohoufo.common.utils.DateUtil;
import com.yohoufo.common.utils.PageHelper;
import com.yohoufo.dal.order.BuyerOrderGoodsMapper;
import com.yohoufo.dal.order.BuyerOrderViewMapper;
import com.yohoufo.dal.order.OrdersPayMapper;
import com.yohoufo.dal.order.SellerOrderGoodsMapper;
import com.yohoufo.dal.order.model.BuyerOrder;
import com.yohoufo.dal.order.model.BuyerOrderGoods;
import com.yohoufo.dal.order.model.OrdersPay;
import com.yohoufo.dal.order.model.SellerOrderGoods;
import com.yohoufo.order.model.dto.DTNode;
import com.yohoufo.order.service.cache.OrderCacheService;
import com.yohoufo.order.service.impl.BuyerOrderCancelService;
import com.yohoufo.order.service.proxy.InBoxFacade;
import com.yohoufo.order.utils.LoggerUtils;
import lombok.experimental.Builder;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Created by chao.chen on 2019/2/18.
*/
@Service
@YhJobDef(jobName = "PreSaleOrderNoticeScheduler", desc = "卖家的预售商品是否发货处理", cron = "0 0/5 * * * ?",
misfiredPolicy = MisfiredPolicy.SMART_POLICY, jobType = JobType.CRON, jobGroup = "ufo-gateway")
public class PreSaleOrderJob implements YhJob {
private final Logger logger = LoggerUtils.getBuyerOrderLogger();
@Autowired
private BuyerOrderViewMapper buyerOrderViewMapper;
@Autowired
private OrdersPayMapper ordersPayMapper;
@Autowired
private BuyerOrderCancelService buyerOrderCancelService;
@Autowired
private OrderCacheService orderCacheService;
@Autowired
private SellerOrderGoodsMapper sellerOrderGoodsMapper;
@Autowired
private BuyerOrderGoodsMapper buyerOrderGoodsMapper;
@Autowired
private InBoxFacade inBoxFacade;
private static final long CACHE_EXPIRED_TIME = 24*3600L;
@Override
public JobProcessResult process(String s) {
logger.info("in PreSaleOrderJob.process");
final OrderStatus tos = OrderStatus.HAS_PAYED;
Integer status = tos.getCode();
final OrderAttributes toa = OrderAttributes.ADVANCE_SALE;
int oa = toa.getCode();
int cnt = buyerOrderViewMapper.selectAllCntByAttr(status, oa);
if (cnt==0){
return null;
}
final int pageSize = 10;
int pageTotal = PageHelper.getPageTotal(cnt, pageSize);
Integer lastId = null;
List<BuyerOrder> bos;
//TODO 第35天 自动超时取消
// 查询条件 预售 + 支付完成(待卖家发货)+ 支付时间到当前时间的差值在(0,35] 单位:天
//TODO 第31天开始 到34天,通知剩余发货时间
List<BuyerOrder> over35DaysBOs = new ArrayList<>(cnt);
List<BuyerOrderWrapper> from30To34BOs = new ArrayList<>(cnt);
final int currentSencond = DateUtil.getCurrentTimeSecond();
for(int i=0; i<pageTotal; i++){
bos = buyerOrderViewMapper.selectAllByAttr(status, oa, lastId, pageSize);
if (Objects.nonNull(bos)){
Node node = processSub(bos, currentSencond);
over35DaysBOs.addAll(node.over35DaysBOs);
from30To34BOs.addAll(node.from30To34BOs);
lastId = node.lastId;
}
}
for(BuyerOrder needCancelBO : over35DaysBOs){
//TODO 调用自动取消
logger.info("in PreSaleOrderJob cancelForSellerSendOutTimeout BuyerOrder {}", needCancelBO);
buyerOrderCancelService.cancelForSellerSendOutTimeout(needCancelBO.getUid(), needCancelBO.getOrderCode(), 0, null);
}
final String nowDateStr = DateUtil.getOnlyDate(new Date(), DateUtil.yyyyMMdd);
for(BuyerOrderWrapper needNoticBO : from30To34BOs){
BuyerOrder bo = needNoticBO.buyerOrder;
//TODO 查Redis 是否存在,没有->计算剩余时间 发消息, 存Redis;
logger.info("in PreSaleOrderJob notice Seller deliver date is closed dead time, BuyerOrder {}", bo);
Long orderCode = bo.getOrderCode() ;
Integer times = orderCacheService.getOrderDeliverNoticeTimes(orderCode, nowDateStr);
if (times==null){
//todo 发送通知
int leftTime = 35*3600 - needNoticBO.diffSeconds;
DTNode node = DTNode.calBySeconds(leftTime);
//todo 35天走配置
int leftHours = node.days * 24 + node.hours + node.minutes/30;
BuyerOrderGoods bog = buyerOrderGoodsMapper.selectOnlyByOrderCode(orderCode);
SellerOrderGoods sog = sellerOrderGoodsMapper.selectByPrimaryKey(bog.getSkup());
inBoxFacade.sellerDeliverNotice(sog, orderCode, 2, leftHours);
times = 1;
orderCacheService.cacheOrderDeliverNoticeTimes(orderCode, nowDateStr, times, CACHE_EXPIRED_TIME);
}else{
logger.info("has send deliver notice , orderCode {} nowDateStr {}", orderCode, nowDateStr);
}
}
return null;
}
static final int secondsOf35Days = secondsOfDays(35), secondsOf31Days = secondsOfDays(31);
Node processSub(List<BuyerOrder> bos, int currentSencond){
BuyerOrder lastBO = null;
Node node = new Node(bos.size());
List<BuyerOrder> over35DaysBOs = node.over35DaysBOs;
List<BuyerOrderWrapper> from30To34BOs = node.from30To34BOs;
if (Objects.nonNull(lastBO=bos.get(bos.size()-1))){
node.lastId = lastBO.getId();
}
Map<Long, BuyerOrder> orderCodes2BoMap = bos.parallelStream()
.collect(Collectors.toMap(BuyerOrder::getOrderCode, Function.identity()));
List<OrdersPay> ops = ordersPayMapper.selectAllByOrderCodes(orderCodes2BoMap.keySet());
for(OrdersPay op : ops){
Integer payTime = op.getCreateTime();
int diff = currentSencond-payTime;
//超过35天
if (diff>=secondsOf35Days){
over35DaysBOs.add(orderCodes2BoMap.get(op.getOrderCode()));
}else {
//超过30天(第31天),不到35天
if (diff>=secondsOf31Days) {
BuyerOrder buyerOrder = orderCodes2BoMap.get(op.getOrderCode());
BuyerOrderWrapper bow = BuyerOrderWrapper.builder().buyerOrder(buyerOrder)
.diffSeconds(diff).build();
from30To34BOs.add(bow);
}
}
}
return node;
}
@Builder
private static class BuyerOrderWrapper{
BuyerOrder buyerOrder;
int diffSeconds;
}
private static class Node{
Integer lastId;
List<BuyerOrder> over35DaysBOs ;
List<BuyerOrderWrapper> from30To34BOs ;
Node(int cnt){
over35DaysBOs = new ArrayList<>(cnt);
from30To34BOs = new ArrayList<>(cnt);
}
}
private static int secondsOfDays(int days){
return days * 24 * 3600;
}
}
... ...
... ... @@ -128,6 +128,12 @@
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.yoho.quartz</groupId>
<artifactId>yoho-quartz-client</artifactId>
<version>1.3.4-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
... ...
... ... @@ -84,6 +84,7 @@ datasources:
- com.yohoufo.dal.order.SellerFuncMapper
- com.yohoufo.dal.order.SellerLevelFuncMapper
- com.yohoufo.dal.order.OrderOverTimeMapper
- com.yohoufo.dal.order.BuyerOrderViewMapper
ufo_promotion:
servers:
... ...
... ... @@ -83,6 +83,7 @@ datasources:
- com.yohoufo.dal.order.SellerFuncMapper
- com.yohoufo.dal.order.SellerLevelFuncMapper
- com.yohoufo.dal.order.OrderOverTimeMapper
- com.yohoufo.dal.order.BuyerOrderViewMapper
ufo_promotion:
servers:
... ...