Compare commits

...

7 Commits

Author SHA1 Message Date
Bunny cf68662c6d 历史订单查询-未完成 2024-01-10 16:56:31 +08:00
Bunny 18aa86fc11 跳过微信支付 2024-01-10 16:38:06 +08:00
Bunny 9ab639f81b 客户催单 2024-01-10 16:23:29 +08:00
Bunny 8f96d6c9c4 来单提醒 2024-01-10 16:13:32 +08:00
Bunny 4d75011b42 初始化websocket 2024-01-10 15:52:13 +08:00
Bunny acadf1b99b 订单超时、订单派送超时 2024-01-10 15:23:28 +08:00
Bunny c6cae41822 微信支付-没有支付资质 2024-01-10 13:43:58 +08:00
16 changed files with 588 additions and 21 deletions

View File

@ -122,6 +122,11 @@
<artifactId>minio</artifactId>
<version>8.5.2</version>
</dependency>
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -4,9 +4,11 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableCaching// 开启缓存注解
@EnableScheduling// 开启task
@SpringBootApplication
@EnableTransactionManagement // 开启注解方式的事务管理
@Slf4j

View File

@ -0,0 +1,18 @@
package com.sky.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket配置类用于注册WebSocket的Bean
*/
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

View File

@ -0,0 +1,120 @@
package com.sky.controller.nofity;
import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.properties.WeChatProperties;
import com.sky.service.OrderService;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
/**
* 支付回调相关接口
*/
@RestController
@RequestMapping("/notify")
@Slf4j
public class PayNotifyController {
@Resource
private OrderService orderService;
@Resource
private WeChatProperties weChatProperties;
/**
* 支付成功回调
*
* @param request HttpServletRequest
*/
@RequestMapping("/paySuccess")
public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 读取数据
String body = readData(request);
log.info("支付成功回调:{}", body);
// 数据解密
String plainText = decryptData(body);
log.info("解密后的文本:{}", plainText);
JSONObject jsonObject = JSON.parseObject(plainText);
String outTradeNo = jsonObject.getString("out_trade_no");// 商户平台订单号
String transactionId = jsonObject.getString("transaction_id");// 微信支付交易号
log.info("商户平台订单号:{}", outTradeNo);
log.info("微信支付交易号:{}", transactionId);
// 业务处理修改订单状态来单提醒
orderService.paySuccess(outTradeNo);
// 给微信响应
responseToWeixin(response);
}
/**
* 读取数据
*
* @param request HttpServletRequest
* @return String
* @throws Exception exception
*/
private String readData(HttpServletRequest request) throws Exception {
BufferedReader reader = request.getReader();
StringBuilder result = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
if (result.length() > 0) {
result.append("\n");
}
result.append(line);
}
return result.toString();
}
/**
* 数据解密
*
* @param body String
* @return String
* @throws Exception exception
*/
private String decryptData(String body) throws Exception {
JSONObject resultObject = JSON.parseObject(body);
JSONObject resource = resultObject.getJSONObject("resource");
String ciphertext = resource.getString("ciphertext");
String nonce = resource.getString("nonce");
String associatedData = resource.getString("associated_data");
AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
// 密文解密
String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
nonce.getBytes(StandardCharsets.UTF_8),
ciphertext);
return plainText;
}
/**
* 给微信响应
*
* @param response HttpServletResponse
*/
private void responseToWeixin(HttpServletResponse response) throws Exception {
response.setStatus(200);
HashMap<Object, Object> map = new HashMap<>();
map.put("code", "SUCCESS");
map.put("message", "SUCCESS");
response.setHeader("Content-type", ContentType.APPLICATION_JSON.toString());
response.getOutputStream().write(JSONUtils.toJSONString(map).getBytes(StandardCharsets.UTF_8));
response.flushBuffer();
}
}

View File

