delayQueue = new DelayQueue<>();
+
+ @PostConstruct
+ private void init() {
+
+ Executors.newSingleThreadExecutor().execute(() -> {
+ while (true) {
+ try {
+ Task task = delayQueue.take();
+ task.run();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ public void addTask(Task task) {
+ if (delayQueue.contains(task)) {
+ return;
+ }
+ delayQueue.add(task);
+ }
+
+ public void removeTask(Task task) {
+ delayQueue.remove(task);
+ }
+
+}
diff --git a/waynboot-common/src/main/java/com/wayn/common/util/mail/MailUtil.java b/waynboot-common/src/main/java/com/wayn/common/util/mail/MailUtil.java
new file mode 100644
index 0000000..1be99bc
--- /dev/null
+++ b/waynboot-common/src/main/java/com/wayn/common/util/mail/MailUtil.java
@@ -0,0 +1,67 @@
+package com.wayn.common.util.mail;
+
+import com.sun.mail.util.MailSSLSocketFactory;
+import com.wayn.common.core.domain.tool.MailConfig;
+import com.wayn.common.core.domain.vo.SendMailVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.mail.javamail.MimeMessageHelper;
+
+import javax.mail.Authenticator;
+import javax.mail.PasswordAuthentication;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import java.util.Properties;
+
+/**
+ * 发送邮件帮助类
+ */
+@Slf4j
+public class MailUtil {
+
+ public static void sendMail(MailConfig mailConfig, SendMailVO mailVO, boolean isHtml) {
+ try {
+ // 设置发件人
+ String from = mailConfig.getFromUser();
+ // 设置收件人
+ String to = mailVO.getSendMail();
+ // 设置邮件发送的服务器,这里为QQ邮件服务器
+ String host = mailConfig.getHost();
+ // 获取系统属性
+ Properties properties = System.getProperties();
+ // SSL加密
+ MailSSLSocketFactory sf = new MailSSLSocketFactory();
+ sf.setTrustAllHosts(true);
+ properties.put("mail.smtp.ssl.enable", "true");
+ properties.put("mail.smtp.ssl.socketFactory", sf);
+ // 设置系统属性
+ properties.setProperty("mail.smtp.host", host);
+ properties.put("mail.smtp.auth", "true");
+ //获取发送邮件会话、获取第三方登录授权码
+ Session session = Session.getDefaultInstance(properties, new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(from, mailConfig.getPass());
+ }
+ });
+// session.setDebug(true);
+ // 创建默认的 MimeMessage 对象
+ MimeMessage message = new MimeMessage(session);
+ MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
+ // Set From: 头部头字段
+ helper.setFrom(new InternetAddress(from));
+ // Set To: 头部头字段
+ helper.setTo(to);
+ // Set Subject: 头部头字段
+ helper.setSubject(mailVO.getTitle());
+ // 设置消息体
+ helper.setText(mailVO.getContent(), isHtml);
+ Transport.send(message);
+ log.info("邮件发送成功");
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/controller/OrderController.java b/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/controller/OrderController.java
index bb5e843..bf298d0 100644
--- a/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/controller/OrderController.java
+++ b/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/controller/OrderController.java
@@ -10,6 +10,9 @@ import com.wayn.mobile.api.service.IOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
@RestController
@RequestMapping("order")
public class OrderController extends BaseController {
@@ -42,4 +45,10 @@ public class OrderController extends BaseController {
public R h5pay(@RequestBody OrderVO orderVO) {
return iOrderService.h5pay(orderVO.getOrderId(), request);
}
+
+ @PostMapping("payNotify")
+ public R payNotify(HttpServletRequest request, HttpServletResponse response) {
+ return iOrderService.payNotify(request, response);
+ }
+
}
diff --git a/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/service/IOrderService.java b/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/service/IOrderService.java
index 232d45c..eb5c4e1 100644
--- a/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/service/IOrderService.java
+++ b/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/service/IOrderService.java
@@ -7,6 +7,7 @@ import com.wayn.mobile.api.domain.Order;
import com.wayn.mobile.api.domain.vo.OrderVO;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
/**
*
@@ -49,11 +50,18 @@ public interface IOrderService extends IService {
R prepay(Long orderId, HttpServletRequest request);
/**
- *
- * @param page 分页对象
- * @param showType 展示类型
+ * 获取订单列表
+ * @param page 分页对象
+ * @param showType 展示类型
* @return r
*/
R selectListPage(IPage page, Integer showType);
+ /**
+ * 支付成功回调处理
+ * @param request 请求
+ * @param response 响应
+ * @return r
+ */
+ R payNotify(HttpServletRequest request, HttpServletResponse response);
}
diff --git a/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/service/impl/OrderServiceImpl.java b/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/service/impl/OrderServiceImpl.java
index c5b6b14..515110c 100644
--- a/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/service/impl/OrderServiceImpl.java
+++ b/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/service/impl/OrderServiceImpl.java
@@ -3,19 +3,29 @@ package com.wayn.mobile.api.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
+import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.wayn.common.core.domain.shop.Address;
import com.wayn.common.core.domain.shop.GoodsProduct;
import com.wayn.common.core.domain.shop.Member;
+import com.wayn.common.core.domain.tool.MailConfig;
+import com.wayn.common.core.domain.vo.SendMailVO;
import com.wayn.common.core.service.shop.IAddressService;
import com.wayn.common.core.service.shop.IGoodsProductService;
import com.wayn.common.core.service.shop.IMemberService;
+import com.wayn.common.core.service.tool.IMailConfigService;
import com.wayn.common.exception.BusinessException;
+import com.wayn.common.task.TaskService;
import com.wayn.common.util.R;
import com.wayn.common.util.ip.IpUtils;
+import com.wayn.common.util.mail.MailUtil;
import com.wayn.mobile.api.domain.Cart;
import com.wayn.mobile.api.domain.Order;
import com.wayn.mobile.api.domain.OrderGoods;
@@ -27,19 +37,21 @@ import com.wayn.mobile.api.service.IOrderService;
import com.wayn.mobile.api.task.CancelOrderTask;
import com.wayn.mobile.api.util.OrderHandleOption;
import com.wayn.mobile.api.util.OrderUtil;
-import com.wayn.mobile.framework.manager.thread.AsyncManager;
import com.wayn.mobile.framework.redis.RedisCache;
import com.wayn.mobile.framework.security.util.SecurityUtils;
+import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
-import java.util.concurrent.TimeUnit;
/**
*
@@ -49,6 +61,7 @@ import java.util.concurrent.TimeUnit;
* @author wayn
* @since 2020-08-11
*/
+@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl implements IOrderService {
@@ -73,9 +86,14 @@ public class OrderServiceImpl extends ServiceImpl implements
@Autowired
private IMemberService iMemberService;
+ @Autowired
+ private IMailConfigService mailConfigService;
+
@Autowired
private OrderMapper orderMapper;
+ @Autowired
+ private TaskService taskService;
@Override
public R selectListPage(IPage page, Integer showType) {
@@ -112,7 +130,6 @@ public class OrderServiceImpl extends ServiceImpl implements
return R.success().add("data", orderVoList).add("pages", orderIPage.getPages()).add("page", orderIPage.getCurrent());
}
-
@Override
@Transactional(rollbackFor = Exception.class)
public R addOrder(OrderVO orderVO) {
@@ -221,9 +238,10 @@ public class OrderServiceImpl extends ServiceImpl implements
if (!iGoodsProductService.reduceStock(productId, checkGoods.getNumber())) {
throw new BusinessException("商品货品库存减少失败");
}
- long delay = 5;
- redisCache.setCacheZset("order_zset", order.getId(), System.currentTimeMillis() / 1000 + 60 * delay);
- AsyncManager.me().execute(new CancelOrderTask(order.getId()), delay, TimeUnit.MINUTES);
+ long delay = 1000;
+ redisCache.setCacheZset("order_zset", order.getId(), System.currentTimeMillis() + 60 * delay);
+ taskService.addTask(new CancelOrderTask(order.getId(), delay * 60));
+// AsyncManager.me().execute(new CancelOrderTask(order.getId()), delay, TimeUnit.MINUTES);
}
return R.success().add("orderId", order.getId());
} else {
@@ -287,7 +305,7 @@ public class OrderServiceImpl extends ServiceImpl implements
return R.error("订单不能支付");
}
- WxPayMwebOrderResult result = null;
+ WxPayMwebOrderResult result;
try {
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
orderRequest.setOutTradeNo(order.getOrderSn());
@@ -300,11 +318,81 @@ public class OrderServiceImpl extends ServiceImpl implements
orderRequest.setTotalFee(fee);
orderRequest.setSpbillCreateIp(IpUtils.getIpAddr(request));
result = wxPayService.createOrder(orderRequest);
-
+ return R.success().add("data", result);
} catch (Exception e) {
log.error(e.getMessage(), e);
+ return R.error("支付失败");
}
- return R.success().add("data", result);
}
+ @Override
+ public R payNotify(HttpServletRequest request, HttpServletResponse response) {
+ String xmlResult;
+ try {
+ xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
+ } catch (IOException e) {
+ e.printStackTrace();
+ return R.error(WxPayNotifyResponse.fail(e.getMessage()));
+ }
+
+ WxPayOrderNotifyResult result;
+ try {
+ result = wxPayService.parseOrderNotifyResult(xmlResult);
+
+ if (!WxPayConstants.ResultCode.SUCCESS.equals(result.getResultCode())) {
+ log.error(xmlResult);
+ throw new WxPayException("微信通知支付失败!");
+ }
+ } catch (WxPayException e) {
+ e.printStackTrace();
+ return R.error(WxPayNotifyResponse.fail(e.getMessage()));
+ }
+
+ log.info("处理腾讯支付平台的订单支付");
+ log.info(result.getReturnMsg());
+
+ String orderSn = result.getOutTradeNo();
+ String payId = result.getTransactionId();
+
+ // 分转化成元
+ String totalFee = BaseWxPayResult.fenToYuan(result.getTotalFee());
+ Order order = getOne(new QueryWrapper().eq("order_sn", orderSn));
+ if (order == null) {
+ return R.error(WxPayNotifyResponse.fail("订单不存在 sn=" + orderSn));
+ }
+
+ // 检查这个订单是否已经处理过
+ if (OrderUtil.hasPayed(order)) {
+ return R.error(WxPayNotifyResponse.success("订单已经处理成功!"));
+ }
+
+ // 检查支付订单金额
+ if (!totalFee.equals(order.getActualPrice().toString())) {
+ return R.error(WxPayNotifyResponse.fail(order.getOrderSn() + " : 支付金额不符合 totalFee=" + totalFee));
+ }
+
+ order.setPayId(payId);
+ order.setPayTime(LocalDateTime.now());
+ order.setOrderStatus(OrderUtil.STATUS_PAY);
+ order.setUpdateTime(LocalDateTime.now());
+ if (!updateById(order)) {
+ return R.error(WxPayNotifyResponse.fail("更新数据已失效"));
+ }
+
+ //TODO 发送邮件和短信通知,这里采用异步发送
+ // 订单支付成功以后,会发送短信给用户,以及发送邮件给管理员
+ MailConfig mailConfig = mailConfigService.getById(1L);
+ SendMailVO sendMailVO = new SendMailVO();
+ sendMailVO.setTitle("新订单通知");
+ sendMailVO.setContent(order.toString());
+ sendMailVO.setSendMail("1669738430@qq.com");
+ MailUtil.sendMail(mailConfig, sendMailVO, false);
+ // 删除redis中订单id
+ redisCache.deleteZsetObject("order_zset", order.getId());
+ // 取消订单超时未支付任务
+ taskService.removeTask(new CancelOrderTask(order.getId()));
+ return R.error(WxPayNotifyResponse.success("处理成功!"));
+ }
+
+
}
diff --git a/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/task/CancelOrderTask.java b/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/task/CancelOrderTask.java
index ddb56f6..3a61b61 100644
--- a/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/task/CancelOrderTask.java
+++ b/waynboot-mobile-api/src/main/java/com/wayn/mobile/api/task/CancelOrderTask.java
@@ -2,6 +2,7 @@ package com.wayn.mobile.api.task;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wayn.common.core.service.shop.IGoodsProductService;
+import com.wayn.common.task.Task;
import com.wayn.common.util.spring.SpringContextUtil;
import com.wayn.mobile.api.domain.Order;
import com.wayn.mobile.api.domain.OrderGoods;
@@ -16,17 +17,30 @@ import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import java.util.TimerTask;
/**
* 未支付订单超时自动取消任务
*/
@Slf4j
-public class CancelOrderTask extends TimerTask {
+public class CancelOrderTask extends Task {
+ /**
+ * 默认延迟时间,单位毫秒
+ */
+ private static final long DELAY_TIME = 30 * 60 * 1000;
+
+ /**
+ * 订单id
+ */
private final Long orderId;
+ public CancelOrderTask(Long orderId, long delayInMilliseconds) {
+ super("CancelOrderTask-" + orderId, delayInMilliseconds);
+ this.orderId = orderId;
+ }
+
public CancelOrderTask(Long orderId) {
+ super("CancelOrderTask-" + orderId, DELAY_TIME);
this.orderId = orderId;
}
@@ -37,13 +51,13 @@ public class CancelOrderTask extends TimerTask {
IOrderGoodsService orderGoodsService = SpringContextUtil.getBean(IOrderGoodsService.class);
IGoodsProductService productService = SpringContextUtil.getBean(IGoodsProductService.class);
RedisCache redisCache = SpringContextUtil.getBean(RedisCache.class);
- Set zset = redisCache.getCacheZset("order_zset", 0, System.currentTimeMillis() / 1000);
+ Set zset = redisCache.getCacheZset("order_zset", 0, System.currentTimeMillis());
if (CollectionUtils.isNotEmpty(zset) && zset.contains(this.orderId)) {
for (Long orderId : zset) {
- log.info("系统开始处理延时任务---redis内超时未付款---" + orderId);
+ log.info("redis内未付款---" + orderId);
final Long num = redisCache.deleteZsetObject("order_zset", orderId);
if (num != null && num > 0) {
- Order order = orderService.getOne(new QueryWrapper().eq("order_status", OrderUtil.STATUS_AUTO_CANCEL).eq("id", orderId));
+ Order order = orderService.getOne(new QueryWrapper().eq("order_status", OrderUtil.STATUS_CREATE).eq("id", orderId));
if (Objects.isNull(order) || !OrderUtil.isCreateStatus(order)) {
return;
}
@@ -66,7 +80,7 @@ public class CancelOrderTask extends TimerTask {
}
}
}
- log.info("系统结束处理延时任务---redis内超时未付款---" + orderId);
+ log.info("redis内未付款---" + orderId);
}
}
log.info("系统结束处理延时任务---订单超时未付款---" + this.orderId);