@ -1,16 +1,16 @@
package com.sky.controller.user;
import com.sky.dto.OrdersPaymentDTO;
import com.sky.dto.OrdersSubmitDTO;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.OrderService;
import com.sky.vo.OrderPaymentVO;
import com.sky.vo.OrderSubmitVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
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;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@ -35,4 +35,41 @@ public class OrderController {
OrderSubmitVO orderSubmitVO = orderService.submitOrder(ordersSubmitDTO);
return Result.success(orderSubmitVO);
}
/**
* 订单支付
*
* @param ordersPaymentDTO OrdersPaymentDTO
* @return Result<OrderPaymentVO>
*/
@PutMapping("/payment")
@ApiOperation("订单支付")
public Result<OrderPaymentVO> payment(@RequestBody OrdersPaymentDTO ordersPaymentDTO) throws Exception {
log.info("订单支付:{}", ordersPaymentDTO);
OrderPaymentVO orderPaymentVO = orderService.payment(ordersPaymentDTO);
// TODO 跳过微信支付
orderService.paySuccess(ordersPaymentDTO.getOrderNumber());
log.info("生成预支付交易单:{}", orderPaymentVO);
return Result.success(orderPaymentVO);
}
/**
* 客户催单
*
* @param id Long
* @return Result<String>
*/
@ApiOperation("客户催单")
@GetMapping("/reminder/{id}")
public Result<String> reminder(@PathVariable("id") Long id) {
orderService.reminder(id);
return Result.success();
}
@ApiOperation("历史订单查询")
@GetMapping("/historyOrders")
public Result<PageResult> page(int page, int pageSize, Integer status) {
PageResult pageResult = orderService.pageQuery4User(page, pageSize, status);
return Result.success(pageResult);
}
}

View File

@ -3,8 +3,46 @@ package com.sky.mapper;
import com.sky.entity.Orders;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
public interface OrderMapper {
// 向订单中插入1条数据
/**
* 向订单中插入1条数据
*
* @param orders Orders
*/
void insert(Orders orders);
/**
* 根据订单号查询订单
*
* @param orderNumber String
*/
Orders getByNumber(String orderNumber);
/**
* 修改订单信息
*
* @param orders Orders
*/
void update(Orders orders);
/**
* 根据订单状态和下单时间查询订单状态
*
* @param status Integer
* @param orderTime LocalDateTime
* @return List<Orders>
*/
List<Orders> getByStatusAndOrderTime(Integer status, LocalDateTime orderTime);
/**
* 查询订单是否存在
*
* @param id 订单id
* @return 订单
*/
Orders getById(Long id);
}

View File

@ -8,14 +8,24 @@ public interface UserMapper {
/**
* 根据用户id查询用户
* @param openid String
*
* @param openid String
* @return User
*/
User getByOpenid(String openid);
/**
* 如果为新用户自动完成注册
*
* @param user User
*/
void insert(User user);
/**
* 当前登录用户id
*
* @param userId Long
* @return User
*/
User getById(Long userId);
}

View File

@ -1,6 +1,9 @@
package com.sky.service;
import com.sky.dto.OrdersPaymentDTO;
import com.sky.dto.OrdersSubmitDTO;
import com.sky.result.PageResult;
import com.sky.vo.OrderPaymentVO;
import com.sky.vo.OrderSubmitVO;
public interface OrderService {
@ -11,4 +14,36 @@ public interface OrderService {
* @return OrderSubmitVO
*/
OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO);
/**
* 订单支付
*
* @param ordersPaymentDTO ordersPaymentDTO
* @return OrderPaymentVO
*/
OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception;
/**
* 支付成功修改订单状态
*
* @param outTradeNo String
*/
void paySuccess(String outTradeNo);
/**
* 客户催单
*
* @param id Long
*/
void reminder(Long id);
/**
* 用户端订单分页查询
*
* @param page
* @param pageSize
* @param status
* @return
*/
PageResult pageQuery4User(int page, int pageSize, Integer status);
}

View File

@ -1,20 +1,20 @@
package com.sky.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.OrdersPaymentDTO;
import com.sky.dto.OrdersSubmitDTO;
import com.sky.entity.AddressBook;
import com.sky.entity.OrderDetail;
import com.sky.entity.Orders;
import com.sky.entity.ShoppingCart;
import com.sky.entity.*;
import com.sky.exception.AddressBookBusinessException;
import com.sky.exception.OrderBusinessException;
import com.sky.exception.ShoppingCartBusinessException;
import com.sky.mapper.AddressBookMapper;
import com.sky.mapper.OrderDetailMapper;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.ShoppingCartMapper;
import com.sky.mapper.*;
import com.sky.service.OrderService;
import com.sky.utils.WeChatPayUtil;
import com.sky.vo.OrderPaymentVO;
import com.sky.vo.OrderSubmitVO;
import com.sky.websocket.WebSocketServer;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -22,7 +22,9 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class OrderServiceImpl implements OrderService {
@ -34,6 +36,12 @@ public class OrderServiceImpl implements OrderService {
private AddressBookMapper addressBookMapper;
@Resource
private ShoppingCartMapper shoppingCartMapper;
@Resource
private UserMapper userMapper;
@Resource
private WeChatPayUtil weChatPayUtil;
@Resource
private WebSocketServer webSocketServer;
/**
* 用户下单
@ -84,11 +92,80 @@ public class OrderServiceImpl implements OrderService {
// 4. 清空当前用户购物车数据
shoppingCartMapper.deleteByUserId(currentId);
// 5. 封装返回结果
return OrderSubmitVO.builder()
.id(orders.getId())
.orderTime(orders.getOrderTime())
.orderNumber(orders.getNumber())
.orderAmount(orders.getAmount()).build();
return OrderSubmitVO.builder().id(orders.getId()).orderTime(orders.getOrderTime()).orderNumber(orders.getNumber()).orderAmount(orders.getAmount()).build();
}
/**
* 订单支付
*
* @param ordersPaymentDTO OrdersPaymentDTO
* @return OrderPaymentVO
*/
public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
// 当前登录用户id
Long userId = BaseContext.getCurrentId();
User user = userMapper.getById(userId);
// TODO 跳过微信支付 调用微信支付接口生成预支付交易单
/* JSONObject jsonObject = weChatPayUtil.pay(ordersPaymentDTO.getOrderNumber(), // 商户订单号
new BigDecimal("0.01"), // 支付金额单位
"苍穹外卖订单", // 商品描述
user.getOpenid() // 微信用户的openid
); */
// TODO 跳过微信支付
JSONObject jsonObject = new JSONObject();
if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
throw new OrderBusinessException("该订单已支付");
}
OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
vo.setPackageStr(jsonObject.getString("package"));
return vo;
}
/**
* 支付成功修改订单状态
*
* @param outTradeNo String
*/
public void paySuccess(String outTradeNo) {
// 根据订单号查询订单
Orders ordersDB = orderMapper.getByNumber(outTradeNo);
// 根据订单id更新订单的状态支付方式支付状态结账时间
Orders orders = Orders.builder().id(ordersDB.getId()).status(Orders.TO_BE_CONFIRMED).payStatus(Orders.PAID).checkoutTime(LocalDateTime.now()).build();
orderMapper.update(orders);
HashMap map = new HashMap<>();
map.put("type", 1);
map.put("orderId", ordersDB.getId());
map.put("content", "订单号" + outTradeNo);
String jsonString = JSONObject.toJSONString(map);
webSocketServer.sendToAllClient(jsonString);
}
/**
* 客户催单
*
* @param id Long
*/
@Override
public void reminder(Long id) {
Orders orderDB = orderMapper.getById(id);
if (orderDB == null) {
throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
}
// 通过websocket 催单
Map map = new HashMap<>();
map.put("type", 2);
map.put("orderId", id);
map.put("content", "订单号" + orderDB.getNumber());
webSocketServer.sendToAllClient(JSONObject.toJSONString(map));
}
}

View File

@ -0,0 +1,54 @@
package com.sky.task;
import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
@Component
@Slf4j
public class OrderTask {
@Resource
private OrderMapper orderMapper;
/**
* 处理超时订单
*/
@Scheduled(cron = "0 * * * * ?")
// @Scheduled(cron = "0/5 * * * * ?")
public void processTImeOrder() {
log.info("定时处理超时订单:{}", LocalDateTime.now());
LocalDateTime time = LocalDateTime.now().plusMinutes(-15);
List<Orders> list = orderMapper.getByStatusAndOrderTime(Orders.PENDING_PAYMENT, time);
if (list != null && !list.isEmpty()) {
for (Orders orders : list) {
orders.setStatus(Orders.CANCELLED);
orders.setCancelReason("订单超时,自动取消");
orders.setCancelTime(LocalDateTime.now());
orderMapper.update(orders);
}
}
}
/**
* 处理一直派送中的订单
*/
@Scheduled(cron = "0 0 1 * * ?")
// @Scheduled(cron = "0/5 * * * * ?")
public void processDeliveryOrder() {
log.info("定时处理处于派送中的订单:{}", LocalDateTime.now());
LocalDateTime time = LocalDateTime.now().plusMinutes(-60);
List<Orders> list = orderMapper.getByStatusAndOrderTime(Orders.DELIVERY_IN_PROGRESS, time);
if (list != null && !list.isEmpty()) {
for (Orders orders : list) {
orders.setStatus(Orders.COMPLETED);
orderMapper.update(orders);
}
}
}
}

View File

@ -0,0 +1,23 @@
package com.sky.task;
import com.sky.websocket.WebSocketServer;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Component
public class WebSocketTask {
@Resource
private WebSocketServer webSocketServer;
/**
* 通过WebSocket每隔5秒向客户端发送消息
*/
@Scheduled(cron = "0/5 * * * * ?")
public void sendMessageToClient() {
webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));
}
}

View File

@ -0,0 +1,72 @@
package com.sky.websocket;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* WebSocket服务
*/
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
// 存放会话对象
private static final Map<String, Session> sessionMap = new HashMap();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
System.out.println("客户端:" + sid + "建立连接");
sessionMap.put(sid, session);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, @PathParam("sid") String sid) {
System.out.println("收到来自客户端:" + sid + "的信息:" + message);
}
/**
* 连接关闭调用的方法
*
* @param sid 请求id
*/
@OnClose
public void onClose(@PathParam("sid") String sid) {
System.out.println("连接断开:" + sid);
sessionMap.remove(sid);
}
/**
* 群发
*
* @param message 消息
*/
public void sendToAllClient(String message) {
Collection<Session> sessions = sessionMap.values();
for (Session session : sessions) {
try {
// 服务器向客户端发送消息
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

View File

@ -18,4 +18,11 @@ sky:
database: 1
wechat:
appid: wx18e5556d7539757b
secret: ac06f1c49f90a2ed69f1a946d4981833
secret: ac06f1c49f90a2ed69f1a946d4981833
mchid: 18012062876
mch-serial-no: xcxzczx
private-key-file-path: sadsad
api-v3-key: dasdasd
we-chat-pay-cert-file-path: asdasds
notify-url: asdsada
refund-notify-url: asdasdsa

View File

@ -58,4 +58,11 @@ sky:
bucket-name: ${sky.minio.bucket-name}
wechat:
appid: ${sky.wechat.appid}
secret: ${sky.wechat.secret}
secret: ${sky.wechat.secret}
mchid: ${sky.wechat.mchid}
mch-serial-no: ${sky.wechat.mchSerialNo}
private-key-file-path: ${sky.wechat.privateKeyFilePath}
api-v3-key: ${sky.wechat.apiV3Key}
we-chat-pay-cert-file-path: ${sky.wechat.weChatPayCertFilePath}
notify-url: ${sky.wechat.notifyUrl}
refund-notify-url: ${sky.wechat.refundNotifyUrl}

View File

@ -13,4 +13,59 @@
#{rejectionReason}, #{cancelTime}, #{estimatedDeliveryTime}, #{deliveryStatus}, #{deliveryTime},
#{packAmount}, #{tablewareNumber}, #{tablewareStatus});
</insert>
<!-- 修改订单信息 -->
<update id="update" parameterType="com.sky.entity.Orders">
update orders
<set>
<if test="cancelReason != null and cancelReason!='' ">
cancel_reason=#{cancelReason},
</if>
<if test="rejectionReason != null and rejectionReason!='' ">
rejection_reason=#{rejectionReason},
</if>
<if test="cancelTime != null">
cancel_time=#{cancelTime},
</if>
<if test="payStatus != null">
pay_status=#{payStatus},
</if>
<if test="payMethod != null">
pay_method=#{payMethod},
</if>
<if test="checkoutTime != null">
checkout_time=#{checkoutTime},
</if>
<if test="status != null">
status = #{status},
</if>
<if test="deliveryTime != null">
delivery_time = #{deliveryTime}
</if>
</set>
where id = #{id}
</update>
<!-- 根据订单号查询订单 -->
<select id="getByNumber" resultType="com.sky.entity.Orders">
select *
from orders
where number = #{orderNumber}
</select>
<!-- 根据订单状态和下单时间查询订单状态 -->
<select id="getByStatusAndOrderTime" resultType="com.sky.entity.Orders">
select *
from orders
where status = #{status}
and order_time &lt; #{orderTime}
</select>
<!-- 查询订单是否存在 -->
<select id="getById" resultType="com.sky.entity.Orders">
select *
from orders
where id = #{id};
</select>
</mapper>

View File

@ -14,4 +14,11 @@
from user
where openid = #{openid};
</select>
<!-- 当前登录用户id -->
<select id="getById" resultType="com.sky.entity.User">
select *
from user
where id = #{id};
</select>
</mapper>